How to monitor Jellyfin from outside your network
Updated Aug 19, 2026
Jellyfin is one of the few self hosted media servers that gives you a proper health endpoint instead of making you guess. That is worth using well, and worth understanding the limits of.
What breaks quietly
Jellyfin logs an error and carries on. It does not phone you, and any notification plugin you have wired up originates inside the same container, on the same host, behind the same router that is often the actual problem.
- A library scan or transcode fills the disk, SQLite starts failing writes, and the server degrades into errors while the process stays up.
- A container update pulls a breaking release and the server sits in a restart loop overnight.
- The media share goes away, so Jellyfin runs perfectly and every title 404s.
- Hardware transcoding stops after a driver update, so remote clients that need a transcode fail while direct play on the TV is fine.
- The reverse proxy certificate expires and every client outside the LAN gets a TLS error.
Only the last two are visible from the internet, and none are visible to something running on the same box. That asymmetry is the whole argument for an outside prober.
The endpoints worth checking
Jellyfin serves HTTP on port 8096 by default. HTTPS on 8920 exists but is off unless you enable it, and most people terminate TLS at a reverse proxy instead.
GET /health is a genuine liveness endpoint. Jellyfin documents it as verifying HTTP and database connectivity, and it returns 200 when both work. It needs no authentication. One documented caveat: it does not behave correctly during startup or database migrations, so give it a confirmation threshold rather than paging on the first failure after an update.
For a stricter assertion, GET /System/Info/Public also answers unauthenticated, returning JSON including ServerName, Version, ProductName, Id, and StartupWizardCompleted. A JSON check can pin Version, or confirm StartupWizardCompleted is true, which catches the ugly case where a volume did not mount and Jellyfin came back offering the first run wizard to the internet.
| Endpoint | Auth | Good for |
|---|---|---|
| /health | None | HTTP plus database liveness |
| /System/Info/Public | None | JSON assertions on version and setup state |
| /System/Info | API key | Full server detail, admin only |
| TCP 8096 | None | Port and process reachability |
The authenticated /System/Info needs an API key in an Authorization header using the MediaBrowser scheme, in the form MediaBrowser Token="yourkey". Create the key from the Dashboard under Advanced, then API Keys. Undownable stores custom request headers encrypted. Older bare token headers such as X-Emby-Token still work on many builds but are the legacy path.
Exposed through a proxy or tunnel
If Jellyfin is published at a hostname your users type, check that hostname. Checking the LAN IP tells you nothing about the path everyone else takes.
curl -fsS -o /dev/null -w '%{http_code}\n' https://jellyfin.example.com/health
# 200
Pair it with an SSL certificate check on the same hostname. Expiry is the most common way a working Jellyfin becomes unreachable, and escalating warnings turn that into a calendar reminder instead of an incident.
One trap: if your reverse proxy returns a friendly maintenance page on upstream errors, a plain HTTP check sees 200 and stays green while Jellyfin is dead. That is where the JSON check on /System/Info/Public earns its keep, because a maintenance page has no ServerName field.
Private installs: push a heartbeat instead
Keeping Jellyfin behind a VPN does not mean giving up external monitoring. Flip the direction and have the host report in, so silence becomes the alert.
*/2 * * * * curl -fsS -m 10 https://undownable.com/ping/your-monitor-id > /dev/null
Better, chain it behind the local health probe so it reflects Jellyfin rather than the machine:
*/2 * * * * curl -fsS -m 10 http://127.0.0.1:8096/health > /dev/null \
&& curl -fsS -m 10 https://undownable.com/ping/your-monitor-id > /dev/null
When /health stops returning 200 the first curl exits non zero, the second never runs, and the heartbeat goes quiet. Expect a ping roughly every two minutes with grace for one missed run, and you get an incident within about five minutes of a failure, from a service that is not in your house.
Heartbeats accept GET or POST, so this works equally well from a systemd timer or a sidecar container.
Alerts that outlive the outage
A notification plugin inside Jellyfin cannot tell you Jellyfin stopped. Route alerting through something outside the failure domain:
- ntfy, self hosted or on ntfy.sh. Generate an access token starting with tk_ and it is sent as a bearer token when publishing. Do not host that server on the machine you are monitoring.
- Telegram, which needs nothing from your infrastructure and arrives on a phone that is not on your LAN.
- Email, the fallback that works when everything else is having an incident of its own.
Turn on flap suppression if your container restarts on a schedule, and set a maintenance window around update night.
Does Jellyfin really have a /health endpoint?
Yes. GET /health is documented by Jellyfin as verifying HTTP and database connectivity, requires no authentication, and returns 200 when healthy. The docs warn it misbehaves during startup and database migrations, so allow a confirmation threshold.
Should I check /health or /System/Info/Public?
Check /health for liveness and /System/Info/Public when you want a JSON assertion a reverse proxy error page cannot fake. Many people run both: one cheap liveness check and one stricter identity check.
Do I need an API key?
Not for /health or /System/Info/Public, which both answer unauthenticated. You need one only for admin endpoints such as /System/Info, sent as Authorization with the MediaBrowser Token form.
Is it safe to expose /health to the internet?
It reveals only that the server is alive. /System/Info/Public is the more interesting one, since it exposes server name and version to anyone. If that bothers you, keep Jellyfin private and use the gated heartbeat.
Related reading
Monitoring that watches from the outside
Free plan with 10 monitors, plus a 14-day Pro trial. No credit card required.
Start free