Tech
I have been a heavy user of Mastodon since 2022, mainly after I abandoned Twitter due to the wave of layoffs following Elon Musk’s acquisition.
I loved from the start the idea of a decentralized social network, and for that reason I was always fascinated by the idea of hosting my own instance.
The idea of this post is to have a set of small notes to remember, especially for myself, about how I performed the process to create an instance with what I had at home.
So this is not a step-by-step guide showing every single detail to reach the result, there are plenty of those on the internet, but rather a quick set of notes to keep in mind.
A single instance, a single user, nothing more. Although this may seem unusual to many, there are actually many instances that have only one user.
There are pros and cons to this choice. In my case, it was a sort of challenge, to put my skills into practice and see if I could build a fully functioning instance. Consequently, the instance had to have only me as the user.
For anyone planning something similar long-term, a single-user instance can also be an interesting choice if you want to own everything, but equally you have to do extra work to get the instance to start populating.
Being the only users, the posts section will be occupied only by your posts, hashtags will need to be searched and followed manually, the news section will remain empty. Searching for toots, hashtags, users will be heavily reduced and you may need to configure add-ons like Elasticsearch for full-text search.
Of course there are various other ways to populate that small instance, but it’s good to keep these things in mind.
In my opinion, the idea at most is to create and manage a single instance with a base of friends, family, relatives and people who share this passion with you.
The reference guide is the one on the Mastodon site; it shows step by step how to install and manage everything.
For our instance, we need:
In my case, I used a domain bought cheaply on Cloudflare and for email delivery I simply used the configuration of an old Gmail account.
Actually, the last point can be skipped if you are the only users of the instance, most operations can be done from the CLI using tootctl.
I decided to use an old ThinkPad L470, i5-6300U, 8GB RAM DDR4-2133, 256GB SSD, Intel HD Graphics 520, which I had at home and always used for small experiments.
I wiped the disk and installed a fresh copy of Ubuntu 24.04 LTS server. After running the usual package updates I installed openssh and changed the configuration to use keys instead of passwords:
sudo apt install openssh-server
This is the only step not present in the Mastodon guide. After that I followed the guide step by step, that’s it!
You get to the point of obtaining the certificate:
certbot certonly --nginx -d example.com
This wasn’t possible because my little ThinkPad is not exposed to the internet; it remains hidden behind the private network. The problem was then to find a way to avoid:
Since the domain was on Cloudflare I started looking for any modes or features that could help with this. Enter Cloudflare Tunnels.
Cloudflare Tunnels uses an outbound connection, completely avoiding the issue of incoming connections. In simple terms:
The result is that there’s no need to open any incoming port on your firewall or router.
Consequently, Cloudflare handles the requests to the site and Cloudflare Tunnels routes the public traffic to the local Nginx service that Mastodon relies on.
In short:
cloudflared, on the laptop creates an outbound tunnel to Cloudflare.
The problem is that, at least as far as I could understand, the Mastodon Nginx configuration expects an SSL certificate, so you have to give it one because Cloudflare Tunnels will connect to Nginx over HTTPS.
It’s not the most elegant solution, but I created a self-signed certificate valid for one year. I then created the folders Nginx expects:
sudo mkdir -p /etc/letsencrypt/live/example.com/
And the certificate:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/letsencrypt/live/example.com/privkey.pem \
-out /etc/letsencrypt/live/example.com/fullchain.pem
Specifying the website domain for Mastodon in the Common Name field.
After that you can continue following the Mastodon guide again by copying the certificate:
sudo cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
sudo rm /etc/nginx/sites-enabled/default
At this point, Nginx is running on https://localhost port 443, using a self-signed certificate.
Creating the communication tunnel was very simple because it is handled directly on Cloudflare.
Once you start the Zero Trust guided setup, go to Networks > Tunnels and create a new Tunnel specifying Cloudflared.
You then get the credentials to install and start the tunnel on the local machine.
Once the tunnel is connected, you can configure a new Published application routes with:
The URL is where Nginx is listening; it will then route the request to the Mastodon components in charge.
After setting everything up, calling the site, nothing worked. 🥲
The solution became obvious after analyzing what happens when a visitor tries to access the site.
Ending up with a nice 502 Bad Gateway error.
So? The trivial fix is to enable the No TLS Verify option in the tunnel settings.
By enabling this option, you give cloudflared explicit instructions that when it connects to https://localhost, it should ignore the fact that the SSL certificate is not trusted.
So the flow becomes: the visitor’s request is handled with Cloudflare’s valid SSL, the HTTPS request is routed via the tunnel to https://localhost:443 where, with No TLS Verify enabled, cloudflared ignores the self-signed certificate and forwards the request to Nginx which serves the Mastodon components (Puma, Sidekiq, etc.).
Is it okay to disable TLS verification? NO!
Disabling TLS verification is a terrible security practice.
But in this specific context, it is acceptable.
The traffic protected by that certificate travels only inside my laptop, from the cloudflared process to the Nginx process.It’s not traveling on my local network or the Internet. The risk that someone intercepts that communication on localhost is practically nil.
So how did it go in the end?
Not bad, everything worked and everything ran without particular problems.
Reading online I saw many people suggesting various solutions to start populating your instance, which will initially be empty. Everyone discouraged using relays because they bring a high data load that you will mostly never consume if you’re just experimenting with your instance.
So, what did I do?
I decided to use relays.
In particular, the relay of the main instance mastodon.social.
In one day it reached 42 GB of media storage on average:
Playing with relays, especially adding large ones to a new instance, and moreover on my small laptop, was like pouring gasoline on a fire.
A relay forwards to the instance all the posts from hundreds of other instances connected to it. When you add a relay, the server is suddenly flooded with thousands of posts and activities from users it has never seen before.
Consequently, for each single post the server must get to work. It creates a huge queue of jobs in Sidekiq, including:
The consequence was a rise in the failure rate of handling many requests:
The poor laptop doesn’t have the resources to handle this deluge from one of the largest instances. I should add that, out of laziness, I didn’t even plug in the LAN cable and the laptop handled everything over Wi‑Fi.
So, indeed, unless you want to waste space and resources, using relays is not a good practice.
In the end, however, the experience was interesting and educational and taught me, even from a very small start, how demanding managing the entire Mastodon infrastructure can be with hundreds of thousands of people constantly connected.