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:
Assumptions
Update the repository
Build the image
[optional] Push the image
Use the image
Restarting the instance
Wrapping up
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.
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.
Seems obvious, but just to have it explicit, let's connect to the server.
SSH into the machine
ssh xavi@jakku
Move yourself into the directory that contains the repository
cd ~/mastodon
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.
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
Get the original docker-compose.yml
file.
git checkout docker-compose.yml
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.
Fetch the git info from the server.
git fetch
Checkout the code for the tag version v4.2.0
git checkout v4.2.0
Once we have the new code in place we're ready to build the image, but we're missing our 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
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.
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.
Login to Docker
docker login
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
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.
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
.
docker-compose.yml
filesWe 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.
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:
59c59,60
< image: ghcr.io/mastodon/mastodon:v4.2.0
---
> #image: ghcr.io/mastodon/mastodon:v4.1.9
> image: arnaus/mastodon:talamanca_4.1.9
80c81,82
< image: ghcr.io/mastodon/mastodon:v4.2.0
---
> #image: ghcr.io/mastodon/mastodon:v4.1.9
> image: arnaus/mastodon:talamanca_4.1.9
98c100,101
< 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.
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
Edit the docker-compose.yml
file to allocate our image
nano docker-compose.yml
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
Save (ctrl
+ o
) and exit (ctrl
+ x
)
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.
Apply the pre-deployment database migrations.
docker-compose run --rm -e SKIP_POST_DEPLOYMENT_MIGRATIONS=true web bundle exec rails db:migrate
Restart the instance, all services.
docker-compose up -d
Apply the post-deployment database migrations
docker-compose run --rm web bundle exec rails db:migrate
If you use Elasticsearch, rebuild the search indexes
docker-compose run --rm web bin/tootctl search deploy --reset-chewy
Done!! 🥳
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 😊