A home media server is the one piece of infrastructure whose users will never file a ticket. They'll open the app on the TV, see a spinner, and either text you or quietly assume you broke it. Meanwhile the server's own dashboard, if you thought to look at it, would probably say everything is fine, because most media-server failures happen around the server rather than in it: the router forgot the port forward, the NAS mount dropped, the dynamic DNS record went stale, the disk filled with transcode temp files. Plex and Jellyfin are both good at running for months. They're bad at telling you when the path to them broke.

This guide sets up external monitoring for Plex, Jellyfin, and Emby using endpoints they already expose without authentication, picks the right kind of check for each way people actually reach their server, and adds a short heartbeat script for the failures nothing outside can see. Most of it works on a free plan.

The endpoints you can check without logging in

You don't want an uptime monitor authenticating against your media server, and you don't have to. Each server has a small unauthenticated endpoint that confirms the process is up:

ServerDefault portEndpointHealthy response
Plex Media Server32400/identity200 with a short XML document containing the server's machine identifier and version
Jellyfin8096 (HTTP), 8920 (HTTPS if enabled)/health200 with the body Healthy
Emby8096/emby/System/Info/Public200 with JSON including the server name and version

These are what the apps themselves hit when they discover a server, so they're stable across versions and reveal nothing about your library. A 200 from one of them means the server process is running, listening, and reachable from wherever the request came from. That last clause is the whole point of checking from outside.

Pick the check that matches how you reach the server

There are three common ways a media server is exposed, and each wants a different monitor.

Behind a reverse proxy with your own domain

If you run Caddy, Nginx Proxy Manager, or Traefik in front of the server with a hostname like jellyfin.example.com and a Let's Encrypt certificate, this is the easy case. Create an HTTP monitor on https://jellyfin.example.com/health (or https://plex.example.com/identity) expecting a 200. On the free plan that runs every 3 minutes from outside your network and also tracks the certificate's expiry, which matters because a proxy that fails to renew takes the whole server offline for every app at once. If you're on Pro, add a keyword check for Healthy on Jellyfin so a proxy returning a 200 error page or a captive login screen doesn't pass as up.

Because the proxy is in the path, this check also covers the proxy's own failures, most commonly a 502 when the upstream container restarted on a new address. The reverse proxy guide goes deeper on reading those codes.

A raw port forward, with or without a domain

Plenty of Plex servers are reached the way Plex's Remote Access sets them up: a router forwards a public port to 32400 on the server, and clients connect by IP. Two things complicate monitoring this directly. CronAlert's HTTP checks run on Cloudflare's network, which won't make HTTP requests to a bare IP address, so an HTTP monitor on http://203.0.113.10:32400/identity can't be created. And Plex serves a certificate for its own *.plex.direct hostname, so an HTTPS request to your hostname on 32400 fails validation and shows as down even when the server is fine.

The clean answer is a TCP port monitor on the public hostname or IP and port 32400 (or 8096 for Jellyfin). It attempts a connection on every check and alerts when the connection is refused or times out, which is exactly what a vanished port forward or a powered-off server looks like from outside. It doesn't speak HTTP, so the certificate question never arises, and it reaches bare IPs. TCP monitors are on the Pro plan. If you use a dynamic DNS hostname, monitor that hostname rather than the IP so the check follows your address when it changes, and see the DDNS section below for the failure that introduces.

VPN-only: Tailscale, WireGuard, or LAN

If the server is deliberately unreachable from the internet, no external check can see it, and you shouldn't open a port just for monitoring. Flip the direction instead. A heartbeat monitor gives you a URL; a cron job on the server hits the local health endpoint every minute and pings that URL only if it passed:

# crontab -e on the media server
* * * * * curl -fsS -m 5 http://127.0.0.1:8096/health | grep -q Healthy && curl -fsS -m 10 -X POST https://cronalert.com/api/heartbeat/<token> >/dev/null

For Plex, replace the first half with curl -fsS -m 5 http://127.0.0.1:32400/identity >/dev/null. If the server hangs, loses power, or the health endpoint stops answering, the pings stop and CronAlert opens an incident after a couple of minutes of silence. Nothing is exposed, nothing is installed beyond curl. Heartbeats are on the Pro plan.

The failures a 200 doesn't catch

Media servers have a distinctive set of "online but useless" states, and they're the ones the household actually notices. The health endpoint returns 200 through all of them:

  • The media mount is gone. The NAS rebooted, the SMB or NFS share didn't remount, and every library is suddenly empty or every title says unavailable. The server is perfectly healthy; it just has no files.
  • The disk is full. Transcode temp files, a runaway log, or a download client sharing the disk fill it, and playback fails or the database corrupts on next write.
  • Plex isn't signed in. After a reinstall or a container recreate without the claim token, the server runs but isn't associated with your account, so remote clients can't see it.
  • The port forward or DDNS record is stale. Covered by the external check, and only by the external check; every local test passes.

Extend the heartbeat script so it fails on the internal ones. Checking that the mount is present, the disk has room, and the server answers takes a few lines:

#!/usr/bin/env bash
# /usr/local/bin/media-health.sh — run from cron every minute
set -uo pipefail
HEARTBEAT="https://cronalert.com/api/heartbeat/<token>"
MEDIA=/mnt/media
fail=0

# Server answers locally (Jellyfin shown; Plex: curl -fsS http://127.0.0.1:32400/identity)
curl -fsS -m 5 http://127.0.0.1:8096/health | grep -q Healthy || { echo "server not healthy"; fail=1; }

# Media share is mounted and readable (keep a sentinel file at its root)
mountpoint -q "$MEDIA" && [ -r "$MEDIA/.mounted" ] || { echo "media mount missing"; fail=1; }

# Disk holding the server's data and transcode directory below 90%
use=$(df --output=pcent /var/lib/jellyfin | tail -1 | tr -dc '0-9')
[ "$use" -ge 90 ] && { echo "disk ${use}%"; fail=1; }

# Ping only when everything passed; silence is the alert
[ "$fail" -eq 0 ] && curl -fsS -m 10 -X POST "$HEARTBEAT" >/dev/null
exit 0

The sentinel file trick (touch /mnt/media/.mounted once, on the share itself) distinguishes "the share is mounted" from "an empty directory is sitting where the share should be," which is what a failed mount looks like to Plex and Jellyfin. Pipe the script's output to logger -t media-health in the cron line so the reason for a silent heartbeat is one journalctl away. This script is the same shape as the one in the VPS monitoring guide; the only media-specific part is the mount check.

If your library lives on a separate NAS with its own web interface, give the NAS its own monitor too: a TCP check on its SMB port 445 or an HTTP check on its admin page, so "the NAS is down" and "the media server lost the NAS" arrive as two different alerts.

Dynamic DNS deserves its own alert

Residential connections change IP, and a DDNS client on the server or router is supposed to keep media.example.duckdns.org pointed at the new one. When that client dies, everything external goes dark at once while every local check passes, and the failure is invisible until someone tries to connect from outside. If you monitor by hostname, your TCP or HTTP monitor already catches this: the record points at a stranger's IP, the connection is refused or times out, and you get an alert. That's an argument for monitoring the hostname rather than the IP even when the IP rarely changes. The DNS monitoring guide covers the resolution failures around it.

Where to send the alert

For a household server, the honest answer is somewhere you'll see it and the household won't have to. A Discord channel is the natural fit for a Plex share that already has a Discord for requests, and it's free. Push notifications to your phone work for solo operators. If the people using the server are the type to ask "is Plex down?", put up a status page (one is included on the free plan) and pin the link; it answers the question before they text you, and it shows the recovery too.

Set a maintenance window over your weekly reboot or update slot so those don't page you, and if the server is on a flaky consumer connection, set the monitor's failure threshold to 2 so a single dropped check doesn't fire. Then test it: stop the container for two minutes and make sure the down and recovered messages both arrive.

Frequently asked questions

Which URL do I monitor?

Plex: /identity on 32400. Jellyfin: /health on 8096, which returns Healthy. Emby: /emby/System/Info/Public. All answer without authentication.

Does this work on the free plan?

HTTP checks through a hostname (reverse proxy or DDNS name) do, with SSL tracking, Discord alerts, and a status page. TCP port checks, the Healthy keyword assertion, and heartbeats are Pro ($5/mo).

Why does HTTPS on port 32400 fail?

Plex's certificate is for its own plex.direct hostname, not yours. Use a TCP check on the port, plain HTTP, or a reverse proxy with your own certificate.

Can I monitor a server that's only on my VPN?

With a heartbeat: a cron job checks the local health endpoint each minute and pings CronAlert on success. Silence becomes the alert; no port is opened.

Do I need Plex Pass for any of this?

No. Nothing here uses Plex's own features; it's all external checks and a shell script.

One check outside, one script inside

An external check on the hostname people actually connect to catches the router, DDNS, proxy, and certificate failures that make a healthy server unreachable. A one-minute heartbeat from inside catches the dropped mount and the full disk that make a reachable server useless. Together they cover every way movie night breaks, and the alert reaches you before the group chat does. Create a free account, add an HTTP monitor on your server's health endpoint, and hook up Discord. Related reading: monitoring Docker and self-hosted apps, TCP port monitoring, monitoring a game server for the same problem with a different audience, and monitoring a VPS end to end.