Skip to content

Mastodon

Decentralized Social Networking

Mastodon is a free, open-source social network server based on ActivityPub. It's part of the fediverse - a collection of federated social networks that can communicate with each other, giving users freedom, control, and choice over their social media experience.

Why Choose Mastodon?

  • Federated - decentralized social network, you own your data
  • Privacy-focused - no tracking or surveillance advertising
  • Community-driven - transparent moderation with custom emojis
  • Open source - AGPL-3.0 licensed with rich API

Install

Infrastructure as Code

This Mastodon instance is deployed using OpenTofu with custom Incus images. The infrastructure configuration manages container provisioning and persistent storage volumes.

Custom Mastodon Image

The Mastodon Incus image is built and maintained at forgejo.benoit.jp.net/Benoit/Laminar.

Infrastructure Configuration

The OpenTofu configuration provisions:

  • Incus instance running custom Mastodon image
  • Two persistent storage volumes:
    • /home/mastodon/live/public/system - Media files and attachments
    • /home/mastodon/backup - Backup storage
OpenTofu Configuration
resource "incus_storage_volume" "mastodon_home_mastodon_live_public_system" {
  name = "mastodon_home_mastodon_live_public_system"
  pool = incus_storage_pool.default.name
  config = {
    "initial.gid" = "1001"
    "initial.uid" = "1001"
    "initial.mode" = "755"
  }
}

resource "incus_storage_volume" "mastodon_home_mastodon_backup" {
  name = "mastodon_home_mastodon_backup"
  pool = incus_storage_pool.default.name
  config = {
    "initial.gid" = "1001"
    "initial.uid" = "1001"
    "initial.mode" = "755"
  }
}

resource "incus_instance" "mastodon" {
  name  = "mastodon"
  image = "laminar.incus:mastodon-4.5.0-3benoitjpnet"

  device {
    name = "home_mastodon_live_public_system"
    type = "disk"
    properties = {
      path   = "/home/mastodon/live/public/system"
      source = incus_storage_volume.mastodon_home_mastodon_live_public_system.name
      pool   = incus_storage_pool.default.name
    }
  }

  device {
    name = "home_mastodon_backup"
    type = "disk"
    properties = {
      path   = "/home/mastodon/backup"
      source = incus_storage_volume.mastodon_home_mastodon_backup.name
      pool   = incus_storage_pool.default.name
    }
  }
}

Deploy Infrastructure

Apply OpenTofu configuration
tofu apply

Initial Configuration

After provisioning, follow the official Mastodon documentation for generating configuration.

TLS Configuration

The SSL certificate generation steps can be skipped. Instead, configure nginx to listen on HTTP only, as this instance runs behind a reverse proxy that handles TLS termination.

Upgrade

Incus Image Upgrade

When upgrading to a newer Incus image version, follow these steps to migrate your data.

Backup current instance data before upgrading:

Enter Incus container
incus shell mastodon

Copy configuration file

cp /home/mastodon/live/.env.production /home/mastodon/backup/

Dump PostgreSQL database

sudo -u postgres pg_dump -d mastodon_production -F c | \
    tee /home/mastodon/backup/mastodon_production.sql > /dev/null

Backup nginx configuration

cp /etc/nginx/sites-available/mastodon \
    /home/mastodon/backup/nginx.mastodon

Deploy to new Incus image and restore data:

Update OpenTofu configuration

Edit your OpenTofu configuration file to update the image version:

resource "incus_instance" "mastodon" {
  name  = "mastodon"
  image = "laminar.incus:mastodon-NEW-VERSION"  # Update this line
  # ...
}

Apply infrastructure changes

Provision new Incus container
tofu apply
Enter Incus container
incus shell mastodon

Setup and restore database

Create fresh database
sudo -u postgres createdb mastodon_production
Restore database dump
cat /home/mastodon/backup/mastodon_production.sql | \
    sudo -u postgres pg_restore -d mastodon_production -F c

Configure nginx

Restore nginx configuration
cp /home/mastodon/backup/nginx.mastodon /etc/nginx/sites-available/mastodon
ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
rm /etc/nginx/sites-enabled/default
Set directory permissions
chmod o+x /home/mastodon
Restart nginx
systemctl restart nginx
Switch to mastodon user
sudo -u mastodon -i

Copy environment configuration

Restore configuration file
cp backup/.env.production live/
cd live

Build frontend assets

Precompile assets
bundle exec rails assets:precompile

Apply database schema changes

Run migrations
bundle exec rails db:migrate

Regenerate user timelines

Rebuild feeds
bin/tootctl feeds build
Exit to root user
exit

Enable systemd services

Install and start services
cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming

Upgrade Complete

Your Mastodon instance is now running on the new Incus image!

Maintenance

Regular Cleanup

Run these commands periodically to manage disk space and performance.

Switch to mastodon user
sudo -u mastodon -i
cd live

Remove old media

Remove cached media older than 7 days
./bin/tootctl media remove --days=7 --concurrency=50

Remove orphaned files

Clean up unreferenced files from storage
./bin/tootctl media remove-orphans

Remove profile media

Remove cached avatars and headers
./bin/tootctl media remove --prune-profiles --concurrency=50

Remove old preview cards

Remove cached link previews older than 7 days
./bin/tootctl preview_cards remove --days=7 --concurrency=50

Cull inactive accounts

Remove accounts with no local interactions
./bin/tootctl accounts cull --concurrency=50

Prune unreachable accounts

Remove accounts from unreachable instances
./bin/tootctl accounts prune --concurrency=50

Remove old posts

Remove remote posts older than 180 days
./bin/tootctl statuses remove --days=180

Clear cache

Clear Rails cache and temporary files
./bin/tootctl cache clear