├── .dockerignore ├── .env.dist ├── .github └── workflows │ └── docker_push.yml ├── .gitignore ├── LICENSE ├── README.md ├── bin ├── README.md ├── backup_db.sh ├── deleteconfig.sh ├── maintenance.sh └── prunedb.sh ├── config.d ├── .gitkeep └── config.exs ├── custom.d └── .gitkeep ├── doc ├── CUSTOMIZATION.MD ├── INSTALLATION.md └── MAINTENANCE.md ├── docker-compose.yml ├── docker ├── Dockerfile ├── config.docker.exs ├── entrypoint.sh └── initdb.sql └── manage.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | data/ 2 | bin/ 3 | config.d/ 4 | cache/ 5 | emoji/ 6 | doc/ -------------------------------------------------------------------------------- /.env.dist: -------------------------------------------------------------------------------- 1 | # 1. 2 | #### Akkoma build settings 3 | 4 | # akkoma tag, commit, or branch to build 5 | AKKOMA_VERSION=stable-2022.07 6 | 7 | # The directory where the containers store their stuff 8 | # Can be an absolute or relative path 9 | DOCKER_DATADIR=./data 10 | 11 | # akkoma's mix environment. 12 | # You should leave this at prod unless you know what you're doing. 13 | MIX_ENV=prod 14 | 15 | # The uid/gid used by akkoma. 16 | # You should probably set this to the same 17 | # uid/guid that cloned the akkoma-docker repo. 18 | # This way modding akkoma becomes a lot easier. 19 | DOCKER_UID=1000 20 | DOCKER_GID=1000 21 | 22 | # The git repo where akkoma's sources are located. 23 | # This will be used at build-time and to resolve AKKOMA_VERSION via "git ls-remote". 24 | # The latter involves one connection per "manage.sh build" execution, even if a rebuild does not occur. 25 | # 26 | # You might want to change this if you're working on a fork, 27 | # or if you do not trust the admins of akkoma's Gitlab instance. 28 | # 29 | AKKOMA_GIT_REPO=https://akkoma.dev/AkkomaGang/akkoma.git 30 | 31 | # 2. 32 | ### Akkoma server settings 33 | 34 | # Here variables for the docker-compose file get defined, so they can affect the runtime configuration 35 | # Take your time to replace the values with placeholders with your own desired values. 36 | # Some might be commented out and have values already assigned to them, change these if these are relevant to you 37 | 38 | # Example given (values are like this: ) 39 | 40 | ##Database settings (Uncomment if you want to use other values or another host) 41 | #DB_HOST=db 42 | #DB_NAME=akkoma 43 | #DB_USER=akkoma 44 | #DB_PASS=akkoma 45 | 46 | ## Instance settings 47 | #DOMAIN=example.com 48 | #INSTANCE_NAME=Pleroma 49 | #ADMIN_EMAIL=admin@example.com 50 | #NOTIFY_EMAIL=noreply@example.com 51 | #REGS_OPEN=FALSE 52 | 53 | # Character limit in posts 54 | # POST_LIMIT=5000 55 | -------------------------------------------------------------------------------- /.github/workflows/docker_push.yml: -------------------------------------------------------------------------------- 1 | name: Create and publish a Docker image 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | 7 | paths: 8 | - 'docker/**' 9 | - '**.dist' 10 | env: 11 | REGISTRY: ghcr.io 12 | IMAGE_NAME: akkoma 13 | 14 | jobs: 15 | build-and-push-image: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | packages: write 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v3 24 | 25 | - name: Log in to the Container registry 26 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 27 | with: 28 | registry: ${{ env.REGISTRY }} 29 | username: ${{ github.actor }} 30 | password: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | - name: Prepare envfile 33 | run: cp .env.dist .env 34 | 35 | - name: Build dockerfile 36 | run: ./manage.sh build 37 | 38 | - name: Push to repository 39 | run: docker-compose push server -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data/ 2 | cache/ 3 | emoji/ 4 | custom.d/ 5 | !custom.d/.gitkeep 6 | config.d/ 7 | !config.d/.gitkeep 8 | config.exs 9 | secret.exs 10 | .env 11 | 12 | # Created by https://www.gitignore.io/api/osx,linux,windows 13 | 14 | ### Linux ### 15 | *~ 16 | 17 | # temporary files which can be created if a process still has a handle open of a deleted file 18 | .fuse_hidden* 19 | 20 | # KDE directory preferences 21 | .directory 22 | 23 | # Linux trash folder which might appear on any partition or disk 24 | .Trash-* 25 | 26 | # .nfs files are created when an open file is removed but is still being accessed 27 | .nfs* 28 | 29 | ### OSX ### 30 | *.DS_Store 31 | .AppleDouble 32 | .LSOverride 33 | 34 | # Icon must end with two \r 35 | Icon 36 | 37 | # Thumbnails 38 | ._* 39 | 40 | # Files that might appear in the root of a volume 41 | .DocumentRevisions-V100 42 | .fseventsd 43 | .Spotlight-V100 44 | .TemporaryItems 45 | .Trashes 46 | .VolumeIcon.icns 47 | .com.apple.timemachine.donotpresent 48 | 49 | # Directories potentially created on remote AFP share 50 | .AppleDB 51 | .AppleDesktop 52 | Network Trash Folder 53 | Temporary Items 54 | .apdisk 55 | 56 | ### Windows ### 57 | # Windows thumbnail cache files 58 | Thumbs.db 59 | ehthumbs.db 60 | ehthumbs_vista.db 61 | 62 | # Folder config file 63 | Desktop.ini 64 | 65 | # Recycle Bin used on file shares 66 | $RECYCLE.BIN/ 67 | 68 | # Windows Installer files 69 | *.cab 70 | *.msi 71 | *.msm 72 | *.msp 73 | 74 | # Windows shortcuts 75 | *.lnk 76 | 77 | 78 | # End of https://www.gitignore.io/api/osx,linux,windows 79 | debug.d 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018, sn0w 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Akkoma-Docker (Unofficial) 2 | 3 | [Akkoma](https://akkoma.dev/) (A Pleroma fork) is a selfhosted social network that uses ActivityPub. 4 | 5 | This repository dockerizes it for easier deployment. 6 | 7 | This repository was heavily based on sn0w's work on [pleroma-docker](https://memleak.eu/sn0w/pleroma-docker) 8 | 9 | #### Differences: 10 | 11 | * Runs Akkoma instead of Pleroma 12 | * Enviroment variable based configuration 13 | * Autogenerating secrets 14 | * Additional scripts 15 | * Default frontends (pleroma-fe + admin-fe) 16 | 17 | 18 | ## Docs: 19 | 20 | To limit the size of this document, the documentation has been split up into several different documents.
21 | A summary and link to those can be found here: 22 | 23 | - Installation guide: A starting guide on how to use this setup [[link]](doc/INSTALLATION.md) 24 | 25 | - Maintenance guide: Tips and Common maintenance tasks [[link]](doc/MAINTENANCE.md) 26 | 27 | - Customization guide: How to go about customizing your setup [[link]](doc/CUSTOMIZATION.MD) 28 | 29 | 30 |
31 | 32 | ```cpp 33 | #include 34 | 35 | /* 36 | * This repository comes with ABSOLUTELY NO WARRANTY 37 | * 38 | * I will happily help you with issues related to this script, 39 | * but I am not responsible for burning servers, angry users, fedi drama, 40 | * thermonuclear war, or you getting fired because your boss saw your NSFW posts. 41 | * 42 | * Please do some research if you have any concerns about the 43 | * included features or software *before* using it. 44 | */ 45 | ``` 46 | 47 |
48 | 49 | ### Attribution 50 | 51 | Great thanks to sn0w for publishing their [pleroma-docker](https://memleak.eu/sn0w/pleroma-docker) repository on what this is based on. -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | This has not been documented yet and are very crude scripts at this time being, be mindful about them not being useful at the moment. -------------------------------------------------------------------------------- /bin/backup_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## MOVE TO SCRIPT DIRECTORY 3 | cd ../"$(dirname "$0")"; 4 | #SAVE PLEROMA STATE 5 | if [[ -z `./pleroma.sh ps | grep 'db'` ]]; then 6 | P_STATE=false 7 | elif [[ -n `./pleroma.sh ps | grep 'db'` ]]; then 8 | P_STATE=true 9 | fi 10 | if [[ -n $1 && $1 = 'restart' ]]; then 11 | ./pleroma.sh down && /usr/local/bin/docker-compose up -d db && sleep 2 && /usr/local/bin/docker-compose exec db pg_dump -U pleroma pleroma | gzip >/backups/my_db-$(date +%Y-%m-%d).tar.gz; 12 | else 13 | /usr/local/bin/docker-compose exec db pg_dump -U pleroma pleroma | gzip >/backups/my_db-$(date +%Y-%m-%d).tar.gz; 14 | fi 15 | 16 | if [[ "$P_STATE" = true ]]; then 17 | ./pleroma.sh up 18 | else 19 | ./pleroma.sh down 20 | fi 21 | -------------------------------------------------------------------------------- /bin/deleteconfig.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## MOVE TO SCRIPT DIRECTORY 3 | cd ../"$(dirname "$0")"; 4 | #SAVE PLEROMA STATE 5 | ./pleroma.sh mix pleroma.config migrate_from_db -d 6 | -------------------------------------------------------------------------------- /bin/maintenance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #export PATH="/home/melli/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" 3 | set -e 4 | 5 | ## MOVE TO SCRIPT DIRECTORY 6 | cd ../"$(dirname "$0")"; 7 | #SAVE PLEROMA STATE 8 | if [[ -z `./pleroma.sh ps | grep 'db'` ]]; then 9 | P_STATE=false 10 | elif [[ -n `./pleroma.sh ps | grep 'db'` ]]; then 11 | P_STATE=true 12 | fi 13 | if [[ -n $1 && $1 = 'restart' ]]; then 14 | ./pleroma.sh down && /usr/local/bin/docker-compose up -d db && sleep 2 && /usr/local/bin/docker-compose exec db pg_dump -U pleroma pleroma | gzip >/backups/my_db-$(date +%Y-%m-%d).tar.gz; 15 | else 16 | /usr/local/bin/docker-compose exec db pg_dump -U pleroma pleroma | gzip >/backups/my_db-$(date +%Y-%m-%d).tar.gz; 17 | fi 18 | 19 | if [[ "$P_STATE" = true ]]; then 20 | ./pleroma.sh up 21 | else 22 | ./pleroma.sh down 23 | fi 24 | #RUN mix pleroma.database prune_objects --vacuum only if pleroma is up 25 | if [[ -n `./pleroma.sh ps | grep 'db'` ]]; then 26 | ./pleroma.sh mix pleroma.database prune_objects --vacuum 27 | fi 28 | -------------------------------------------------------------------------------- /bin/prunedb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## MOVE TO SCRIPT DIRECTORY 3 | cd ../"$(dirname "$0")"; 4 | #RUN mix pleroma.database prune_objects --vacuum only if pleroma is up 5 | if [[ -n `./pleroma.sh ps | grep 'db'` ]]; then 6 | ./pleroma.sh mix pleroma.database prune_objects --vacuum 7 | fi 8 | -------------------------------------------------------------------------------- /config.d/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyena-network/akkoma-docker/eb1d65ba1b30a1d66313faf872f3291afbf4f330/config.d/.gitkeep -------------------------------------------------------------------------------- /config.d/config.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | # For additional configuration outside of environmental variables 3 | # Configure Akkoma Frontends 4 | config :pleroma, :frontends, 5 | primary: %{ 6 | "name" => "pleroma-fe", 7 | "ref" => "stable" 8 | }, 9 | admin: %{ 10 | "name" => "admin-fe", 11 | "ref" => "stable" 12 | } -------------------------------------------------------------------------------- /custom.d/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyena-network/akkoma-docker/eb1d65ba1b30a1d66313faf872f3291afbf4f330/custom.d/.gitkeep -------------------------------------------------------------------------------- /doc/CUSTOMIZATION.MD: -------------------------------------------------------------------------------- 1 | 2 | # Customization 3 | 4 | Add your customizations (and their folder structure) to `custom.d/`.
5 | They will be copied into the right place when the container starts.
6 | You can even replace/patch akkoma’s code with this, because the project is recompiled at startup if needed. 7 | 8 | In general: Prepending `custom.d/` to akkoma’s customization guides should work all the time.
9 | 10 | For example: A custom thumbnail now goes into `custom.d/` + `instance/static/instance/thumbnail.jpeg`. 11 | 12 | Hint: `./manage.sh enter` gives you a shell, from where you can `cd` and `ls` to explore the file structure 13 | 14 | ### Frontends 15 | 16 | To install alternative frontends other than the standard installed `pleroma-fe` and `admin-fe`. 17 | First, uncomment the following volume line in `docker-compose.yml` like: 18 | ``` 19 | - $DOCKER_DATADIR/frontends:/home/akkoma/akkoma/instance/static/frontends 20 | ``` 21 | Then, use the `manage.sh` script to install them like this example: 22 | 23 | ``` 24 | ./manage.sh mix pleroma.frontend install pleroma-fe 25 | ./manage.sh mix pleroma.frontend install admin-fe 26 | ./manage.sh mix pleroma.frontend install masto-fe 27 | ./manage.sh restart 28 | ``` 29 | From there on you should find a folder called `frontends` in your Docker data directory where you can see the installed frontends. 30 | 31 | ### Patches 32 | 33 | ** THIS DOES NOT WORK AT THE MOMENT, unlike Pleroma, Akkoma is not hosted on gitlab so the same API is not in place ** 34 | 35 | ~~Works exactly like customization, but we have a neat little helper here.
~~ 36 | ~~Use `./manage.sh mod [regex]` to mod any file that ships with akkoma, without having to type the complete path.~~ -------------------------------------------------------------------------------- /doc/INSTALLATION.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | These docs assume that you have at least a basic understanding 4 | of the akkoma installation process and common docker commands. 5 | 6 | If you have questions about Pleroma head over to https://docs.akkoma.dev/stable/. 7 | For help with docker check out https://docs.docker.com/. 8 | 9 | For other problems related to this script, contact me or open an issue :) 10 | 11 | ### Prerequisites 12 | 13 | - ~1GB of free HDD space 14 | - `git` if you want smart build caches 15 | - ~~`curl`, `jq`, and `dialog` if you want to use `./manage.sh mod`~~ 16 | - Bash 4+ 17 | - Docker 18.06+ and docker-compose 1.22+ 18 | 19 | ### Quick-start 20 | 21 | - Clone this repository (or just copy the [docker-compose.yml](../docker-compose.yml)) 22 | - Copy the base [.env.dist](../.env.dist) to `.env` and change the desired values 23 | - Run `./manage.sh build` to build the Akkoma container 24 | - Run `./manage.sh up` or `docker-compose up` 25 | - [Configure a reverse-proxy](#my-instance-is-up-how-do-i-reach-it) 26 | - Profit! 27 | 28 | After which you might want to make yourself an admin user as following: 29 | 30 | ``` 31 | ./manage.sh mix pleroma.user new --admin 32 | ``` 33 | 34 | Hint: 35 | You can also use normal `docker-compose` commands to maintain your setup.
36 | The only command that you cannot use is `docker-compose build` due to build caching. 37 | 38 | ### Configuration 39 | 40 | 41 | `.env` stores config values that need to be known at orchestration/build time as well as configuration values most commonly put in the `config.exs`.
42 | All additional options that you usually put into your `*.secret.exs` now go into `config.d/config.exs`, this will be created on first run with a header.
43 | 44 | Incase you mainly want to configure through the `admin-fe` add the following line to your `config.d/config.exs`: 45 | ``` 46 | config :pleroma, configurable_from_database: true 47 | ``` 48 | 49 | 50 | Documentation for the possible values, please refer to the Pleroma (since Akkoma does not have it yet at this time) configuration cheatsheet 51 | [[link]](https://docs-develop.pleroma.social/backend/configuration/cheatsheet/) 52 | 53 | 54 | ### My instance is up, how do I reach it? 55 | 56 | To reach Gopher or SSH, just uncomment the port-forward in your `docker-compose.yml`. 57 | 58 | To reach HTTP you will have to configure a "reverse-proxy".
59 | Older versions of this project contained a huge amount of scripting to support all kinds of reverse-proxy setups.
60 | This newer version tries to focus only on providing good akkoma tooling.
61 | That makes the whole process a bit more manual, but also more flexible. 62 | 63 | You can use Caddy, Traefik, Apache, nginx, or whatever else you come up with.
64 | Just modify your `docker-compose.yml` accordingly. 65 | 66 | One example would be to add an [nginx server](https://hub.docker.com/_/nginx) to your `docker-compose.yml`: 67 | ```yml 68 | # ... 69 | 70 | proxy: 71 | image: nginx 72 | init: true 73 | restart: unless-stopped 74 | links: 75 | - server 76 | volumes: 77 | - ./my-nginx-config.conf:/etc/nginx/nginx.conf:ro 78 | ports: 79 | - "80:80" 80 | - "443:443" 81 | ``` 82 | 83 | Then take a look at [the pleroma nginx example](https://git.pleroma.social/pleroma/pleroma/blob/develop/installation/pleroma.nginx) for hints about what to put into `my-nginx-config.conf`. 84 | 85 | Using apache would work in a very similar way (see [Apache Docker Docs](https://hub.docker.com/_/httpd) and [the pleroma apache example](https://git.pleroma.social/pleroma/pleroma/blob/develop/installation/pleroma-apache.conf)). 86 | 87 | The target that you proxy to is called `http://server:4000/`.
88 | This will work automagically when the proxy also lives inside of docker. 89 | 90 | If you need help with this, or if you think that this needs more documentation, please let me know. 91 | -------------------------------------------------------------------------------- /doc/MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | # Maintenance 2 | 3 | Pleroma maintenance is usually done with mix tasks.
4 | You can run these tasks in your running akkoma server using `./manage.sh mix [task] [arguments...]`.
5 | For example: `./manage.sh mix pleroma.user new sn0w ...`
6 | If you need to fix bigger problems you can also spawn a shell with `./manage.sh enter`. 7 | 8 | ### Updates 9 | 10 | Run `./manage.sh build` again and start the updated image with `./manage.sh up`.
11 | You don't need to stop your akkoma server for either of those commands. 12 | 13 | ## Postgres Upgrades 14 | 15 | Postgres upgrades are a slow process in docker (even more than usual) because we can't utilize `pg_upgrade` in any sensible way.
16 | If you ever wish to upgrade postgres to a new major release for some reason, here's a list of things you'll need to do. 17 | 18 | - Inform your users about the impending downtime 19 | - Seriously this can take anywhere from a couple hours to a week depending on your instance 20 | - Make sure you have enough free disk space or some network drive to dump to, we can't do in-place upgrades 21 | - Stop akkoma (`docker-compose stop server`) 22 | - Dump the current database into an SQL file (`docker-compose exec db pg_dumpall -U akkoma > /my/sql/location/akkoma.sql`) 23 | - Remove the old containers (`docker-compose down`) 24 | - Modify the postgres version in `docker-compose.yml` to your desired release 25 | - Delete `data/db` or move it into some different place (might be handy if you want to abort/revert the migration) 26 | - Start the new postgres container (`docker-compose up -d db`) 27 | - Start the import (`docker-compose exec -T db psql -U akkoma < /my/sql/location/akkoma.sql`) 28 | - Wait for a possibly ridculously long time 29 | - Boot akkoma again (`docker-compose up -d`) 30 | - Wait for service to stabilize while federation catches up 31 | - Done! -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | db: 5 | image: postgres:13-alpine 6 | init: true 7 | restart: unless-stopped 8 | environment: { 9 | # This might seem insecure but is usually not a problem. 10 | # You should leave this at the "akkoma" default. 11 | # The DB is only reachable by containers in the same docker network, 12 | # and is not exposed to the open internet. 13 | # 14 | # If you do change this, remember to update "config.exs". 15 | POSTGRES_DB: akkoma, 16 | POSTGRES_USER: akkoma, 17 | POSTGRES_PASSWORD: akkoma, 18 | } 19 | volumes: 20 | - $DOCKER_DATADIR/db:/var/lib/postgresql/data 21 | - ./docker/initdb.sql:/docker-entrypoint-initdb.d/akkoma.sql 22 | 23 | server: 24 | build: docker/ 25 | image: ghcr.io/hyena-network/akkoma:${AKKOMA_VERSION} 26 | init: true 27 | restart: unless-stopped 28 | env_file: 29 | - .env 30 | links: 31 | - db 32 | ports: [ 33 | # Uncomment/Change port mappings below as needed. 34 | # The left side is your host machine, the right one is the akkoma container. 35 | # You can prefix the left side with an ip. 36 | 37 | # Webserver (for reverse-proxies outside of docker) 38 | # If you use a dockerized proxy (see README), you can leave this commented 39 | # and use a container link instead. 40 | "127.0.0.1:4000:4000", 41 | 42 | # SSH support 43 | # "2222:2222", 44 | 45 | # Gopher support 46 | # "9999:9999", 47 | ] 48 | volumes: 49 | - ./custom.d:/custom.d:ro 50 | - ./docker/config.docker.exs:/home/akkoma/akkoma/config/prod.secret.exs:ro 51 | - ./docker/config.docker.exs:/home/akkoma/akkoma/config/dev.secret.exs:ro 52 | - ./emoji:/home/akkoma/akkoma/instance/static/emoji 53 | - ./config.d:/config.d 54 | - $DOCKER_DATADIR/akkoma/uploads:/uploads 55 | - $DOCKER_DATADIR/akkoma/secrets:/secrets 56 | # Uncomment for custom frontends 57 | #- $DOCKER_DATADIR/akkoma/frontends:/home/akkoma/akkoma/instance/static/frontends 58 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.16 2 | 3 | ARG __VIA_SCRIPT 4 | RUN \ 5 | if [ -z "$__VIA_SCRIPT" ]; then \ 6 | echo -e "\n\nERROR\nYou must build pleroma via build.sh\n\n"; \ 7 | exit 1; \ 8 | fi 9 | 10 | #Setup labels 11 | ARG BUILD_DATE 12 | ARG VCS_REF 13 | 14 | LABEL maintainer="mel@hyena.network" \ 15 | org.opencontainers.image.title="Akkoma" \ 16 | org.opencontainers.image.description="Akkoma containerized" \ 17 | org.opencontainers.image.authors="mel@hyena.network" \ 18 | org.opencontainers.image.vendor="soc.hyena.network" \ 19 | org.opencontainers.image.documentation="https://docs.akkoma.dev/stable" \ 20 | org.opencontainers.image.licenses="AGPL-3.0" \ 21 | org.opencontainers.image.url="https://akkoma.dev" \ 22 | org.opencontainers.image.revision=$VCS_REF \ 23 | org.opencontainers.image.created=$BUILD_DATE 24 | 25 | # Set up environment 26 | ENV LC_ALL=C.UTF-8 27 | ENV LANG=C.UTF-8 28 | ARG MIX_ENV 29 | ENV MIX_ENV=$MIX_ENV 30 | 31 | # Prepare mounts 32 | VOLUME /custom.d /uploads 33 | 34 | # Expose HTTP, Gopher, and SSH ports, respectively 35 | EXPOSE 4000 9999 2222 36 | 37 | # Get dependencies 38 | RUN \ 39 | apk add --no-cache --virtual .tools \ 40 | git curl rsync postgresql-client \ 41 | && \ 42 | apk add --no-cache --virtual .sdk \ 43 | build-base cmake file-dev \ 44 | && \ 45 | apk add --no-cache --virtual .runtime \ 46 | imagemagick ffmpeg exiftool \ 47 | elixir erlang erlang-dev 48 | 49 | # Add entrypoint 50 | COPY ./entrypoint.sh / 51 | RUN chmod a+x /entrypoint.sh 52 | ENTRYPOINT ["/entrypoint.sh"] 53 | 54 | # Limit permissions 55 | ARG DOCKER_UID 56 | ARG DOCKER_GID 57 | RUN \ 58 | echo "#> akkoma user will be ${DOCKER_UID}:${DOCKER_GID}" 1>&2 && \ 59 | addgroup -g ${DOCKER_GID} akkoma && \ 60 | adduser -S -s /bin/ash -G akkoma -u ${DOCKER_UID} akkoma && \ 61 | mkdir -p /custom.d /uploads && \ 62 | chown -R akkoma:akkoma /custom.d /uploads 63 | 64 | USER akkoma 65 | WORKDIR /home/akkoma 66 | 67 | # Get akkoma sources 68 | # 69 | # Also make sure that instance/static/emoji exists. 70 | # akkoma does not ship with an `instance` folder by default, causing docker to create `instance/static` for us at launch. 71 | # In itself that wouldn't be much of an issue, but said folder(s) are created as root:akkoma with 2755. 72 | # This breaks the custom.d step in entrypoint.sh which relies on writing there (See #10). 73 | # 74 | ARG AKKOMA_GIT_REPO 75 | RUN \ 76 | echo "#> Getting akkoma sources from $AKKOMA_GIT_REPO..." 1>&2 && \ 77 | git clone --progress $AKKOMA_GIT_REPO ./akkoma && \ 78 | mkdir -p ./akkoma/instance/static/emoji 79 | 80 | WORKDIR /home/akkoma/akkoma 81 | 82 | # Bust the build cache (if needed) 83 | # This works by setting an environment variable with the last 84 | # used version/branch/tag/commit/... which originates in the script. 85 | ARG __CACHE_TAG 86 | ENV __CACHE_TAG $__CACHE_TAG 87 | 88 | # Fetch changes, checkout 89 | # Only pull if the version-string happens to be a branch 90 | ARG AKKOMA_VERSION 91 | RUN \ 92 | git fetch --all && \ 93 | git checkout $AKKOMA_VERSION && \ 94 | if git show-ref --quiet "refs/heads/$AKKOMA_VERSION"; then \ 95 | git pull --rebase --autostash; \ 96 | fi 97 | 98 | # Precompile 99 | RUN \ 100 | cp ./config/dev.exs ./config/prod.secret.exs && \ 101 | BUILDTIME=1 /entrypoint.sh 102 | 103 | # Install default frontends 104 | RUN \ 105 | mix pleroma.frontend install pleroma-fe && \ 106 | mix pleroma.frontend install admin-fe && \ 107 | rm ./config/prod.secret.exs 108 | 109 | # Register healthcheck 110 | # You might need to change these values on slow or busy servers. 111 | HEALTHCHECK \ 112 | --interval=10s \ 113 | --start-period=50s \ 114 | --timeout=4s \ 115 | --retries=3 \ 116 | CMD curl -sSLf http://localhost:4000/api/pleroma/healthcheck || exit 1 117 | -------------------------------------------------------------------------------- /docker/config.docker.exs: -------------------------------------------------------------------------------- 1 | # !!! 2 | # This is the base config for this setup, please do not change things here unless you know what you are doing. 3 | # Other changes go in `config.d` 4 | 5 | import Config 6 | 7 | config :pleroma, Pleroma.Web.Endpoint, 8 | url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443], 9 | http: [ip: {0, 0, 0, 0}, port: 4000] 10 | 11 | config :pleroma, :instance, 12 | name: System.get_env("INSTANCE_NAME", "Pleroma"), 13 | email: System.get_env("ADMIN_EMAIL"), 14 | notify_email: System.get_env("NOTIFY_EMAIL"), 15 | limit: System.get_env("POST_LIMIT", "5000"), 16 | registrations_open: System.get_env("REGS_OPEN", "false"), 17 | healthcheck: true 18 | 19 | config :pleroma, Pleroma.Repo, 20 | adapter: Ecto.Adapters.Postgres, 21 | username: System.get_env("DB_USER", "akkoma"), 22 | password: System.get_env("DB_PASS", "akkoma"), 23 | database: System.get_env("DB_NAME", "akkoma"), 24 | hostname: System.get_env("DB_HOST", "db"), 25 | pool_size: 10 26 | 27 | # Configure Akkoma Frontends 28 | config :pleroma, :frontends, 29 | primary: %{ 30 | "name" => "pleroma-fe", 31 | "ref" => "stable" 32 | }, 33 | admin: %{ 34 | "name" => "admin-fe", 35 | "ref" => "stable" 36 | } 37 | 38 | # Configure web push notifications 39 | config :web_push_encryption, :vapid_details, subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}" 40 | 41 | config :pleroma, :database, rum_enabled: false 42 | 43 | # You should not change this. 44 | config :pleroma, Pleroma.Uploaders.Local, uploads: "/uploads" 45 | 46 | # We can't store the secrets in this file, since this is baked into the docker image 47 | # For additional user config 48 | if not File.exists?("/secrets/secret.exs") do 49 | secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) 50 | signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8) 51 | {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) 52 | 53 | secret_file = 54 | EEx.eval_string( 55 | """ 56 | import Config 57 | config :pleroma, Pleroma.Web.Endpoint, 58 | secret_key_base: "<%= secret %>", 59 | signing_salt: "<%= signing_salt %>" 60 | config :web_push_encryption, :vapid_details, 61 | public_key: "<%= web_push_public_key %>", 62 | private_key: "<%= web_push_private_key %>" 63 | """, 64 | secret: secret, 65 | signing_salt: signing_salt, 66 | web_push_public_key: Base.url_encode64(web_push_public_key, padding: false), 67 | web_push_private_key: Base.url_encode64(web_push_private_key, padding: false) 68 | ) 69 | 70 | File.write("/secrets/secret.exs", secret_file) 71 | end 72 | 73 | import_config("/secrets/secret.exs") 74 | 75 | 76 | 77 | # For additional user config 78 | if File.exists?("/config.d/config.exs"), 79 | do: import_config("/config.d/config.exs"), 80 | else: 81 | File.write("/config.d/config.exs", """ 82 | import Config 83 | # For additional configuration outside of environmental variables 84 | """) -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | # shellcheck shell=dash 3 | 4 | set -e 5 | 6 | log() { 7 | echo -e "\n#> $@\n" 1>&2 8 | } 9 | 10 | if [ -n "$BUILDTIME" ]; then 11 | log "Getting rebar..." 12 | mix local.rebar --force 13 | 14 | log "Getting hex..." 15 | mix local.hex --force 16 | 17 | log "Getting dependencies..." 18 | mix deps.get 19 | 20 | log "Precompiling..." 21 | mix compile 22 | exit 0 23 | fi 24 | 25 | log "Syncing changes and patches..." 26 | rsync -av /custom.d/ /home/akkoma/akkoma/ 27 | 28 | log "Performing sanity checks..." 29 | if ! touch /config.d/.sanity-check; then 30 | log "\ 31 | The config.d dir is NOT writable by `id -u`:`id -g`!\n\ 32 | Without this the server will not able to start.\n\ 33 | Please fix the permissions and try again.\ 34 | " 35 | exit 1 36 | fi 37 | rm /config.d/.sanity-check 38 | if ! touch /secrets/.sanity-check; then 39 | log "\ 40 | The secrets datadir is NOT writable by `id -u`:`id -g`!\n\ 41 | Without this the server will not able to start.\n\ 42 | Please fix the permissions and try again.\ 43 | " 44 | exit 1 45 | fi 46 | rm /secrets/.sanity-check 47 | if ! touch /uploads/.sanity-check; then 48 | log "\ 49 | The uploads datadir is NOT writable by `id -u`:`id -g`!\n\ 50 | This will break all upload functionality.\n\ 51 | Please fix the permissions and try again.\ 52 | " 53 | exit 1 54 | fi 55 | rm /uploads/.sanity-check 56 | if ! touch /home/akkoma/akkoma/instance/static/frontends/.sanity-check; then 57 | log "\ 58 | The frontends datadir is NOT writable by `id -u`:`id -g`!\n\ 59 | This will break all frontend functionality.\n\ 60 | Please fix the permissions and try again.\ 61 | " 62 | exit 1 63 | fi 64 | rm /home/akkoma/akkoma/instance/static/frontends/.sanity-check 65 | 66 | 67 | log "Recompiling..." 68 | mix compile 69 | 70 | log "Waiting for postgres..." 71 | while ! pg_isready -U akkoma -d postgres://db:5432/akkoma -t 1; do 72 | sleep 1s 73 | done 74 | 75 | log "Migrating database..." 76 | mix ecto.migrate 77 | 78 | log "Liftoff o/" 79 | exec mix phx.server 80 | -------------------------------------------------------------------------------- /docker/initdb.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION IF NOT EXISTS citext; 2 | CREATE EXTENSION IF NOT EXISTS pg_trgm; 3 | CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; 4 | -------------------------------------------------------------------------------- /manage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -Eeo pipefail 4 | 5 | ######################################################### 6 | # Globals # 7 | ######################################################### 8 | 9 | # readonly GITLAB_URI="https://git.pleroma.social" 10 | # readonly PREFIX_API="api/v4/projects/pleroma%2Fpleroma/repository" 11 | # readonly ENDPOINT_REPO="pleroma/pleroma.git" 12 | # readonly ENDPOINT_FILE="pleroma/pleroma/raw" 13 | # readonly ENDPOINT_LIST="pleroma/pleroma/files" 14 | # readonly ENDPOINT_TAG="$PREFIX_API/tags" 15 | # readonly ENDPOINT_BLOB="$PREFIX_API/blobs" 16 | # readonly ENDPOINT_BRANCH="$PREFIX_API/branches" 17 | 18 | ######################################################### 19 | # Helpers # 20 | ######################################################### 21 | 22 | has_command() { 23 | if command -v 1>/dev/null 2>&1 "$1"; then 24 | return 0 25 | else 26 | return 1 27 | fi 28 | } 29 | 30 | require_command() { 31 | if ! has_command "$1"; then 32 | printf "\nError: This action requires the command '%s' in your PATH.\n" "$1" 33 | exit 1 34 | fi 35 | } 36 | 37 | require_file() { 38 | if [[ ! -f $1 ]]; then 39 | echo "File missing: '$1' (Example at: '$2')" 40 | FILE_FAILED=1 41 | fi 42 | } 43 | 44 | throw_file_errors() { 45 | if [[ -n "$FILE_FAILED" ]]; then 46 | echo "" 47 | echo "Please create the missing files first." 48 | echo "The script will now exit." 49 | exit 1 50 | fi 51 | } 52 | 53 | docker_compose() { 54 | require_command docker-compose 55 | docker-compose "$@" 56 | } 57 | 58 | download_file() { # $1: source, $2: target 59 | if has_command curl; then 60 | curl -sSL "$1" -o "$2" 61 | elif has_command wget; then 62 | wget "$1" -O "$2" 63 | else 64 | printf "\nError: This action requires either curl or wget in your PATH.\n" 65 | exit 1 66 | fi 67 | } 68 | 69 | request_file_content() { # $1: source 70 | if has_command curl; then 71 | curl -sSL "$1" 72 | elif has_command wget; then 73 | wget "$1" -O- 2>/dev/null 74 | else 75 | printf "\nError: This action requires either curl or wget in your PATH.\n" 76 | exit 1 77 | fi 78 | } 79 | 80 | builds_args="" 81 | load_env() { 82 | while read -r line; do 83 | if [[ "$line" == \#* ]] || [[ -z "$line" ]]; then 84 | continue; 85 | fi 86 | 87 | builds_args="${builds_args} --build-arg ${line?}" 88 | export "${line?}" 89 | done < .env 90 | } 91 | 92 | ######################################################### 93 | # Subcommands # 94 | ######################################################### 95 | 96 | action__build() { 97 | local cacheTag="" 98 | 99 | # Alternative 1: Get tags or branches from git (if installed) 100 | if [[ -z "$cacheTag" ]] && has_command git && has_command grep && has_command awk; then 101 | set +o pipefail 102 | local resolvedHash 103 | resolvedHash="$(git ls-remote $AKKOMA_GIT_REPO | grep "/$AKKOMA_VERSION" | awk '{ print $1 }')" 104 | set -o pipefail 105 | 106 | if [[ -n "$resolvedHash" ]]; then 107 | cacheTag="$resolvedHash" 108 | fi 109 | fi 110 | 111 | # Alternative 2: Current time 112 | if [[ -z "$cacheTag" ]] && has_command date; then 113 | echo "" 114 | echo "WARNING WARNING WARNING" 115 | echo "" 116 | echo "You don't have git installed, so we cannot know if the cache is up to date." 117 | echo "We'll use the current unix timestamp as a replacement value," 118 | echo "but this means that your cache is always 'stale' and docker wastes your time." 119 | echo "" 120 | echo "WARNING WARNING WARNING" 121 | echo "" 122 | echo "Waiting 5 seconds to make sure you notice this..." 123 | sleep 5 124 | 125 | cacheTag="$(date '+%s')" 126 | fi 127 | 128 | # Alternative 3: Random number with shell 129 | if [[ -z "$cacheTag" ]] && [[ -n "$RANDOM" ]]; then 130 | echo "" 131 | echo "WARNING WARNING WARNING" 132 | echo "" 133 | echo "You don't have git installed, so we cannot know if the cache is up to date." 134 | echo "Additionally you don't have \`date\` available. (What kind of pc is this?!)" 135 | echo "This means we cannot set any unique value as cache tag." 136 | echo "" 137 | echo "We'll generate a random number to try and mark the cache as 'always stale'." 138 | echo "Hoewever: Depending on your shell this might not always work, or only work a few times." 139 | echo "" 140 | echo "You should *really* get this fixed unless you know what you're doing." 141 | echo "" 142 | echo "WARNING WARNING WARNING" 143 | echo "" 144 | echo "Waiting 5 seconds to make sure you notice this..." 145 | sleep 5 146 | 147 | cacheTag="$RANDOM" 148 | fi 149 | 150 | # Last resort: Constant value 151 | if [[ -z "$cacheTag" ]]; then 152 | echo "" 153 | echo "WARNING WARNING WARNING" 154 | echo "" 155 | echo "You don't have git installed, so we cannot know if the cache is up to date." 156 | echo "Additionally you don't have \`date\` available, and your shell refuses to generate random numbers." 157 | echo "This means we cannot set any unique or random value as cache tag." 158 | echo "Consequently your cache will always be 'fresh' and you never get updates." 159 | echo "" 160 | echo "You can work around this by running \`docker system prune\` to throw away the build cache," 161 | echo "but you should *really* get this fixed unless you know what you're doing." 162 | echo "" 163 | echo "WARNING WARNING WARNING" 164 | echo "" 165 | echo "Waiting 5 seconds to make sure you notice this..." 166 | sleep 5 167 | 168 | cacheTag="broken-host-env" 169 | fi 170 | 171 | echo -e "#> (Re-)Building akkoma @$AKKOMA_VERSION with cache tag \`${cacheTag}\`...\n" 172 | sleep 1 173 | 174 | docker_compose build \ 175 | $builds_args \ 176 | --build-arg __VIA_SCRIPT=1 \ 177 | --build-arg __CACHE_TAG="$cacheTag" \ 178 | server 179 | } 180 | 181 | action__enter() { 182 | docker_compose exec server sh -c 'cd ~/akkoma && ash' 183 | } 184 | 185 | action__logs() { 186 | docker_compose logs "$@" 187 | } 188 | 189 | action__mix() { 190 | docker_compose exec server sh -c "cd ~/akkoma && mix $*" 191 | } 192 | 193 | action__restart() { 194 | action__stop 195 | action__start 196 | } 197 | 198 | action__start() { 199 | if [[ ! -d ./data/uploads ]] || [[ ! -d ./emoji ]]; then 200 | if [[ "$(id -u)" != "$DOCKER_UID" ]]; then 201 | echo "Please create the folders ./data/uploads and ./emoji, and chown them to $DOCKER_UID" 202 | exit 1 203 | fi 204 | 205 | mkdir -p ./data/uploads ./emoji 206 | fi 207 | 208 | docker_compose up --remove-orphans -d 209 | } 210 | 211 | action__up() { 212 | action__start 213 | } 214 | 215 | action__stop() { 216 | docker_compose down 217 | } 218 | 219 | action__down() { 220 | action__stop 221 | } 222 | 223 | action__status() { 224 | docker_compose ps 225 | } 226 | 227 | action__ps() { 228 | action__status 229 | } 230 | 231 | ### DISABLED FOR THIS SETUP 232 | # For Akkoma, I am not sure how this works yet seeing they don't use GitLab for their git hosting 233 | # That and I have no idea how this function works lol 234 | 235 | # action__mod() { 236 | # require_command dialog 237 | # require_command jq 238 | # require_command curl 239 | 240 | # if [[ ! -d ./debug.d ]]; then 241 | # mkdir ./debug.d 242 | # fi 243 | 244 | # if [[ ! -f ./debug.d/mod_files.json ]] || [[ -n "$(find ./debug.d/mod_files.json -mmin +5)" ]]; then 245 | # curl -sSL -# "$GITLAB_URI/$ENDPOINT_LIST/$AKKOMA_VERSION?format=json" > ./debug.d/mod_files.json 246 | 247 | # if [[ -f ./debug.d/mod_files.lst ]]; then 248 | # rm ./debug.d/mod_files.lst 249 | # fi 250 | 251 | # jq -r 'map("\(.)\n") | add' <./debug.d/mod_files.json >./debug.d/mod_files.lst 252 | # fi 253 | 254 | # if [[ -f ./debug.d/mod_files.lst ]] && [[ -r ./debug.d/mod_files.lst ]]; then 255 | # choices="" 256 | 257 | # while read -r candidate; do 258 | # choices="$choices $candidate $(echo "$candidate" | rev | cut -d/ -f1 | rev)" 259 | # done <<< "$(grep -E ".*$1.*" <./debug.d/mod_files.lst)" 260 | 261 | # res=$(mktemp) 262 | # dialog --menu "Select the file you want to modify:" 35 80 30 $choices 2>"$res" 263 | # choice=$(cat "$res") 264 | 265 | # install -D <(echo '') "./custom.d/$choice" 266 | # curl -sSL -# "$GITLAB_URI/$ENDPOINT_FILE/$AKKOMA_VERSION/$choice" > "./custom.d/$choice" 267 | # else 268 | # install -D <(echo '') "./custom.d/$1" 269 | # curl -sSL -# "$GITLAB_URI/$ENDPOINT_FILE/$AKKOMA_VERSION/$1" > "./custom.d/$1" 270 | # fi 271 | # } 272 | 273 | action__cp() { 274 | container="$(docker_compose ps -q server)" 275 | 276 | echo "$container:$1 -> $2" 277 | docker cp "$container:$1" "$2" 278 | } 279 | 280 | ######################################################### 281 | # Help # 282 | ######################################################### 283 | 284 | print_help() { 285 | echo " 286 | akkoma Maintenance Script 287 | 288 | Usage: 289 | $0 [action] [action-args...] 290 | 291 | Actions: 292 | build (Re)build the akkoma container. 293 | 294 | enter Spawn a shell inside the container for debugging/maintenance. 295 | 296 | logs Show the current container logs. 297 | 298 | mix [task] [args...] Run a mix task without entering the container. 299 | 300 | mod [file] Creates the file in custom.d and downloads the content from akkoma.dev. 301 | The download respects your \$AKKOMA_VERSION from .env. 302 | (CURRENTLY BROKEN!) 303 | 304 | restart Executes #stop and #start respectively. 305 | 306 | start / up Start akkoma and sibling services. 307 | 308 | stop / down Stop akkoma and sibling services. 309 | 310 | status / ps Show the current container status. 311 | 312 | copy / cp [source] [target] Copy a file from your pc to the akkoma container. 313 | This operation only works in one direction. 314 | For making permanent changes to the container use custom.d. 315 | 316 | ---------------------------- 317 | 318 | You can report bugs or contribute to this project at: 319 | https://memleak.eu/sn0w/pleroma-docker 320 | 321 | or specificly for this Akkoma downstream, at: 322 | https://github.com/hyena-network/akkoma-docker 323 | " 324 | } 325 | 326 | ######################################################### 327 | # Main # 328 | ######################################################### 329 | 330 | # Check if there is any command at all 331 | if [[ -z "$1" ]]; then 332 | print_help 333 | exit 1 334 | fi 335 | 336 | # Check for SHOPTs 337 | if [[ -n "$SHOPT" ]]; then 338 | for opt in $SHOPT; do 339 | if [[ $opt =~ ":" ]]; then 340 | set -o "${opt//-o:/}" 341 | else 342 | set "$opt" 343 | fi 344 | done 345 | fi 346 | 347 | # Check for DEBUG 348 | if [[ -n "$DEBUG" ]]; then 349 | if [[ $DEBUG == 1 ]]; then 350 | export DEBUG_COMMANDS=1 351 | elif [[ $DEBUG == 2 ]]; then 352 | set -x 353 | fi 354 | fi 355 | 356 | # Check if the option is "help" 357 | case "$1" in 358 | "help"|"h"|"--help"|"-h"|"-?") 359 | print_help 360 | exit 0 361 | ;; 362 | esac 363 | 364 | # Check if the called command exists 365 | func="action__${1}" 366 | if ! type -t "$func" 1>/dev/null 2>&1; then 367 | echo "Unknown flag or subcommand." 368 | echo "Try '$0 help'" 369 | exit 1 370 | fi 371 | 372 | # Fail if mandatory files are missing 373 | require_file ".env" ".env.dist" 374 | # No longer required 375 | #require_file "config.exs" "config.dist.exs" 376 | throw_file_errors 377 | 378 | # Parse .env 379 | load_env 380 | 381 | # Handle DEBUG=2 382 | [[ $DEBUG != 1 ]] || set -x 383 | 384 | # Jump to called function 385 | shift 386 | $func "$@" 387 | 388 | # Disable debug mode 389 | { [[ $DEBUG != 1 ]] || set +x; } 2>/dev/null 390 | --------------------------------------------------------------------------------