Back to Blog
Tutorial12 min read2026-07-02

How to Deploy a Mastodon Instance

Running your own Mastodon instance means orchestrating web, streaming, and Sidekiq processes plus PostgreSQL and Redis. This guide breaks down the architecture and deploys it piece by piece.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Mastodon is the leading self-hosted, federated social network — but it's one of the more involved apps to operate. It isn't a single process; it's a web app, a streaming server, background workers (Sidekiq), PostgreSQL, Redis, and object storage for media. This guide breaks down the architecture and deploys each component.

Be warned upfront: Mastodon is resource-hungry and federation generates real ongoing traffic. This is a weekend-project-plus, not a five-minute deploy.

Mastodon's architecture

ComponentRoleScales by
Web (Puma)HTTP API + web UIReplicas
Streaming (Node)Real-time timelines over WebSocketReplicas
SidekiqBackground jobs: federation, push, mediaReplicas / queues
PostgreSQLPrimary datastoreManaged instance
RedisCache + Sidekiq queueManaged instance
Object storageMedia (avatars, attachments)S3-compatible

All three app processes run from the same Mastodon image with different commands.

Step 1: Provision data stores first

On [PandaStack](https://dashboard.pandastack.io), create:

  • A managed PostgreSQL (16.x recommended).
  • A managed Redis.
  • An S3-compatible bucket for media (object storage).

Mastodon needs a real, durable PostgreSQL — its whole social graph lives there. Use a tier with adequate storage and connections from the start; the free-tier dev database is too small for a real instance.

Step 2: Generate secrets

Mastodon needs several secrets. Generate them once using the official image:

docker run --rm tootsuite/mastodon:v4.3.0 bundle exec rake secret  # SECRET_KEY_BASE
docker run --rm tootsuite/mastodon:v4.3.0 bundle exec rake secret  # OTP_SECRET
# VAPID keys for web push:
docker run --rm tootsuite/mastodon:v4.3.0 bundle exec rake mastodon:webpush:generate_vapid_key

Mastodon 4.3+ also uses ACTIVE_RECORD_ENCRYPTION_* keys; generate them with the provided rake task.

Step 3: Build the environment file

Key environment variables (store secrets as secret env vars, never in git):

LOCAL_DOMAIN=social.example.com
WEB_DOMAIN=social.example.com

DB_HOST=<managed-postgres-host>
DB_PORT=5432
DB_NAME=mastodon
DB_USER=<user>
DB_PASS=<password>

REDIS_URL=redis://:<password>@<redis-host>:6379

SECRET_KEY_BASE=<generated>
OTP_SECRET=<generated>
VAPID_PRIVATE_KEY=<generated>
VAPID_PUBLIC_KEY=<generated>

# Object storage for media
S3_ENABLED=true
S3_BUCKET=<bucket>
S3_ENDPOINT=<endpoint>
AWS_ACCESS_KEY_ID=<key>
AWS_SECRET_ACCESS_KEY=<secret>

# SMTP for email confirmations
SMTP_SERVER=<smtp-host>
SMTP_PORT=587
SMTP_LOGIN=<user>
SMTP_PASSWORD=<password>
SMTP_FROM_ADDRESS=notifications@example.com

LOCAL_DOMAIN is permanent — it's baked into every federated identity. Choose it carefully; you cannot change it later without breaking federation.

Step 4: Initialize the database

Before the app can serve traffic, run the schema setup once (as a pre-deploy/init job using the Mastodon image):

bundle exec rails db:migrate

For a fresh install, rails db:setup (or the guided mastodon:setup) initializes the database.

Step 5: Deploy the three processes

Deploy three container apps, all from tootsuite/mastodon:v4.3.0, each with a different command:

Web:

bundle exec puma -C config/puma.rb
# expose port 3000

Streaming:

node ./streaming
# expose port 4000

Sidekiq (workers):

bundle exec sidekiq
# no inbound port

All three share the same environment variables and connect to the same PostgreSQL/Redis.

Step 6: Route the domains

Mastodon expects:

  • /api/v1/streaming to route to the streaming service (port 4000).
  • Everything else to route to the web service (port 3000).

Configure your ingress/routing so the streaming path goes to the streaming app and the rest goes to web. PandaStack's Kong ingress handles WebSocket upgrades, which the streaming server requires.

Point your DNS at the platform and add the custom domain for automatic SSL. Mastodon strictly requires HTTPS.

Step 7: Create your admin account

Once running, create and promote an admin via the Mastodon image:

bundle exec bin/tootctl accounts create alice --email alice@example.com --confirmed --role Owner

Operating realities — read before committing

  • Federation is chatty. Following active accounts pulls a steady stream of inbound activity that Sidekiq processes. Scale Sidekiq replicas if queues back up.
  • Media grows fast. Cached remote media accumulates; run tootctl media remove periodically and rely on object storage, not container disk.
  • Sidekiq queues matter. Mastodon uses multiple queues (default, push, pull, mailers, ingress). For larger instances, run dedicated Sidekiq apps per queue so a backlog in one doesn't starve others.
  • Backups are non-negotiable. Your PostgreSQL holds irreplaceable social data; use scheduled backups and verify restores.

References

  • [Mastodon installation docs](https://docs.joinmastodon.org/admin/install/)
  • [Mastodon Docker / config](https://docs.joinmastodon.org/admin/config/)
  • [Scaling Mastodon (Sidekiq queues)](https://docs.joinmastodon.org/admin/scaling/)
  • [tootctl reference](https://docs.joinmastodon.org/admin/tootctl/)

---

Mastodon rewards the effort with full ownership of your corner of the fediverse, but it genuinely needs managed PostgreSQL, Redis, object storage, and multiple always-on processes. PandaStack provisions the data stores and runs the web/streaming/Sidekiq apps together. If you want to experiment first, the free tier covers the data stores for a small test instance: [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also