Operating Akkoma
Series: Akkoma
A couple of months into self-hosting my Akkoma instance, I find myself doing a couple of operational tasks at a recurring basis. So might as well write them down here for future reference. Again, this is on my ARM-based machine with Ubuntu 22.04, where Akkoma is installed from source.
Backing Up (Periodically)
The doc’s backup instructions essentially says we should back up the database, plus a couple of configs and static files/directories.
So first off, ensure we have a place to put the backup files by creating this backup directory:
sudo su - root
mkdir -p /mnt/backups/akkoma
chown -R akkoma /mnt/backups/akkoma
chmod o=rw /mnt/backups/akkoma/
Then I whip up a quick script to back up all things mentioned in the doc:
#!/usr/bin/env bash
set -e
echo "Akkoma backup starting"
BACKUP_DIR_PARENT=/mnt/backups/akkoma
BACKUP_DIR=$BACKUP_DIR_PARENT/$(date -I)
mkdir -p $BACKUP_DIR/config
echo "Stopping akkoma"
systemctl stop akkoma
echo "Backing up postgres"
sudo -Hiu postgres pg_dump -d akkoma --format=custom -f /tmp/akkoma.pgdump
mv /tmp/akkoma.pgdump $BACKUP_DIR/akkoma.pgdump
echo "Copying config and static files"
cp /opt/akkoma/config/prod.secret.exs $BACKUP_DIR/config/prod.secret.exs
cp /opt/akkoma/config/setup_db.psql $BACKUP_DIR/config/setup_db.psql
cp -r /var/lib/akkoma/uploads $BACKUP_DIR
cp -r /var/lib/akkoma/static $BACKUP_DIR
echo "Restarting akkoma"
systemctl start akkoma
echo "Pruning backups older than the last 8"
cd $BACKUP_DIR_PARENT
ls -rt $BACKUP_DIR_PARENT | head -n -8 | xargs rm -r
I put this script at /usr/local/bin/akkomabackup
.
My goal is to create a backup once per day, so I also set up a systemd timer to run the backup script periodically. Steps:
- Create the backup service file as
/etc/systemd/system/akkoma-backup.service
:[Unit] Description="Backing up my Akkoma instance" [Service] ExecStart=/usr/local/bin/akkomabackup
- Create the timer file as
/etc/systemd/system/akkoma-backup.timer
:[Unit] Description="Run akkoma-backup.service 5min after boot and every 24 hours relative to activation time" [Timer] OnBootSec=5min OnUnitActiveSec=24h OnCalendar=Mon..Sun *-*-* 10:00:* Unit=akkoma-backup.service [Install] WantedBy=multi-user.target
- Validate the setup with:
systemd-analyze verify /etc/systemd/system/akkoma-backup.*
- Enable the timer:
systemctl start akkoma-backup.timer systemctl enable akkoma-backup.timer
- Verify that the backup timer works:
If the timer file ever gets changed after it starts, do a
systemctl status akkoma-backup.timer systemctl status akkoma-backup.service
systemctl daemon-reload
to reapply the change.
Restoring From A Backup
Backups are useless unless I know how to restore them. Here are the steps I took during a fire drill:
-
Restore static files from the backup:
sudo -su akkoma BACKUP_DIR=/mnt/backups/akkoma/$(ls -rt /mnt/backups/akkoma/ | tail -1) cp -r $BACKUP_DIR/uploads/. /var/lib/akkoma/uploads cp -r $BACKUP_DIR/static/. /var/lib/akkoma/static
-
Stop the service:
exit sudo -su root systemctl stop akkoma
-
Restore the db from the backup – this step is pretty dangerous! I haven’t found a better way to make sure the pgdump file is valid before erasing the db yet though.
BACKUP_DIR=/mnt/backups/akkoma/$(ls -rt /mnt/backups/akkoma/ | tail -1) cd /opt/akkoma/ cp $BACKUP_DIR/config/setup_db.psql /tmp/setup_db.psql cp $BACKUP_DIR/akkoma.pgdump /tmp/akkoma.pgdump # check the backup file to ensure it looks ok before dropping the database head /tmp/akkoma.pgdump tail /tmp/akkoma.pgdump # restore the db sudo -Hu postgres psql -c 'DROP DATABASE akkoma;'; sudo -Hu postgres psql -c 'DROP USER akkoma;' sudo -Hu postgres psql -f /tmp/setup_db.psql sudo -Hu postgres pg_restore -d akkoma -v -1 /tmp/akkoma.pgdump
-
Do this only if you’re upgrading from a previous version – migrate the db:
sudo -su akkoma MIX_ENV=prod mix ecto.migrate
-
Restart the service:
exit systemctl start akkoma # verifications systemctl status akkoma journalctl -u akkoma.service -f
-
Generate the statistics for postgres to plan queries:
sudo -Hu postgres vacuumdb --all --analyze-in-stages
For some extra precautions, I also back up the entire VM daily from my VPS provider, so that I can roll back in case I really mess things up.
Renewing That TLS Certificate
I thought my TLS certificate was going to be renewed automatically, but at one point near my cert expiration, I started getting emails prompting me to renew my cert. Turns out (with systemctl status certbot.service
) the renewal has been consistently failing because the script needs port 80, which conflicts with nginx, who also uses port 80. So to manually renew the cert, just do:
systemctl stop nginx # free up port 80
/usr/bin/certbot renew
systemctl start nginx
For an automated solution, I made an edit to /lib/systemd/system/certbot.service
by adding
ExecStartPre=systemctl stop nginx
ExecStartPost=systemctl start nginx
under [Service]
. After running systemctl daemon-reload
, the service starts to succeed. (This isn’t perfect since every time the renewal script runs, we have a tiny bit of downtime. But hey.)
Upgrading the Akkoma Instance
Upgrading Akkoma usually doesn’t come with a big fanfare – except that, starting v3.8.0, Akkoma requires an Elixir version 1.14 that’s not provided by the apt
package manager. This brings me some confusion since I’ll have to figure out how to install Elixir 1.14, and ensure the systemd service uses that upgraded Elixir runtime.
- Switch to the
akkoma
user:sudo -su akkoma /bin/bash cd /opt/akkoma/
- Download asdf, the recommended multi-version runtime manager:
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.3 # latest stable at the time of writing
- Install asdf:
# add to ~/.bashrc: echo ". $HOME/.asdf/asdf.sh" >> "$HOME"/.bashrc echo ". $HOME/.asdf/completions/asdf.bash" >> "$HOME"/.bashrc source ~/.bashrc # verify asdf --version
- Install Elixir 1.14.2 as required by Akkoma v3.8.0:
# lock into the specific versions touch $HOME/.tool-versions echo "erlang 25.0" > $HOME/.tool-versions echo "elixir 1.14.2" >> $HOME/.tool-versions # install asdf plugin-add erlang asdf plugin-add elixir asdf install # verify with elixir -v # should be 1.14.2
- Make sure systemd uses the proper elixir version by editing
/etc/systemd/system/akkoma.service
(reference). Two changes to this file are required:- Adding
Environment="PATH=/opt/akkoma/.asdf/shims:/opt/akkoma/.asdf/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
- And changing the
ExecStart
directive to
ExecStart=/opt/akkoma/.asdf/shims/mix phx.server
Moving on to the real upgrading work, the doc had it pretty nicely.
- Get the code:
sudo -su akkoma # switch to user 'akkoma' cd /opt/akkoma/ git fetch git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
- Get the dependencies, and compile the code:
export MIX_ENV=prod mix deps.get mix compile
- Migrate the DB and install frontends:
exit # (OCI specific) go back to the ubuntu user sudo systemctl stop akkoma sudo -su akkoma MIX_ENV=prod mix ecto.migrate MIX_ENV=prod mix pleroma.frontend install pleroma-fe --ref stable MIX_ENV=prod mix pleroma.frontend install admin-fe --ref stable
- Optionally verify that the new backend version works, by starting the app without systemd (then stop the app):
MIX_ENV=prod mix phx.server
- If all goes well, restart the service with systemd:
exit # exit # (OCI specific) go back to the ubuntu user sudo systemctl start akkoma
- Verify with
systemctl status akkoma # and journalctl -u akkoma.service -f
Extras: Onboarding Another Admin
I’m really fortunate to have a volunteer sys admin for this Akkoma instance. so I’m making sure that they have root access on the server to be able to handle any incidents/issues. Not Akkoma related, just Ubuntu/Linux stuff.
-
Create a linux user account:
sudo adduser <username>
It’ll prompt to set up a password, and a couple of other information. (Look, it even has a Room Number field! Wow in the old days sys admins used to sit in separate rooms.)
-
Make sure the user can ssh into the server. I don’t have password-based access to the server (well I can enable password-based access, but let’s say I don’t), so I’ll just copy the public key manually:
sudo -su root sudo -su <username> mkdir -p ~/.ssh echo <copied_public_key> >> ~/.ssh/authorized_keys
Now testing it out:
ssh -i <path/to/private/key> <username>@<host>
-
Grant root access to the user:
sudo -su root usermod -aG sudo dbxadmin # add to the sudo group
Optionally, if you want the user to run sudo commands without getting prompted for a password:
visudo
And add this line
<username> ALL=(ALL) NOPASSWD:ALL
to the bottom of the editor.
Monitoring
Looking into monitoring solutions for hobby projects, I bumped into the Monitoring Basics by Steve Mookie Kong. Really the article is more than I need now, but I like the idea of using third-party hosted services to monitor my instance – self-hosted monitoring solutions runs the risk of being down themselves. For now I use Better Uptime for external health pings + uptime dashboard, and Datadog for internal metrics.