A minimal set of commands I had to do to successfully run Mastodon via docker-compose on the VPS. Many OS specific configurations are omitted, as I decided to use Arch on this VPS as well, which is not what most people choose for their server environment, at least not when caveats of such choice play a major role.

Getting started

Clone the Mastodon repository. It contains a docker-compose.yml file as well as other files directly or indirectly referenced by it (for example package.json or yarn.lock):

git clone https://github.com/mastodon/mastodon.git
cd mastodon

Now here's what I occasionally do to help me keep track of the changes to the configuration files easily. Make a branch on a given tag, which at the time of writing was v3.4.1:

git checkout v3.4.1 -b v3.4.1-branch

Without creating a branch, the HEAD would be in a detached state (pointing at a tagged commit, not a branch), It would still track changes, but these would not be accessible after another checkout.

Tip: to get the latest available tag easily, you can use git rev-list as follows:

git fetch --all --tags
git describe --tags `git rev-list --tags --max-count=1`

Also consider changing the mastodon image to some tagged version. In the section web replace mastodon:latest image with the tagged one:

image: tootsuite/mastodon:v3.4.1

It is useful for referencing and searching for issues, should some arise, at the very least. Even more important to me is that it requires a manual intervention to bump a version number, so things won't suddenly change when the docker-compose script get restarted without you understanding why. It is overall a good practice to avoid unnecessary surprises.

Postgres database

The referenced version of postgres in the docker-compose file is 9.6-alpine. This might work, but I tested with 12.5-alpine instead and found no problems so far, so I changes to this version under the db section:

image: postgres:12.5-alpine

Start the container to setup the user, assuming the path to the docker-compose file is /home/mastodon/mastodon/docker-compose.yml. If not, modify the path so the postgres volume folder matches it. Consider setting a custom password:

sudo docker run --name postgres12 -v /home/mastodon/mastodon/postgres:/var/lib/postgresql/data -e POSTGRES_PASSWORD=password --rm -d postgres:12.5-alpine

Create a mastodon database user, use the password from above:

sudo docker exec -it postgres12 psql -U postgres
> exit
sudo docker stop postgres12

This makes database setup complete.

Set up Mastodon

This part is a little bit tricky, as it took me the most time to figure out right:

sudo docker-compose run --rm web bundle exec rake mastodon:setup

Fill the domain name you intend to run the instance. This one is probably hard to change once the instance is running. Fill the next questions according to the table below:

QuestionType in
Do you want to enable single user mode?No
Are you using Docker to run Mastodon?Yes
PostgreSQL host:mastodon_db_1
PostgreSQL port:5432
Name of PostgreSQL database:mastodon
Name of PostgreSQL user:mastodon
Password of ProstgreSQL user:password

The above part should look like this in the terminal:

Single user mode? N, Using Docker to run Mastodon? Y, PostgreSQL host: mastodon_db_1, PostgreSQL port: 5432, Name of PostgreSQL database: mastodon, Name of PostgreSQL user: mastodon, Password of ProstgreSQL user: password

The setup then continues with email capabilities configuration questions. I am omitting details for this part, as my email provider required different SMTP settings, some of which were not offered via this setup wizard. I have not found a reliable way to send a test email from the UI or the console later, so it might be worth trying here to get the emails sent out. Setting up cloud storage or email capabilities can be also safely skipped now and configured later, if you wish to do so, use these options:

QuestionType in
Do you want to store uploaded files on the cloud?No
Do you want to send e-mails from localhost?Yes
E-mail address to send e-mails "from":Enter
Send a test e-mail with this configuration right now?No
Save configuration?Yes

Your terminal should resemble this:

Store uploaded files in cloud: No, Send e-mails from localhost: Yes, Send e-mail "from": Enter, Send a test email now: No, Save configuration: Yes

The terminal then outputs the configuration, including secret keys. Copy and paste it into .env.production file in the cloned repository already containing postgres/ directory and docker-compose.yml file, among others.

The last part is to migrate the database and create an admin account. Answer Yes to both and proceed. The Mastodon instance admin user password will be generated and displayed, make sure to not lose it! If you lose it before logging in successfully, one way to obtain it again is to delete postgres/ folder and start over from the Postgres database step above.

This step is optional, although it is a nice addition to have a full-text search provided via ElasticSearch available. Edit the docker-compose.yml and uncomment two es related blocks:

#  es:
#    restart: always
#    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10
#    environment:
#      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
#      - "cluster.name=es-mastodon"
#      - "discovery.type=single-node"
#      - "bootstrap.memory_lock=true"
#    networks:
#      - internal_network
#    healthcheck:
#      test:
#        [
#          "CMD-SHELL",
#          "curl --silent --fail localhost:9200/_cluster/health || exit 1",
#        ]
#    volumes:
#      - ./elasticsearch:/usr/share/elasticsearch/data
#    ulimits:
#      memlock:
#        soft: -1
#        hard: -1

    build: .
    image: tootsuite/mastodon
    restart: always
    env_file: .env.production
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
      - external_network
      - internal_network
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"]
      - ""
      - db
      - redis
#      - es
      - ./public/system:/mastodon/public/system

Edit .env.production file and append the following:


The instance should now be ready to start.

First run

Start the whole stack, this can take a while:

sudo docker-compose up -d
sudo docker-compose down

This generates other files and folders, consider setting the permissions for them and start the instance again:

sudo chown -R 70:70 ./postgres
sudo chown -R 991:991 ./public
sudo chown -R 1000:1000 ./elascticsearch
sudo docker-compose up -d

Now without any modifications on docker-compose.yml the instance should be available under the port 3000. Configure the reverse proxy of your choice to terminate the SSL/TLS and to proxy the domain name inserted into the wizard earlier to this port. You can also find some inspiration about how to do so in my previous articles under tags Nginx and especially acme.sh, should you choose to use these two to manage this task and the certificates for you.

To access the web user interface, insert the admin user name and the password generated earlier, and you are ready to have fun in the fediverse!