With the fall of #Firefish I started to look for alternatives for my nerdy instance. I won’t come back to #Akkoma and I really got fed up of the #Misskey / #Calckey flavour. I already have a #Mastodon that simply works great, and what about this #Glitch fork that just adds some features? Looks promising, let’s try it!
The Mastodon Glitch Edition is a fork from the original Mastodon code. As they explain in their documentation, installing it is mostly the same as installing the original Mastodon code. You could actually interchange the repositories between the original Mastodon and the Glitch Edition and the following steps would work the same.
This is going to be a Docker installation, and taking the simplest and most basic approach (no CDN, no customisations). I’ve divided the process into the following sections:
As usual in my articles, I assume the following:
naboo
and the user is xavi
.dagobah
, as explained in a previous article Quick DNS server on a Raspberry Pi, that is also working as a Reverse Proxy as explained in Set up a Reverse Proxy in a Raspberry Pi with ApacheDocker will allow us to run all the needed infrastructure isolated from the machine itself. It allows us to spin up any infrastructure without having an amount of components installed in our system, making it really easy to keep the system up-to-date and to repurpose the Raspberry Pi if needed.
docker-compose
is a wrapping tool that makes our live with Docker easier. We'll install both.
ssh naboo
ssh xavi@naboo
Download and install Docker
curl -sSL https://get.docker.com | sh
Add the current user into the docker
group
sudo usermod -aG docker $USER
Exit the session and ssh in again.
Test that it works with a hello world container:
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
70f5ac315c5a: Pull complete
Digest: sha256:c79d06dfdfd3d3eb04cafd0dc2bacab0992ebc243e083cabe208bac4dd7759e0
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(arm64v8)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Get the container ID of this test run. In my case is 1dfdb4d1a3cf
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1dfdb4d1a3cf hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago stupefied_hofstadter
Remove the test container
docker rm 1dfdb4d1a3cf
First of all, check what is the latest version by navigating to their releases: Releases · docker/compose · GitHub. At the moment of this article, the latest stable release was v2.24.3
Dowload the right binary from their releases into the binaries directory of our system. Pay attention to the versions in the following command:
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Give executable permissions to the downloaded file
sudo chmod +x /usr/local/bin/docker-compose
Test the installation:
docker-compose --version
It should print something like Docker Compose version v2.24.3
We just need to clone the code from the Mastodon Glitch Edition’s GitHub repository. As it is not classified by tags nor releases, we just make sure we have the latest code from main
.
Move yourself to your home, if you're not there already
cd ~
Clone the official Mastodon code in a directory called social.arnaus.net
(yeah, just the domain name, you can call it whatever you want)
git clone git@github.com:glitch-soc/mastodon.git social.arnaus.net
Now move to the new directory
cd social.arnaus.net
The docker-compose.yml
points to the Mastodon original image for 4.2.0, that runs ok but won’t contain the code upgrades of the Glitch version.
Searching around I found that there is actually a repository for the Glitch docker images, apparently for linux/amd64
and linux/arm64
(this last one should work for Raspberry Pi), but I just lost the fear to build my own images, it’s just taking ~20 mins, so why not?
At the moment of this guide, the version 4.3.0 was in the oven, almost ready in the version alpha, but the code in the main
branch was not fully updated. I mention it because I found some issues on the go like a new image split between the web
and the streaming
containers, that I could not find documented anywhere, only as a bug. The solution is actually to create 2 images:
First build the image considered main, for the web
and sidekiq
containers
docker build . -t glitch-soc:20240126 -f Dockerfile
Then build the image that will run the streaming
container, that we actually have the Dockerfile
file as well in the repository.
docker build . -t glitch-soc-streaming:20240126 -f streaming/Dockerfile
Here we can just use these images into our docker-compose.yml
, but I like to push them to my DockerHub and use the image from there, so:
Login to Docker
docker login
Tag the built main image to what will be used once pushed
docker tag glitch-soc:20240126 arnaus/glitch-soc:original_rpi4_4.3.0a
Push the built main image
docker push arnaus/glitch-soc:original_rpi4_4.3.0a
Tag the built streaming image to what will be used once pushed
docker tag glitch-soc-streaming:20240126 arnaus/glitch-soc-streaming:original_rpi4_4.3.0a
Push the built streaming image
docker push arnaus/glitch-soc-streaming:original_rpi4_4.3.0a
Update the docker-compose.yml
file, and replace the image file from the web
and sidekiq
containers with our arnaus/glitch-soc:original_rpi4_4.3.0a
and the image file from the streaming
container with our arnaus/glitch-soc-streaming:original_rpi4_4.3.0a
This point is a bit messy. I found a blog post that have it very well explained, so I will simply quote it directly:
If we simply try and launch setup, Docker will refuse to start the containers because
.env.production
doesn't exist.I had initially just touch'd the file (because you won't know what to put in it until after setup is complete), however, doing this leads to setup failing part way through.
There's a step where the setup script exports environment variables for use by later steps. However, some of those steps execute within a different container and variables exported in one container won't be available to another, so the
streaming
container fails to connect to Redis (because it tries to connect to the default - localhost rather than the redis container).To resolve this, we start by creating
.env.production
with known connection details (replace the Database password with the correct value)
cat << EOM > .env.production
DB_HOST=db
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASS=<replace>
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
EOM
(
REDIS_PASSWORD
is supposed to have an empty value in the above).
With this file created, we're ready to fire off the setup process
docker-compose run --rm web bundle exec rake mastodon:setup
It downloads the images and runs the container, triggering the rake process to setup the instance
[+] Creating 4/4
✔ Network socialarnausnet_internal_network Created 0.1s
✔ Network socialarnausnet_external_network Created 0.1s
✔ Container socialarnausnet-redis-1 Created 0.1s
✔ Container socialarnausnet-db-1 Created 0.1s
[+] Running 2/2
✔ Container socialarnausnet-redis-1 Started 0.7s
✔ Container socialarnausnet-db-1 Started 0.7s
Your instance is identified by its domain name. Changing it afterward will break things.
Domain name: social.arnaus.net
Single user mode disables registrations and redirects the landing page to your public profile.
Do you want to enable single user mode? No
Are you using Docker to run Mastodon? Yes
PostgreSQL host: db
PostgreSQL port: 5432
Name of PostgreSQL database: postgres
Name of PostgreSQL user: postgres
Password of PostgreSQL user:
Database configuration works! 🎆
Redis host: redis
Redis port: 6379
Redis password:
Redis configuration works! 🎆
Do you want to store uploaded files on the cloud? No
Do you want to send e-mails from localhost? No
SMTP server: smtp.gmail.com
SMTP port: 587
SMTP username: ****
SMTP password: ****
SMTP authentication: plain
SMTP OpenSSL verify mode: none
E-mail address to send e-mails "from": Mastodon <****>
Send a test e-mail with this configuration right now? Yes
Send test e-mail to: ****
Do you want Mastodon to periodically check for important updates and notify you? (Recommended) Yes
This configuration will be written to .env.production
Save configuration? Yes
Below is your configuration, save it to an .env.production file outside Docker:
# Generated with mastodon:setup on 2024-01-25 20:48:36 UTC
# Some variables in this file will be interpreted differently whether you are
# using docker-compose or not.
LOCAL_DOMAIN=social.arnaus.net
SINGLE_USER_MODE=false
SECRET_KEY_BASE=abcdefg1234567890
OTP_SECRET=bcdefga2345678901
VAPID_PRIVATE_KEY=cdefgab3456789012
VAPID_PUBLIC_KEY=defgacbd4567890123
DB_HOST=db
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASS=****
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=****
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_LOGIN=****
SMTP_PASSWORD=****
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS=Mastodon <****>
It is also saved within this container so you can proceed with this wizard.
Now that configuration is saved, the database schema must be loaded.
If the database already exists, this will erase its contents.
Prepare the database now? Yes
Running `RAILS_ENV=production rails db:setup` ...
Database 'postgres' already exists
Done!
All done! You can now power on the Mastodon server 🐘
Do you want to create an admin user straight away? Yes
Username: xavi
E-mail: ****
You can login with the password: efgabcd5678901234
You can change your password once you login.
Even it says that the configuration is saved, it is actually not as quoted above, so copy all the env vars above and paste them into the .env.production
, overwriting what we previously added.
So we assume that we have the domain social.arnaus.net
already set up to deliver the traffic to our home’s external IP and our router already routing the traffic for the port 80
and 443
to our reverse proxy machine. The next steps are adding this domain into the reverse proxy and generate a SSL certificate on it.
As mentioned in the Assumptions, I run a reverse proxy in a separated machine called Dagobah
. The reverse proxy is just a virtual host, so we only need to ssh there, create the virtual host, link it properly and restart the Apache:
SSH into the host
ssh xavi@dagobah
Create a new virtual host file
sudo nano /etc/apache2/sites-available/014-social.arnaus.net.conf
Add the following content. I had it quite easy as I already run a Mastodon instance and the configuration is actually the same. Please note that I directly set up both ports 80
and 443
, as in the next subsection I generate the SSL certificate and I like the have both references in the same file (as long as the certbot
finds the related 443
virtual host, it respects it and adds the configuration in there besides creating a separate file)
<VirtualHost 192.168.1.231:80>
ServerName social.arnaus.net
ProxyPreserveHost On
ProxyPass / http://naboo:3000/ nocanon
ProxyPassReverse / http://naboo:3000/
ProxyRequests Off
</VirtualHost>
<VirtualHost 192.168.1.231:443>
ServerName social.arnaus.net
RewriteEngine On
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /(.*) ws://naboo:4000/$1 [P,L]
ProxyPreserveHost On
ProxyPass / http://naboo:3000/ nocanon
ProxyPassReverse / http://naboo:3000/
ProxyRequests Off
ProxyPassReverseCookiePath / /
RequestHeader set X_FORWARDED_PROTO 'https'
</VirtualHost>
Save (ctrl
+ o
) and Exit (ctrl
+ x
)
Link the file into the Enabled Sites
cd /etc/apache2/sites-enabled
sudo ln -s ../sites-available/014-social.arnaus.net.conf
Restart Apache
sudo service apache2 restart
At this point we should be able to access to naboo
from outside if we would have the host up.
As we did several times, the reverse proxy host is the one that holds the SSL certificates to communicate with the outside world, but internally we go unencrypted from the reverse proxy to the instance itself. That’s why when we set up the virtual host in the reverse proxy we directed both cases to http://naboo:3000
.
Said that, we only need to generate the certificate. We already have the certbot
program installed (with the apache
module), so it’s as simple as running the following command:
sudo certbot --agree-tos -d social.arnaus.net --apache
… and after the challenge the virtual hosts get updated as follows:
<VirtualHost 192.168.1.231:80>
ServerName social.arnaus.net
ProxyPreserveHost On
ProxyPass / http://naboo:3000/ nocanon
ProxyPassReverse / http://naboo:3000/
ProxyRequests Off
RewriteEngine on
RewriteCond %{SERVER_NAME} =social.arnaus.net
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost 192.168.1.231:443>
ServerName social.arnaus.net
RewriteEngine On
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /(.*) ws://naboo:4000/$1 [P,L]
ProxyPreserveHost On
ProxyPass / http://naboo:3000/ nocanon
ProxyPassReverse / http://naboo:3000/
ProxyRequests Off
ProxyPassReverseCookiePath / /
RequestHeader set X_FORWARDED_PROTO 'https'
SSLCertificateFile /etc/letsencrypt/live/social.arnaus.net/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/social.arnaus.net/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
And here we come to the interesting part of the installation. Actually we installed the Mastodon Glitch Edition to profit from the extra features that it provide, and some of them are set up as parameters in the .env.production
file.
Simply edit it and add them, setting up the value that fits the best to us:
# Custom settings
# ---------------
# Various ways to customize Mastodon's behavior
# ---------------
# Maximum allowed character count
MAX_TOOT_CHARS=500
# Maximum number of pinned posts
MAX_PINNED_TOOTS=5
# Maximum allowed bio characters
MAX_BIO_CHARS=500
# Maximim number of profile fields allowed
MAX_PROFILE_FIELDS=4
# Maximum allowed display name characters
MAX_DISPLAY_NAME_CHARS=30
# Maximum allowed poll options
MAX_POLL_OPTIONS=5
# Maximum allowed poll option characters
MAX_POLL_OPTION_CHARS=100
One moment before starting the instance. In this installation we did not set up any separate location to hold the uploads and media from the statuses. It is not a joke, it takes space and we’re running the instance in a quite small disk.
We have few options, like connecting an external drive and mount it as a docker volume so that the media will exists outside the /
disk, or also setting up an Object Storage in a CDN (as I explained in catalan here).
Still, at this point what we need to do is to assign the public
directory to the docker
user that is the id 991
so uploads will work straight away:
sudo chown -R 991:991 public/
And now yes, we can start the instance!
docker-compose up -d
After starting the instance everything felt good, but visiting the Administration Dashboard for the first time, a message appeared saying:
There are pending database migrations. Please run them to ensure the application behaves as expected
First stop the instance
docker-compose down
Run the migrations
docker-compose run --rm web bundle exec rails db:migrate
Bring the instance back online
docker-compose up -d
I was surprised about how easy was to install this Mastodon Glitch Edition. Of course there were some challenges that made me search internet for answers (the instance setup that fails with Redis and that I run into a new image split with the new 4.3.0), but what is a project without blockers to solve?
Now I enjoy a Mastodon with the 2 out of my 3 main feature requests solved: configurable length and Markdown support. Now it’s only time for Quotes 😉 And the best of all is that I can just integrate smoothly to all Mastodon API side projects without having to implement adapters. I am very much looking forward to play with it!