
With the news about what’s happening on the BirdSite… I thought it might be time to explore what the state of the art alternative is these days.
As an aside, this isn’t the first time I’ve looked at Twitter alternatives, back in the very early days of Twitter I build and ran a project called BlueTwit inside IBM. This was ground up clone that was there to see how a micro blogging platform (how quaint a term…) would work in a large organisation. It had a Firefox plugin client, supported 250+ characters long before Twitter. The whole was written in Java ran on a modest AMD64 box under my desk for many years and was even played with by the IBM Research team before similar functionality ended up in IBM/Lotus Connections. (Someday I should do a proper writup about it…)
Anyway back to the hear and now… I have a propensity for wanting to know how things work under the covers (which is why I run my own web, DNS, mail, SIP, finger and, gopher server). So I thought I’d have a go at running my own Mastodon server for a little while to see how it all fits together.
A little digging shows that a Mastodon instance isn’t just one thing, it needs the following:
- Mastodon Web interface
- Mastodon Streaming interface
- Mastodon Job server (sidekiq)
- A PostgreSQL instance (for persistence)
- A Redis Instance
- A Elastic Search Instance (optional)
Given this list of parts trying to run it in a containerised environment made sense. I have both a Docker Compose setup and a Kubernetes setup at home for testing FlowForge on while I’m working, so that wouldn’t be a major problem (though I understand I’m the outlier here). I decided to go with Kubernetes, because that cluster is a bit bigger and I like a challenge.
Deploying to Kubernetes
A bit of Googling turned up that while there isn’t a published Helm chart, there is one included in the project. So I cloned the project to my laptop.
git clone https://github.com/mastodon/mastodon.git
Configuration
I then started to create a local-values.yml
file to contain my instances specific configuration details. To get a feel for what values I’d need I started by looking in the chart/values.yml
file to see what the defaults are and what I could override.
I also started to read the Mastodon server install guide as it had explanations to what each option means.
The first choice was what to call the instance. I went with a suggestion from @andypiper for the server name, but I’ll have the users hosted at my root domain
Yeah I was wondering whether there’s a recipe for adding it to @KubeSail … anyway, if you do it, you need to call it BlueToot.
— Andy Piper (@andypiper) October 29, 2022
This means that the server is called bluetoot.hardill.me.uk
and the instance is called hardill.me.uk
so users will be identified for example as @ben@hardill.me.uk
. These are configured as local_domain
and web_domain
Next up was setting up the details of my mail server so that thinks like alerts and password resets can be sent. That was all pretty self explanatory.
The first tricky bit was setting up some secrets for the instance. There are secret keys for authentication tokens and a seed for the OTP setup. The documentation says to use rake secret
but that implies you have Ruby environment already setup. I don’t work with Ruby so this wasn’t available. A bit more searching suggested that OpenSSL could be used:
openssl rand -hex 64
The next set of secrets are the vapid public and private keys. Here the documentation again has a rake
command, but this time it appears to be a Mastodon specific Gem. To get round this I decided to pull the pre-built docker image and see if I could run the command in the container.
docker pull tootsuite/mastodon
docker run -it --rm --entrypoint /bin/bash tootsuite/mastodon:latest
mastodon@ab417e0a893a:~$ RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key
VAPID_PRIVATE_KEY=MdgBNkR98ctXtk3xSTbs7-KJBCcykvvw_q1aFGNfMgY=
VAPID_PUBLIC_KEY=BB-g5Lgund3gYi3UhGGn7Z1Yj06gy4DqdozXQXYxeCDJjpEUW9TXYau7Ifv9xK_676MgUE4JSOSh4XSsroBoHmo=
(These keys are purely for demonstration and have not been used)
I made the decision to skip the elastic search instance to start with just to try and limit just how many resources I need to provide.
There are a couple of other bits I tweaked to make things work with my environment (forcing the bitnami charts to run on the AMD64 nodes rather than the ARM64) but the local-values.yml
ended up looking like
mastodon:
createAdmin:
enabled: true
username: fat_controller
email: mastodon@example.com
local_domain: example.com
web_domain: bluetoot.example.com
persistence:
assets:
accessMode: ReadWriteMany
resources:
requests:
storage: 10Gi
system:
accessMode: ReadWriteMany
resources:
requests:
storage: 10Gi
smtp:
auth_method: plain
delivery_method: smtp
enable_starttls_auto: true
port: 587
server: mail.example.com
login: user
password: password
from_address: mastodon@example.com
domain: example.com
secrets:
secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
otp_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
vapid:
private_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
public_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
elasticsearch:
enabled: false
ingress:
annotations:
tls:
hosts:
- host: bluetoot.example.com
paths:
- path: '/'
postgresql:
auth:
password: xxxxxxx
primary:
nodeSelector:
beta.kubernetes.io/arch: amd64
redis:
password: xxxxxxx
master:
nodeSelector:
beta.kubernetes.io/arch: amd64
replica:
nodeSelector:
beta.kubernetes.io/arch: amd64
Deploying
The first part is to run helm
helm upgrade --install --namespace mastodon --create-namespace mastodon ../mastodon/chart -f ./local-values.yml
The first time I ran this it failed to start most of the Mastodon pods with a bunch of errors around the PostgreSQL user password. I tracked it down to a recent Pull Request so I raised a new issue and a matching PR to fix things. This got merged really quickly which was very nice to see.
Once I’d modified the helm chart templates a little it deployed cleanly.
Proxying
Next up was setting up my Internet facing web proxy. The Ingress controller on my Kubernetes cluster is not directly exposed to the internet so I needed to add an extra layer of proxying.
First up was to setup an new host entry and renew my LetsEncrypt certificate with the new SAN entry.
location / {
proxy_pass https://kube-three.local;
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_redirect off;
proxy_read_timeout 900;
proxy_set_header Host $http_host; # host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_http_version 1.1;
}
It was important to make sure that the proxy target was over https
otherwise the mastodon web app would trigger a redirect loop. the proxy_ssl_verify off;
is because I’m using the default https certificate on the kubernetes ingress controller so it will fail the hostname check.
The other bit that needed doing was to add a proxy on the hardill.me.uk
server for the .well-known/webfinger
path to make sure user discovery works properly.
location /.well-known/webfinger {
return 301 https://bluetoot.example.com$request_uri;
}
First Login
Now all the proxying and https setup is complete I can point my browser at https://bluetoot.hardill.me.uk and I get the intial sigin/signup screen.
As part of the configuration I created an admin user (fat_controller
) but didn’t have a way to login as I don’t know what the generated password is. I tried follwing the lost password flow but couldn’t get it to work so followed the documentation about using the admin cli to do a password reset. I did this by using kubectl
to get a shell in the mastodon web app pod in the cluster.
kubectl -n mastodon exec -it mastodon-web-5dd6764d4-mvwnl /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
mastodon@mastodon-web-5dd6764d4-mvwnl:~$ RAILS_ENV=production bin/tootctl accounts modify fat_controller --reset-password
OK
New password: fc20b54eddb312bf9462abfec77a27d8
I could then log in as the administrator, update things like the contact email address, change the default user creation to require approval for new users.
What next?
I’m not sure if I’m going to keep this instance yet. At the moment it’s a testing environment and I’m not sure yet how much I’m going to actually use Mastodon. A lot will depend on what Mr Musk decides to do with his new play thing and if/when the communities I follow move.
But I will probably play with writing a few bots for some of the home automation things I have on the go, e.g. as I write this I’m waiting for a WiFi CO2 monitor to be delivered. Having a bot that Toots the level in my office sounds like a good first project.