Nextcloud and Immich are the two self-hosted applications most likely to fail without anyone noticing for days. Both work through clients that sync in the background: the Nextcloud desktop client turns its icon grey and keeps waiting, the Immich app stops backing up photos and says so only in a screen nobody opens. The server, meanwhile, might be perfectly fine and simply unreachable from outside, or reachable and stuck in maintenance mode, or up with a database that isn't, or quietly not running the background jobs that make everything else work. You find out when you need a file on your laptop that was only ever on your phone.

This guide monitors both apps from outside using the endpoints they expose for exactly this purpose, goes one level deeper with the authenticated endpoints that actually exercise the database, catches the two Nextcloud-specific silent failures (maintenance mode and a dead cron), covers the document server behind Nextcloud, and adds a heartbeat for the storage and backups both apps sit on.

Nextcloud from outside: status.php

Every Nextcloud answers https://cloud.example.com/status.php without authentication with a small JSON document:

{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"31.0.4.1","versionstring":"31.0.4","edition":"","productname":"Nextcloud","extendedSupport":false}

Point an HTTP monitor at it, expecting a 200. That alone covers the host, the reverse proxy, PHP, and the certificate on your hostname (SSL expiry is tracked on every HTTPS monitor), from CronAlert's free plan every three minutes. On Pro, add a keyword check for "maintenance":false. This is the important one: when an update fails halfway, or an admin runs occ maintenance:mode --on for a backup and forgets, Nextcloud serves a maintenance page to every client while status.php keeps returning 200. The keyword turns "the instance is answering" into "the instance is usable." If you'd rather alert on a pending database upgrade as well, a second monitor with a does-not-contain check on "needsDbUpgrade":true does it; each monitor carries one assertion.

For a check that reaches into the system, Nextcloud's serverinfo app (bundled, under Administration → Monitoring) exposes /ocs/v2.php/apps/serverinfo/api/v1/info?format=json with CPU load, memory, free disk space, active users, and database size. It requires either admin credentials or the monitoring token you can generate on that same admin page, sent as an NC-Token header along with OCS-APIRequest: true. Custom headers are on every plan, so a monitor on that URL with those two headers, expecting 200, confirms the database is answering too. Treat the token like a password; it reads system metrics, not files.

Nextcloud's background jobs: a heartbeat on cron

Nextcloud depends on a background job runner for file scanning, trash and version cleanup, activity emails, calendar reminders, and most app functionality. The recommended configuration is a system cron entry that runs cron.php every five minutes as the web server user. When that stops (a renamed container, a changed PHP path, a crontab lost in a rebuild), Nextcloud shows a warning in the admin overview and otherwise carries on degrading quietly. Make the cron line report for itself:

# crontab -u www-data -e
*/5 * * * * php -f /var/www/nextcloud/cron.php && curl -fsS -m 10 -X POST https://cronalert.com/api/heartbeat/<token> >/dev/null

Create the heartbeat monitor with a 5-minute expected interval; with the default grace it alerts after ten minutes of silence. The && matters: a cron.php that exits non-zero doesn't ping, so a broken job run and a missing job run look the same to the monitor. In the Docker image the cron runs in a separate container from the same image; append the ping to that container's cron file instead, or run the check from the host with docker exec.

Immich from outside: ping, then statistics

Immich exposes /api/server/ping, which returns {"res":"pong"} with no authentication. Monitor it on your public hostname expecting 200, and on Pro add the keyword pong so a proxy's error page can't pass. That proves the server container is up and reachable from the internet, which is exactly the condition the mobile app's background backup needs and the one that breaks most often: a port forward, a DDNS record, a reverse proxy, a certificate.

Ping doesn't touch the database, so Immich can answer pong while Postgres is down and every real request fails. For that, use an authenticated endpoint. In Immich, open Account Settings → API Keys, create a key named "CronAlert", and create a second monitor on /api/server/statistics with the header x-api-key: <your key>, expecting 200. That call queries the database for photo, video, and usage totals, so a database outage fails it immediately. On Pro, a keyword check for "photos": (or a regex asserting the count is non-zero) adds "and the library is still there," which catches the storage-mount failure described below from the application's point of view. The API key only needs read access to server statistics; Immich lets you scope keys when you create them.

Immich's machine-learning container and its job queues can also stall without affecting either endpoint; thumbnails stop generating and faces stop being recognized while uploads succeed. Those are visible in the admin UI's Jobs page and through authenticated job-status endpoints; if you want an external signal, a small script on the host that compares the queue depth over time and pings a heartbeat is the pattern, and the background worker guide describes it.

The document server behind Nextcloud

If you edit documents in Nextcloud, there's a second server involved, and when it's down, every spreadsheet opens to a spinner while Nextcloud itself is fine. Both popular choices expose an unauthenticated endpoint. Collabora Online answers https://office.example.com/hosting/discovery with an XML document listing supported formats; monitor it expecting 200 (keyword wopi-discovery on Pro). OnlyOffice Document Server answers https://office.example.com/healthcheck with the literal text true; monitor it expecting 200 (keyword true). Both are on the free plan as plain HTTP checks, and both go behind the same reverse proxy whose misconfiguration is the usual reason documents stop opening after a change.

Underneath both: storage and backups

Both apps store their data on a filesystem that is frequently a mounted share: Nextcloud's data directory on a NAS, Immich's upload location on an external disk. When the mount drops, the application keeps running against an empty directory. Nextcloud notices (it refuses to start if its data directory is missing its .ocdata marker, which produces a 500 your HTTP monitor catches); Immich mostly doesn't, which is why the statistics keyword above is worth having. Belt and braces is a per-minute heartbeat script on the host that checks the mount is present and the disk has room, the same script as in the NAS guide and the media server guide, with the mount path changed.

And backups. A Nextcloud backup is a database dump plus the data directory, taken with maintenance mode on; an Immich backup is a Postgres dump (pg_dumpall from the database container) plus the upload directory, and Immich's own documentation is blunt that the database dump is the part people forget. Whatever script does yours, end it with a heartbeat ping on verified success and set the expected interval to the schedule, as laid out in monitoring scheduled backups. If you turn maintenance mode on for the Nextcloud backup, put a maintenance window over that slot on the status.php keyword monitor so the planned maintenance doesn't page you.

Reading the alert

status.php / pingAuthenticated checkCron or storage heartbeatMost likely
DownDownSilentHost, network, or reverse proxy. Everything behind it is unreachable.
UpDownFineThe database (Postgres, MariaDB) or Redis. The app answers; real requests don't.
Up, keyword failingUpFineNextcloud in maintenance mode, or Immich's library count dropped: check the mount.
UpUpCron silentNextcloud's background jobs stopped. Files sync; everything else slowly degrades.
UpUpStorage silentMount dropped or disk full before the app has noticed.

Frequently asked questions

Which Nextcloud URL do I monitor?

/status.php, expecting 200, with a keyword check on "maintenance":false on Pro.

Which Immich URL?

/api/server/ping for reachability; /api/server/statistics with an x-api-key header for the database.

How do I know Nextcloud's cron stopped?

Append a heartbeat ping to the cron line with &&; a 5-minute heartbeat alerts after ten minutes of silence.

Which plan?

All the HTTP checks, including header-authenticated ones, are free. Keyword assertions and heartbeats are Pro ($5/mo).

Does the API key or token expose my files?

No. Immich keys can be scoped to statistics; Nextcloud's monitoring token reads system metrics only. Treat both as secrets regardless.

Sync should fail loudly

The apps that hold your files and photos deserve better than a grey icon. Two HTTP checks per app, one of them reaching the database, a heartbeat on Nextcloud's cron, and a heartbeat on the backup script turn every quiet failure into a message before the next time you need something that was only on your phone. Create a free account, add the status and ping monitors, and upgrade to Pro for the keywords and heartbeats. Related reading: monitoring Docker and self-hosted apps, monitoring a NAS, monitoring a Proxmox host, monitoring Home Assistant, and choosing between HTTP, TCP, and heartbeat monitors.