The Joys of Self-Hosting

From Lair Of Sorrow

To the surprise of probably nobody, I am not a huge fan of corporations, especially when it comes to the internet. For decades I have been self-hosting - first on my own dsl line, then by renting one in a small server farm. This allowed me to own my email, webpages, network file storage and many other things.

Mastodon

I am also not a huge fan of social networks, with twitter being probably the only one I really *use* (as in: I am there from time to time). Recent advancements on the matter (i.e. the acquisition of the platform by a certain billionaire) made me look for an alternative - and that is how I rediscovered Mastodon. To a little surprise, the technology has matured in the past years, so I decided to host my own instance of it and become part of the *fediverse*. And no, I do not really care if anyone follows me or reads my content. I write things mostly for myself to remember them when needed ;)

The setup

At the moment of installation the server was running Ubuntu 24.04 and Apache 2. I have to say that the official setup guide was of great help and it was pretty straightforward to follow it. Most of the steps worked, with some notable exceptions.

Mastodon

I used the latest version of Mastodon available at the time, so 4.3.1. System user has its home directory not in /home/mastodon, but elsewhere. The home directory is readable by various user groups, most notably www-data.

Ruby 3

Sadly, there is no default package for the latest version of Ruby on Ubuntu, so some magic with rbenv was needed. Remember to update $PATH to include the .rbenv version first, otherwise all gems install nicely, but out of a sudden there is an error of a non-existing class, pointing still to version 2.7.

Node 18

I decided to use a newer version of Node - 23.2 instead of the suggested LTS 18. That failed spectacularly - while I was able to get everything up, Mastodon just ended up showing an empty main page, with files failing to load. To my surprise, some pages were showing (like account settings), but the web app was unusable.

So I had to install the recommended Node 18 instead of the newest version, and it just worked.

My previous installation used also Node 18, but for an older version of Mastodon (4.0.2, which recommended Node 16). That broke yarn, as it uses openssl 3 instead of 2. To allow it to work an extra parameter must be provided when running the setup: RAILS_ENV=production NODE_OPTIONS=--openssl-legacy-provider bundle exec rake mastodon:setup (only if precompiling assets is needed).

Postgres

Despite creating a database user, its database and making the user the owner of it, the install script still required the user to have createdb rights.

Also, the database password is written in plain text in the .env file. Please make sure nobody can access that file and that the password is not used anywhere else. As soon as the setup was done I also updated the postgres user with nocreatedb, just in case.

Paths

As I mentioned before, my instance is installed in a directory different from /home/mastodon. This means that all the paths in systemd configuration were incorrect and required adjusting.

Apache 2 configuration

The setup deals with proxying ngix. Here are the relevant bits for Apache:

       <Directory "/path/to/mastodon/public">
           Options Indexes MultiViews FollowSymLinks
           AllowOverride None
           Require all granted
       </Directory>
       <LocationMatch "^/(assets|avatars|emoji|headers|packs|sounds|system)>
         Header always set Cache-Control "public, max-age=31536000, immutable"
         Require all granted
       </LocationMatch>
       <IfModule mod_headers.c>
         Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
         Header always set Referrer-Policy "strict-origin-when-cross-origin"
       </IfModule>
       SSLProxyEngine On
       SSLEngine on
       SSLProtocol -all +TLSv1.2
       SSLHonorCipherOrder on
       SSLCipherSuite EECDH+AESGCM:AES256+EECDH:AES128+EECDH
       Protocols h2 h2c http/1.1
       ProxyPreserveHost On
       RequestHeader set X-Forwarded-Proto "https"
       ProxyPass /api/v1/streaming/ ws://localhost:4000/
       ProxyPassReverse /api/v1/streaming/ ws://localhost:4000/
       ProxyPass / http://localhost:3000/
       ProxyPassReverse / http://localhost:3000/
       ErrorDocument 500 /500.html
       ErrorDocument 501 /500.html
       ErrorDocument 502 /500.html
       ErrorDocument 503 /500.html
       ErrorDocument 504 /500.html

This seems to be working fine. Of course the site is behind a Let's Encrypt certificate.

Rails issues

Once everything was up and running I was constantly getting 404 Not Found from Apache. Checking directly with Rails by wget http://localhost:port/intent.css I noticed that the real code was actually 403 Forbidden. That is how I found out that Rails blocks all hosts by default and a magic line needs to be added to configuration: config.hosts << 'localhost'.

That solved 403, it was now showing 404 from both web servers - and that was caused by Rails not serving static files. To solve that, I simply modified the setting to config.public_file_server.enabled = true.

Conclusion

Setting up Mastodon was an interesting experience, relatively easy to follow, but with a few twists on the way. However, it seems to be working - feel free to follow me on my own Mastodon server.