With the landing of the new #Mastodon v4.2.0, I decided to document the process I take to build and apply a new #Docker image for the instance I maintain at talamanca.social. Even it is distributed with an official Docker image, there are some customizations (mostly icons and images) that I want to keep and therefore I must build my own one.


In this article I go through the process of building a docker image based on the official release and applying some customizations, as I did for my Pixelfed instance. I split it in the following sections:

  1. Assumptions

  2. Update the repository

  3. Build the image

  4. [optional] Push the image

  5. Use the image

  6. Restarting the instance

  7. Wrapping up

1. Assumptions

The whole process assumes some points that I summarize here:

  • This instance runs over Raspberry Pi 4 8GB. The set up follows my article Spawning a Raspberry Pi 4 with Raspberry Pi OS.

  • This instance is already running at the latest version before the new one came up. This means that the instance is right now at version 4.1.9 before upgrading to 4.2.0. It is highly recommended that you go through the Upgrade Notes of each version between yours and the last one (or the one you update to).

  • The instance is running behind a Reverse Proxy as I explained in Set up a Reverse Proxy in a Raspberry Pi with Apache which brings the traffic in and cares about the SSL certificates. It also uses a local DNS server as explained in Quick DNS server on a Raspberry Pi and that's why you won't see IP addresses here.

  • The hostname is jakku and the linux user is xavi. You may want to change them for your IP address and your own username.

  • The mastodon code came from a git clone from the official repository into the path ~/mastodon.

  • I pay very attention to the release article that includes the changes and the upgrade notes, a mandatory read.

2. Update the repository

The basic motivation for me to build a new image is because the Mastodon engineers come up with a new version, so the very first thing to do is to update their code.

2.1. Go to the repository

Seems obvious, but just to have it explicit, let's connect to the server.

  1. SSH into the machine

    ssh xavi@jakku
  2. Move yourself into the directory that contains the repository

    cd ~/mastodon

2.2. Keep the customizations safe

Through time I customized the instance, by changing the favicons and the images that appear in the static and about pages. Even this is something that escape from the scope of the article, I want to include the steps to just keep them safe while updating and include them when building the new image.

  1. Rename the docker-compose.yml file. It always changes and if we keep it together with the rest of the customizations the restoration will fail and things get complicated.

    mv docker-compose.yml docker-compose.yml.custom
  2. Get the original docker-compose.yml file.

    git checkout docker-compose.yml
  3. Save the customisations (logos, icons, ...). The customized docker-compose.yml.custom will come together.

    git stash

Now we have our repository clean and ready to receive the new code.

2.3. Get the changes for the version 4.2.0

  1. Fetch the git info from the server.

    git fetch
  2. Checkout the code for the tag version v4.2.0

    git checkout v4.2.0 

3. Build the Image

Once we have the new code in place we're ready to build the image, but we're missing our customizations.

3.1. Recover and apply the customizations

⚠️ Here I must mention that this may break. You need to be aware that in some cases the version bump include changes that make our customizations not to work, most of the times happening in major version upgrades. As this is relying in git stash, could be that the directory does not exist anymore, or the files permissions change, or the customizations need to be applied over other files...

Read if your customizations are still valid over the new code. Only when I upgraded from the 3.5.2 to the 4.0.x I lost any customization.

So, assuming that the new code admits your previoys customizations, re-apply them

git stash pop

3.2. Build the image

And here we are finally. We'll build the new image and we'll define a local tag. I use to define it as instance_type:date for my local images so that later I can say clearly which one is it and from when.

Run the build command defining a tag and using the oficial Dockerfile:

docker build . -t mastodon:20230922 -f Dockerfile

This will take really long. You can go now for a coffee.

4. [optional] Push the image

At this point we have a local docker image that we can use to bring up our instance. Still, I like to have the image stored somewhere else for sharing and quick restoring reasons.

This step requires that you have an account in Docker Hub and already created a repository there.

ℹ️ If you want to avoid this step, just jump to the next section and use the local tag mastodon:20230922 wherever I use the remote tag arnaus/mastodon:talamanca_4.2.0

Images in remote need to have a tag. We will create a new tag for the remote image based on the local image, and then push this image to the remote artifactory.

  1. Login to Docker

    docker login
  2. Tag the image we built with a label relating to your account, project and version.

    docker tag mastodon:20230922 arnaus/mastodon:talamanca_4.2.0
  3. Push the image to the artifactory

    docker push arnaus/mastodon:talamanca_4.2.0

At this point the image with our customized code of mastodon 4.2.0 is ready to be used from anywhere.

5. Use the image

Now it's time to prepare the use of the newly created image. Can be that some customizations rely in a new volume in the web container, or that we change the port listening for the traffic. That's why we have the previous docker-compose.yml file still at hand as docker-compose.yml.custom.

5.1. Compare the docker-compose.yml files

We never know what comes new fom the repository. What if we are overwriting something that now it's key to the instance? Let's find out what's new and if we can simply replace the files and update the reference to the image.

  1. Compare the docker-compose.yml that comes with the new version and the custom one.

    diff docker-compose.yml docker-compose.yml.custom

    What I find here is something like the following:

    <     image: ghcr.io/mastodon/mastodon:v4.2.0
    >     #image: ghcr.io/mastodon/mastodon:v4.1.9
    >     image: arnaus/mastodon:talamanca_4.1.9
    <     image: ghcr.io/mastodon/mastodon:v4.2.0
    >     #image: ghcr.io/mastodon/mastodon:v4.1.9
    >     image: arnaus/mastodon:talamanca_4.1.9
    <     image: ghcr.io/mastodon/mastodon:v4.2.0
    >     #image: ghcr.io/mastodon/mastodon:v4.1.9
    >     image: arnaus/mastodon:talamanca_4.1.9

    So, this time we only received changes about the new official image, that we won't use because we generate our own one. All good, we can move forward the fastest way: we'll delete the current custom and customize the original.

    If that would not be the case, a more manual merge should happen, understanding what do we do on top of the original code and what did change in the distributed original file.

  2. Remove the docker-compose.yml.custom file, we don't need it anymore as we saw in the previous point.

    rm docker-compose.yml.custom
  3. Edit the docker-compose.yml file to allocate our image

    nano docker-compose.yml
  4. Replace all image: ghcr.io/mastodon/mastodon:v4.2.0 lines there (should be 3) with our image tha we just pushed image: arnaus/mastodon:talamanca_4.2.0

  5. Save (ctrl + o) and exit (ctrl + x)

6. Restarting the instance

So here where are with our new image and the docker-compose.yml ready to be run. Just one more thing before that: migrations!!.

In some cases (like this one in the v4.2.0) there are changes in the DB that need to be applied before and/or after restarting the service. You will read about it in the Upgrade Notes published with the new release.

  1. Apply the pre-deployment database migrations.

    docker-compose run --rm -e SKIP_POST_DEPLOYMENT_MIGRATIONS=true web bundle exec rails db:migrate
  2. Restart the instance, all services.

    docker-compose up -d
  3. Apply the post-deployment database migrations

    docker-compose run --rm web bundle exec rails db:migrate
  4. If you use Elasticsearch, rebuild the search indexes

    docker-compose run --rm web bin/tootctl search deploy --reset-chewy

Done!! 🥳

7. Wrapping up

The use of Docker images allow us to isolate the infrastructure that a Mastodon (and any) instance requires. We have a web server, a database and more services encapsulated in a bunch of containers that talk to each other and also, they get upgraded eventually. By using Docker images we keep all complexity inside the image and we just care about applying some customizations over the code and generating a new image.

I've been following these steps in all updates from the version 3.5.2 until this 4.2.0 and never had more issues than a customization that gets lost in new code (and I need to find a new way).

I hope that I was clear enough and this guide was useful 😊

Previous Post Next Post