← All articles
COMMUNICATION Self-Hosting Mastodon: Run Your Own Fediverse Social... 2026-02-09 · mastodon · fediverse · activitypub

Self-Hosting Mastodon: Run Your Own Fediverse Social Media Instance

Communication 2026-02-09 mastodon fediverse activitypub social-media twitter-alternative

Running your own social media server sounds like an absurd amount of work for a marginal benefit. And honestly, for most people it is. But there are legitimate reasons to self-host a Mastodon instance: you want to own your online identity, you need a branded presence for a community or organization, or you simply want to participate in the fediverse on your own terms. Before you commit, you should understand what you're signing up for — this guide covers both the how and the honest operational reality.

Mastodon is an open source, decentralized social media platform that implements the ActivityPub protocol. It looks and feels similar to Twitter, with posts (called "toots" originally, now just "posts"), followers, timelines, and hashtags. The key difference is federation: your Mastodon instance communicates with thousands of other Mastodon instances, plus Lemmy, Pixelfed, PeerTube, and anything else that speaks ActivityPub. Your account on your server can interact with users on any federated server.

How Federation Works

The fediverse operates like email: your Mastodon instance is like your email server. You have an address (@[email protected]), and you can send messages to anyone on any other instance, just like [email protected] can email [email protected].

When you follow someone on a remote instance:

  1. Your instance sends a follow request to their instance via ActivityPub
  2. Their instance accepts and starts forwarding their posts to your instance
  3. Your instance stores copies of their posts for your timeline
  4. Replies, boosts, and favorites are all federated back and forth

This means your instance stores not only your posts but also cached copies of everything your users follow. This has significant storage implications (more on that later).

The timeline types

Mastodon federation architecture Your Instance Web Puma :3000 Streaming Node :4000 Sidekiq Background PostgreSQL Data store Redis Cache & queues ActivityPub Federation protocol Remote Instances mastodon.social pixelfed.org lemmy.world peertube.tv Posts, follows, boosts, and replies federate bidirectionally via ActivityPub

Mastodon vs. GoToSocial vs. Akkoma vs. Misskey

Feature Mastodon GoToSocial Akkoma Misskey
Protocol ActivityPub ActivityPub ActivityPub ActivityPub
Language Ruby on Rails Go Elixir Node.js
RAM (idle) 1-2 GB 100-250 MB 500 MB-1 GB 500 MB-1 GB
Storage growth High Low-Moderate Moderate Moderate
Web UI Yes (Glitch-soc fork popular) No (use third-party clients) Yes (Pleroma-based) Yes (unique UI)
Mobile apps Many (Ivory, Ice Cubes, Tusky, etc.) Mastodon-compatible apps Mastodon-compatible apps Misskey-specific apps
Character limit 500 (configurable) 5,000 (default) Configurable 3,000 (default)
Custom emoji Yes Yes Yes Yes
Reactions Favorites only Favorites only Emoji reactions Emoji reactions
Quote posts No (controversial) No Yes Yes
Admin UI Web-based Limited (CLI + settings) Web-based Web-based
Moderation tools Excellent Basic Good Good
Single-user friendly Not ideal Excellent Good Not ideal
Maturity Most mature Newer (active dev) Mature (Pleroma fork) Mature
Full-text search Elasticsearch/optional Built-in Built-in Built-in

Which to choose

Mastodon is the default choice if you want the widest ecosystem of mobile apps, the most robust moderation tools, and the strongest community. It's also the heaviest.

GoToSocial is the best choice for single-user instances or small groups. It uses a fraction of the resources and has no web UI — you interact entirely through Mastodon-compatible apps like Ivory or Tusky. If you just want your own fediverse identity without running a full social network, GoToSocial is the practical choice.

Akkoma (a fork of Pleroma) is a good middle ground — lighter than Mastodon, more features than GoToSocial, with quote posts and emoji reactions.

Misskey offers a different UI paradigm with features Mastodon deliberately omits (quote posts, reactions, a drive for file management). It has a strong following in Japan.

Self-Hosting Mastodon: Setup

Server requirements

Mastodon is resource-intensive compared to most self-hosted applications:

Docker Compose setup

Mastodon requires several services: the web frontend, the streaming server, Sidekiq background workers, PostgreSQL, Redis, and optionally Elasticsearch.

services:
  mastodon-web:
    container_name: mastodon-web
    image: ghcr.io/mastodon/mastodon:latest
    command: bundle exec puma -C config/puma.rb
    ports:
      - "3000:3000"
    environment: &mastodon-env
      LOCAL_DOMAIN: "social.yourdomain.com"
      REDIS_HOST: "redis"
      DB_HOST: "postgres"
      DB_USER: "mastodon"
      DB_PASS: "mastodon"
      DB_NAME: "mastodon"
      SECRET_KEY_BASE: "generate-with-rake-secret"
      OTP_SECRET: "generate-with-rake-secret"
      VAPID_PRIVATE_KEY: "generate-with-rake-task"
      VAPID_PUBLIC_KEY: "generate-with-rake-task"
      SMTP_SERVER: "smtp.yourdomain.com"
      SMTP_PORT: "587"
      SMTP_LOGIN: "[email protected]"
      SMTP_PASSWORD: "your-smtp-password"
      SMTP_FROM_ADDRESS: "[email protected]"
      S3_ENABLED: "false"
    volumes:
      - mastodon-public:/opt/mastodon/public/system
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: always

  mastodon-streaming:
    container_name: mastodon-streaming
    image: ghcr.io/mastodon/mastodon:latest
    command: node ./streaming/index.js
    ports:
      - "4000:4000"
    environment:
      <<: *mastodon-env
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: always

  mastodon-sidekiq:
    container_name: mastodon-sidekiq
    image: ghcr.io/mastodon/mastodon:latest
    command: bundle exec sidekiq
    environment:
      <<: *mastodon-env
    volumes:
      - mastodon-public:/opt/mastodon/public/system
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: always

  postgres:
    container_name: mastodon-db
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: "mastodon"
      POSTGRES_USER: "mastodon"
      POSTGRES_PASSWORD: "mastodon"
    volumes:
      - mastodon-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mastodon"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  redis:
    container_name: mastodon-redis
    image: redis:7-alpine
    volumes:
      - mastodon-redis:/data
    restart: always

volumes:
  mastodon-public:
  mastodon-db:
  mastodon-redis:

Generating secrets

Before starting, generate the required secrets:

# Generate SECRET_KEY_BASE and OTP_SECRET
docker run --rm ghcr.io/mastodon/mastodon:latest bundle exec rake secret
# Run this twice — once for SECRET_KEY_BASE, once for OTP_SECRET

# Generate VAPID keys
docker run --rm ghcr.io/mastodon/mastodon:latest bundle exec rake mastodon:webpush:generate_vapid_key

Database setup

Initialize the database before first run:

docker compose run --rm mastodon-web bundle exec rake db:setup

Creating your admin account

docker compose run --rm mastodon-web bin/tootctl accounts create \
  yourusername \
  --email [email protected] \
  --confirmed \
  --role Owner

Starting the service

docker compose up -d

Access Mastodon at http://your-server:3000. You'll need a reverse proxy for production use.

Reverse Proxy Setup

Mastodon requires a reverse proxy that handles both the web interface and the streaming API:

Caddy:

social.yourdomain.com {
    handle /api/v1/streaming* {
        reverse_proxy localhost:4000
    }

    handle {
        reverse_proxy localhost:3000
    }
}

Nginx:

server {
    server_name social.yourdomain.com;

    client_max_body_size 80M;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /api/v1/streaming {
        proxy_pass http://localhost:4000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;
        proxy_redirect off;
    }
}

Instance Administration

Site settings

In the admin panel (Preferences > Administration > Server Settings):

For single-user instances, set registration to closed after creating your account.

Moderation

Mastodon's moderation tools are the most mature in the fediverse:

Relay setup

Relays broadcast posts between instances, filling your federated timeline with content. Without relays or active following, a new instance's federated timeline will be empty.

To add a relay:

  1. Go to Administration > Relays
  2. Add a relay URL (e.g., https://relay.fedi.buzz/instance/yourdomain.com)
  3. Wait for the relay to accept your instance

Popular relays include relay.fedi.buzz (topic-based) and relay.intahnet.co.uk. Be selective — relays increase both network traffic and storage usage.

Storage Management

Storage is the biggest ongoing challenge of running a Mastodon instance. Every remote post your users see, every avatar, every image preview gets cached on your server.

Media cache cleanup

Run periodic cleanups to remove cached remote media:

# Remove remote media cache older than 7 days
docker compose exec mastodon-web bin/tootctl media remove --days=7

# Remove preview cards older than 14 days
docker compose exec mastodon-web bin/tootctl preview_cards remove --days=14

# Remove orphaned media
docker compose exec mastodon-web bin/tootctl media remove-orphans

Automating cleanup with cron

Add a cron job to run cleanup regularly:

# Run media cleanup daily at 3 AM
0 3 * * * cd /path/to/mastodon && docker compose exec -T mastodon-web bin/tootctl media remove --days=7
0 4 * * * cd /path/to/mastodon && docker compose exec -T mastodon-web bin/tootctl preview_cards remove --days=14

S3 object storage

For larger instances, offloading media to S3-compatible storage (AWS S3, MinIO, Cloudflare R2, Wasabi) is strongly recommended:

environment:
  S3_ENABLED: "true"
  S3_BUCKET: "mastodon-media"
  AWS_ACCESS_KEY_ID: "your-key"
  AWS_SECRET_ACCESS_KEY: "your-secret"
  S3_REGION: "us-east-1"
  S3_HOSTNAME: "s3.amazonaws.com"
  S3_PROTOCOL: "https"

Object storage keeps your local disk manageable and makes media serving faster through CDN integration.

The Honest Operational Reality

Running a Mastodon instance is more like running a small web service than installing a typical self-hosted application. Here's what ongoing operation actually looks like:

What goes well

What requires ongoing attention

What catches people off guard

Backup Strategy

# Database backup
docker compose exec -T mastodon-db pg_dump -U mastodon mastodon > mastodon-backup-$(date +%Y%m%d).sql

# Volume backup (local media)
docker run --rm -v mastodon-public:/data -v $(pwd):/backup alpine tar czf /backup/mastodon-media-$(date +%Y%m%d).tar.gz /data

For the database, daily backups are recommended. For media, if you're using S3, the object storage handles durability — just back up the database.

Honest Trade-offs

Mastodon is the right choice if you:

Consider GoToSocial instead if you:

Consider Akkoma instead if you:

Consider Misskey instead if you:

The bottom line: Self-hosting Mastodon is a commitment. It's not like spinning up a Nextcloud instance and forgetting about it — federation adds complexity, storage grows, and the system needs regular attention. For organizations and communities that want their own identity on the fediverse, it's the gold standard. For individuals who just want a fediverse account, joining an existing instance or running GoToSocial is usually more practical. Know what you're getting into before you start, and you'll be fine.