Your containers are up—but how do you know they’re actually doing their jobs?
A recent discussion about invoices failing to reach clients got me thinking about what we mean when we say a self-hosted service is “healthy.”
The container can be running. The login page can load. An HTTP monitor can keep reporting success. Meanwhile, the task we installed the application for may have stopped working.
That gap seems especially important for services that work quietly in the background: photo uploads, document processing, file synchronization, scheduled backups, and outgoing email. If nobody opens the application regularly, a failure can remain invisible until someone needs the result.
I’m interested in how people monitor that gap without turning a home setup into another large infrastructure project.
I think there are several different questions hiding behind a green status indicator.
First, is the process alive? Container state and resource metrics help answer that. Second, can a client reach the service? DNS, TLS, HTTP checks, and response times help there. Third, can the application perform a useful operation? Finally, did the expected result actually appear where it was needed?
Those checks provide different evidence. A Docker health check can be as useful as the command behind it, but its meaning depends on what that command tests. Checking a page says something different from checking whether a worker successfully processed a queued document.
For a small setup, my proposed starting point would be to choose one important workflow per service and define its success condition explicitly.
For a document application, that might mean importing a harmless sample document and checking that its text becomes searchable. For file synchronization, it could mean verifying that a small test file reaches the destination intact within a reasonable time. For a photo service, it might mean checking that a test upload becomes visible through the client people actually use.
Email needs a little more care. An application reporting “sent,” or an SMTP server accepting a message, does not prove that a person found it in their inbox. A dedicated test mailbox could provide evidence that the message reached that particular destination, but it still wouldn’t prove delivery to every client or every provider.
The point would be to decide what each check establishes, rather than give every stage the same reassuring label.
For scheduled jobs, I’d also want to distinguish the last attempt from the last successful completion. A job can launch on schedule and still fail every time. Useful signals might include the timestamp of the last success, the age of the oldest queued item, or the time a test item takes to pass through the workflow.
There’s a complication with quiet services, though. No activity doesn’t necessarily mean failure. If nobody has uploaded a photo this week, an old processing timestamp may be perfectly normal. That’s where a small synthetic test could help—but it also creates maintenance of its own.
Test data needs to stay separate from real data. Checks should avoid sending messages to actual customers, creating genuine invoices, or repeatedly changing a family member’s library. A dedicated account or test destination seems preferable, with limited permissions and a clear cleanup policy.
I’d also keep frequent checks lightweight. Testing a small operation regularly and exercising a longer workflow after an upgrade may be a better compromise than running a full browser journey every minute. Backups deserve their own distinction too: a successful backup job is useful evidence, while being able to restore the data is a separate question.
The monitoring system can fail as well. If both the application and the only monitor run on the same host, losing that host may silence both. For something important, I’d consider whether the alert path can still work when the thing being monitored is unavailable.
I don’t think every hobby application needs all of this. If maintaining the checks takes more time than the occasional failure
Post #54076
9