A Mastodon instance is four processes pretending to be one website: the Rails web app, the Node streaming server, Sidekiq workers, and Postgres with Redis beside it, plus media storage that is usually somewhere else entirely. They fail separately. The web app serves perfectly while the streaming server is dead and nobody's timeline updates. Sidekiq falls three hours behind, so posts appear late, notifications arrive in clumps, and federation stalls, while every page loads instantly. Media storage starts answering 403 and every avatar on the instance breaks. And the nightly Postgres dump stopped in March, which you find out about when the disk dies in August.

A single check on the home page catches almost none of this. This guide puts one CronAlert monitor on each layer: web, streaming, public API, Sidekiq, media, federation surface, TLS, and backups. The HTTP checks and SSL monitoring run on the free plan; keyword assertions and the heartbeats need Pro.

Layer 1: the web process (/health)

Mastodon exposes a health route for exactly this purpose. It answers 200 when the web process is up and can talk to its database, and it is unauthenticated:

URL:              https://social.example.com/health
Method:           GET
Expected status:  200

The body is tiny and has changed across versions (an empty JSON object on recent releases), so rely on the status code rather than a keyword here. The home page is a worse target than it looks: logged-out requests redirect to the about or sign-in page, and uptime monitors follow redirects, so a monitor on / ends up measuring whichever page that is. If a reverse proxy sits in front (it almost always does), monitor it separately so an alert tells you which layer broke.

Layer 2: the streaming server

Streaming is a separate Node.js process, usually on its own port behind the proxy at /api/v1/streaming. When it dies, the web app keeps serving and nothing looks wrong until someone notices their timeline has not moved in an hour. It has its own health route, added in Mastodon 2.5, which returns the text OK:

URL:              https://social.example.com/api/v1/streaming/health
Expected status:  200
Keyword (Pro):    contains "OK"

A 502 or 404 here with layer 1 green means the streaming process is down or the proxy's /api/v1/streaming location is misrouted, which is the most common thing to break during a proxy config change. If you run several streaming processes behind a load balancer, a health check that hits one healthy node cannot vouch for the others; monitor each node's hostname if they have them.

Layer 3: the app renders real responses

The public instance endpoint returns JSON about the server, unauthenticated, through the full Rails stack:

URL:              https://social.example.com/api/v2/instance
Expected status:  200
Keyword (Pro):    contains "\"domain\":\"social.example.com\""

This proves what /health cannot: that the application answers a real API request with your configuration in it, not a cached page or a proxy's 200. It also reports registrations state, so a second monitor with not_contains on "enabled":true under registrations is a cheap guard against sign-ups being accidentally opened on an invite-only instance. On older servers use /api/v1/instance, which still works and carries "uri" instead of "domain".

Layer 4: Sidekiq is keeping up (the heartbeat)

This is the silent failure, and the one that most affects how the instance feels. Sidekiq handles delivery of your users' posts to other servers (push), receiving posts from them (ingress), fetching remote content (pull), notifications and most everything else (default), emails (mailers), and the periodic jobs (scheduler). When a worker process dies or the queues outgrow the workers, posts land late, notifications arrive in bursts, and remote timelines go quiet. The web app is fine throughout.

There is no public endpoint for queue health; /sidekiq requires an admin session. Measure latency on the host and ping a heartbeat only when every queue is current:

#!/bin/bash
# Ping only if the oldest waiting job in every important queue is under 300s old
cd /home/mastodon/live
LAT=$(RAILS_ENV=production bin/rails runner '
  require "sidekiq/api"
  puts %w[default push pull ingress mailers].map { |q| Sidekiq::Queue.new(q).latency.to_i }.max
')
[ -n "$LAT" ] && [ "$LAT" -lt 300 ] \
  && curl -fsS --max-time 10 --retry 3 https://cronalert.com/api/heartbeat/<sidekiq-token>

Run it every 5 minutes, with the heartbeat's expected interval at 5 minutes and a grace of 5. Latency (the age of the oldest waiting job) is the right measure rather than queue size: a large instance can have thousands of jobs queued and moving fine, while a small one with twelve stuck jobs is broken. Set the threshold from your instance's normal behavior; 300 seconds is a reasonable start. For a lighter check without Rails, read the queue lengths straight from Redis with redis-cli LLEN queue:default, which is a size rather than a latency but costs nothing to run. Monitoring background workers covers the general pattern and the trap of pinging from a worker that only processes one queue.

The scheduler queue is special: it runs Mastodon's periodic maintenance (feed regeneration, media cleanup, retention). A heartbeat on it is awkward from the outside, so rely on the latency script above including it if you have ever seen it stall, or on the backup heartbeat below, which fails for the same host-level reasons.

Layer 5: media storage

Avatars, attachments, and custom emoji are served from object storage (S3, R2, MinIO, Wasabi) or a local public/system directory, often on a different hostname such as files.social.example.com, often through a CDN. All of that fails on its own schedule: a bucket policy edit, a CDN certificate that expired, a full disk. Pick one media object that will not be deleted, for example the instance's own thumbnail or a custom emoji, and monitor it directly:

URL:              https://files.social.example.com/custom_emojis/images/000/000/001/original/blobcat.png
Expected status:  200

Enable SSL monitoring on the media hostname too; it typically carries a different certificate from the main site, renewed by a different mechanism. If you self-host the bucket, monitoring MinIO adds the quorum and policy checks underneath this one, and monitoring CDN assets covers the case where the CDN keeps serving a cached object after the origin has already broken.

Layer 6: the federation surface

Other servers find your instance through two unauthenticated endpoints before they ever fetch a post. Monitor both; if either breaks, mentions and follows from remote servers silently fail while everything looks fine locally:

URL:              https://social.example.com/.well-known/webfinger?resource=acct:[email protected]
Expected status:  200
Keyword (Pro):    contains "https://social.example.com/users/admin"

URL:              https://social.example.com/nodeinfo/2.0
Expected status:  200
Keyword (Pro):    contains "\"software\":{\"name\":\"mastodon\""

WebFinger is the one that breaks when a proxy rewrite rule or a WEB_DOMAIN/LOCAL_DOMAIN split is misconfigured; it returns a 200 from the wrong host or a 404, and remote lookups of every account on your server fail. Nodeinfo is how crawlers, relays, and other instances read your software version and user counts. The DNS guide covers the related failure where the domain itself stops resolving for some remote servers.

Layer 7: the certificate

Remote instances verify TLS strictly, and an expired certificate on your main domain does not just lock out your users; every other server's deliveries to you fail and are retried with backoff for days, and some of that content never arrives. Enable SSL monitoring on the main hostname and the media hostname. It is on every plan and alerts days ahead; SSL certificate monitoring covers thresholds and monitoring the renewal job catches the certbot timer that stopped.

Layer 8: the backups

Mastodon's state is a Postgres database, a Redis dump that is nice to have, the .env.production file with the secret keys that make every stored password and session valid, and the media directory or bucket. Back up the first and the third nightly, end the job with a heartbeat ping that only fires on success, and set a 24-hour interval with a 2-hour grace:

0 3 * * * pg_dump -Fc mastodon_production > /backups/mastodon-$(date +\%F).dump \
  && cp /home/mastodon/live/.env.production /backups/env-$(date +\%F) \
  && curl -fsS --max-time 10 --retry 3 https://cronalert.com/api/heartbeat/<backup-token>

Media is usually too large to dump nightly; if it lives in a bucket, rely on the bucket's versioning or a mirror job with its own heartbeat. Without .env.production, a restored database is a server full of accounts nobody can log in to. Monitoring scheduled backups goes deeper on verifying the dump, not only the exit code.

Reading the alerts

AlertWhat brokeFirst thing to check
/health down, everything downHost, proxy, or Postgressystemctl status mastodon-web nginx postgresql
/health green, streaming health 502Streaming process or proxy routesystemctl status mastodon-streaming; the proxy's streaming location
Instance API missing the keywordProxy or CDN answering for the app, or wrong vhostWhat the body actually is
Sidekiq heartbeat overdue, web greenWorkers dead or behind, or Redis/sidekiq as admin; systemctl status mastodon-sidekiq; Redis memory
Media object 403 or 404Bucket policy, CDN, or diskBucket policy; CDN origin status
WebFinger or nodeinfo failingProxy rewrite or domain configLOCAL_DOMAIN/WEB_DOMAIN; the proxy's .well-known rules
SSL expiringRenewal missed, or renewed and not reloadedCertificate dates; nginx -s reload
Backup heartbeat overdueDump did not run or failedCron log; free space on the backup target

Alert channels in CronAlert are team-wide. If the instance's admins want push alerts and the moderation team wants a Discord notice, put the monitors in their own team; alert routing with teams explains how.

Frequently asked questions

Does this apply to Pleroma, Akkoma, Misskey, or GoToSocial?

The shape does. WebFinger and nodeinfo are standard across the Fediverse, every server has some health or instance route, and every one has a background job system whose latency is the silent failure. The specific paths differ; /api/v1/instance is implemented by most for client compatibility.

Will a 1-minute check on /health show up in my logs?

In the proxy's access log, yes; set a User-Agent header on the monitor to filter it. It creates no session and no database rows.

My instance is small and runs on one box. Is this overkill?

Start with three: /health, the streaming health route, and the backup heartbeat. Those catch the three failures small instances actually have. Add Sidekiq latency when the instance grows past a few hundred active users, which is when queues start to matter.

Can I monitor from inside the Fediverse instead?

Services that watch instance uptime across the network exist, but they see only the public face. Nothing outside your host can see Sidekiq latency or the backup, which is why those two are heartbeats you send.

Which plan do I need?

The /health, streaming, instance, media, WebFinger, and nodeinfo checks are HTTP monitors with an expected status, on every plan at 3-minute intervals on Free and 1-minute on Pro, and SSL expiry monitoring is free. Keyword assertions and the Sidekiq and backup heartbeats are Pro ($5/mo, or $4/mo billed annually).

Monitor the processes, not the page

A Mastodon home page that loads is the least informative fact about a Mastodon server. Whether streaming is alive, whether Sidekiq is current, whether media serves, whether remote servers can find you, and whether last night's dump exists are the facts that decide if your users have a good day, and each is one small monitor. Create a free account, add the health and streaming checks, and upgrade to Pro for the Sidekiq heartbeat before the next quiet queue. Related reading: monitoring Docker and self-hosted apps if the instance runs in containers, monitoring the server underneath, monitoring Cloudflare Tunnel if it is published that way, monitoring Rails applications, and choosing between HTTP, TCP, and heartbeat monitors.