What runs on the machine
| Component | Job | What sizes it |
|---|---|---|
| Puma (web) | Short HTTP requests: the web app and the REST API | WEB_CONCURRENCY processes (default 2) x MAX_THREADS (default 5) |
| Sidekiq | Delivery to other servers, incoming activities, mail, imports, scheduled jobs | Threads per process (-c), one database connection each |
| Streaming API (Node.js) | Long-lived WebSocket and EventSource connections for live timelines | Concurrent clients; one process per port |
| PostgreSQL | Accounts, posts, follows: everything that matters | Connections, disk operations, RAM for cache |
| Redis | Home and list feeds, Sidekiq queues, cache | Memory; persistence to dump.rdb |
The project's README lists PostgreSQL 14 or newer, Redis 7.0 or newer and Node.js 24 or newer; the install guide targets Ubuntu 24.04 or Debian 13 with root access. Threads in a Puma process share its memory, while each extra process allocates its own, so more threads run out of CPU first and more processes run out of RAM first. The docs add that, for throughput, processes beat threads.
Counting database connections
PostgreSQL connections are the first hard limit. The scaling guide says the number open is roughly the sum of all threads in Puma, Sidekiq and the streaming server, and PostgreSQL's default ceiling is 100. With the shipped defaults the arithmetic is short:
| Process | Default | Connections |
|---|---|---|
| Puma | 2 processes x 5 threads | 10 |
| Sidekiq (shipped systemd unit) | sidekiq -c 25, DB_POOL=25 | 25 |
| Streaming | One process, up to its DB_POOL | a pool per process |
Raise Sidekiq threads and DB_POOL must rise with them, since every thread needs its own connection. Add streaming processes and each brings its own pool. When the total nears 100, the guide's answer is PgBouncer in transaction pooling mode with PREPARED_STATEMENTS=false in .env.production; migrations still run against port 5432 directly. For scale, the guide puts a community of about 50,000 accounts and 10,000 to 20,000 monthly actives at 800 to 1,200 concurrent streaming connections on average.
Splitting Sidekiq queues
Sidekiq works through seven queues in order of importance to local users: default, push (delivery to other servers), ingress (incoming remote activity), mailers, pull (imports, backups, thread resolution), scheduler and fasp. The docs describe a process checking its queues in that order, so an overfilled queue near the top delays everything listed after it, mail included. The fix is several Sidekiq processes, each a systemd unit bound to its own queues:
bundle exec sidekiq -c 15
bundle exec sidekiq -q default
Run the scheduler queue in exactly one Sidekiq process. The docs are explicit: never more than one at a time. A separate Redis for Sidekiq (SIDEKIQ_REDIS_URL) and one for cache (CACHE_REDIS_URL) are further options when Redis becomes the bottleneck.
Media storage growth
By default uploads land in public/system under the install directory. That directory holds your users' media and also cached copies of media from every remote account your users see, which is why disk use climbs with federation rather than with local sign-ups. The docs call local storage serviceable for small servers and object storage more scalable.
- Content retention. Under Administration > Content retention, set the media cache retention period; the cleanup runs from the job scheduler with no cron needed.
- tootctl.
tootctl media usagereports disk consumed;tootctl media removedeletes cached remote attachments older than--days(default 7), and--prune-profilesdoes the same for remote avatars and headers. - Object storage. Set
S3_ENABLED=truewith an S3-compatible bucket; ACL support is recommended, because it lets Mastodon hide a suspended user's media at once. Changing the media domain later means adding the new one to the Content-Security-Policy at least a week ahead, because service workers cache it that long.
RAILS_ENV=production bin/tootctl media usage
RAILS_ENV=production bin/tootctl media remove --days 7 --dry-run
Outbound mail
Mastodon sends confirmation links and notifications by email, and the docs say running your own SMTP server is much harder to do reliably than using a delivery provider. Point SMTP_SERVER, SMTP_LOGIN, SMTP_PASSWORD and SMTP_FROM_ADDRESS at the relay. Because SMTP_PORT falls back to 25 when unset, override it with the relay's submission port, usually 587 with StartTLS. Since 4.4 a separate BULK_SMTP_* set can carry announcement mail when the provider wants bulk traffic split off.
VPS or a whole machine
A single-user or invite-only instance with modest federation runs on a VPS, and the docs start there. The signs of outgrowing one are the ones the processes above produce: Sidekiq threads busy all day as follows grow, PostgreSQL wanting more disk operations than a hypervisor cap allows, and media egress to other servers that a monthly transfer allowance would meter.
A Linux dedicated server answers those with hardware nobody else uses: every core for Puma and Sidekiq, all the memory for PostgreSQL's cache and Redis, and an unmetered port with no allowance and no overage line, 1 Gbps on most lines. Put PostgreSQL and public/system on NVMe drives, or take an enterprise two-socket machine with hardware RAID and room for memory. Rack servers and blades carry an out-of-band controller with a browser console, which matters on the night an upgrade's migration leaves the box unbootable.
Ubuntu and Debian install in one click. A racked machine is live about 30 minutes after the order is approved; a build to order takes 4 to 24 hours. Pick the city nearest your community: New York, Miami, San Francisco, Amsterdam or Bucharest, all on our own network, AS19624. One IPv4 is included and IPv6 is free at checkout.
Installing it
- Decide the domain first.
LOCAL_DOMAINis the instance's identity on the network and cannot be safely changed later;WEB_DOMAINlets handles read@you@example.comwhile the app runs on a subdomain. - Secure the box per the docs: key-only SSH, fail2ban, and a firewall that admits 22, 80 and 443.
- Add the NodeSource and PostgreSQL repositories, install the system packages,
redis-server,nginxandcertbot, and create amastodonuser. Optionally tune PostgreSQL with pgTune, choosing the web application profile. - As
mastodon, clone the latest stable tag, install Ruby with rbenv, runbundle installandyarn install, thenRAILS_ENV=production bin/rails mastodon:setup, which writes.env.production, precompiles assets and creates the schema. - Issue a certificate with
certbot certonly --nginx, copydist/nginx.conf, then copy the systemd units and enable them. - Create the owner account with
tootctl accounts createand approve it; browser registration is off by default.
cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
Backups and monitoring
Back up in the docs' order of importance: the PostgreSQL database (pg_dump), .env.production, uploaded files, then Redis's /var/lib/redis/dump.rdb. Losing PostgreSQL loses the server; losing the secrets logs everyone out and breaks two-factor; losing Redis costs only queued jobs, since feeds rebuild with tootctl. Keep copies off the machine. For health checks, /health on Puma and /api/v1/streaming/health on the streaming server return 200 and OK; PgHero in the admin panel shows live connection use.
Questions
What hardware does Mastodon need?
The official documentation publishes no RAM or core minimum. It starts from a VPS and sizes by configuration: Puma processes and threads, Sidekiq threads (25 in the shipped unit), PostgreSQL connections (100 by default) and disk for media. Add memory as you add processes, and disk as federation fills the media cache.
Which operating system does the install guide use?
Ubuntu 24.04 or Debian 13, with root access. It installs Node.js from NodeSource and PostgreSQL from the PostgreSQL project's own repository, then Ruby with rbenv under a dedicated mastodon user.
Do I have to use object storage?
No. Local storage in public/system is the default and the docs call it serviceable for small servers. Object storage is the scalable option and removes media from your backup job, at the cost of an outside bill for storage and requests.
Can the server deliver its own email?
It can be configured to, but the docs advise against it: a third-party SMTP provider is far easier to run reliably. Note that an unset SMTP_PORT means 25; give it the provider's submission port instead.
How many Sidekiq processes should I run?
As many as keep queues short. Watch the Sidekiq dashboard; when push or ingress backs up behind default, start a separate process for those queues. Only one process may run the scheduler queue, and each thread needs a database connection.
Can I change the domain later?
Not safely. LOCAL_DOMAIN identifies your accounts across the network, and changing it makes remote servers treat them as new accounts. Choose it, and WEB_DOMAIN if you want one, before the first start.