Self-hosted uptime monitoring: one compose file, and the logs come with it
5 min read ยท published 2026-10-11
The short version
Self-hosted uptime monitoring used to mean picking between two shapes: a single-purpose uptime tool that never grows past green and red, or a monitoring stack that can graph anything once you have spent a week assembling it. UpControl's core is a third shape. It is AGPL-3.0, runs from one compose file, and does not stop at uptime: heartbeats for jobs, a log ingest endpoint, events and analytics, public status pages. The hosted service at upcontrol.io runs the same code and answers the same CLI, so nothing you wire up is thrown away if you ever stop operating the box.
What the self-hosted package is, from the project's README as of 2026-10:
| The self-hosted core | |
|---|---|
| License | AGPL-3.0 core, MIT CLI |
| Setup | one compose file, Docker with Compose v2 |
| The box | 1GB RAM recommended (512MB with swap minimum), 10GB disk |
| Website checks | from 1-minute intervals, SSL and domain expiry on every check |
| Heartbeats | for cron jobs and workers, never counted toward the check limit |
| Logs | one ingest endpoint, POST /i, NDJSON; SDK, CLI or any sender |
| Events and analytics | funnels, retention, breakdowns, A/B tests built from events |
| Status pages | public, one per project, with real uptime bars |
| Alerts | Telegram, email through your own SMTP, Slack, Discord, any webhook |
| Telemetry | none: no version pings, no usage beacons |
Why run your own
Three reasons show up every time. Some teams want the data to never leave their network: customer URLs, log lines, alert targets all staying inside. Others have a box that is already paid for and want the monitoring line item to stay at zero. And some want the monitor working even when the vendor's own page is red.
The self-hosted package does not call home, and that is a documented promise rather than a default nobody checked: no version pings, no usage beacons, and if it ever changes the project says it will be opt-in and loudly documented.
One counterpoint belongs here, and the README says it too: a one-box self-host cannot watch the box it runs on. If the box dies, the monitor dies with it. The honest advice is to let something outside the box watch the box, and the free hosted plan covers exactly that: three checks, every 5 minutes, no card.
The install, in one command
- Put Docker with Compose v2 on the box. 1GB of RAM is the recommendation; 512MB with active swap is the floor, and the installer refuses to start below it rather than fall over later.
- Clone the repo and run the installer:
git clone https://github.com/upcontrol-io/upcontrol,cd upcontrol/infra,./install.sh. - Answer four questions while the images pull. When it finishes,
https://localhostis a working instance: website checks every minute, a log ingest endpoint, Telegram and email alerts. - No account with anyone is created, and nothing phones home at any point in this flow.
Single-user is the default: UC_AUTH=none boots one owner account with no sign-in, which is right for a private box. Set UC_AUTH=magic-link before exposing the instance to the internet.
Wiring the app for logs and events
Checks need nothing from your code. Logs do, and one command hands the wiring to the coding agent you already use (Claude Code, Cursor, Codex, Gemini CLI, Copilot, Windsurf, Amp, Aider, Cline, opencode):
npx upcontrol --endpoint https://your-box
The CLI points wherever you tell it; the hosted service is only the default. You ask in plain words, "send all my logs to upcontrol" or "track my users from first visit to purchase", the agent stages a diff for your review and commits nothing, and npx upcontrol verify waits until data provably arrives, naming the failure precisely when it does not.
Your code never leaves the machine. The SDK sends only what the reviewed log points emit, scrubbed client-side before anything is sent. And the SDK is optional: the wire is plain NDJSON over one HTTP door, so a Go, Rust or browser sender needs no package at all.
The why, not just down
Uptime tells you the site broke. The interesting question is what it was doing when it did, and that is the part most self-hosted setups never get to. The core correlates checks with logs, deploys and events: an incident page carries the deploy that landed before it and the log lines around the failure, not only "down since 12:04". Error-rate detection runs over your own logs and fires alerts on new errors, and a missed heartbeat opens an incident like any failed check, at most an hour past its due time.
That is the lane the README claims for it, between the two usual shapes: Uptime Kuma is genuinely lighter if uptime checks are all you need, and a Grafana stack is more powerful if you have the time to assemble and query it. Logs, checks and the why of an incident on one box is the middle.
The honest limits
- One owner account by default, no sign-in. Right for a private box, and you must switch auth modes yourself before putting it on the internet.
- No synthetic probes advertised from many cities. The fleet checks from its regions and stores the response phases (dns, tcp, tls, wait) per check; a fixed region list is what a self-host gets.
- Younger product, fewer integrations than the decade-old incumbents. Telegram, email, Slack, Discord and webhooks cover the alerting; a large existing toolchain may miss a connector it relied on elsewhere.
- Web analytics on a self-host reaches back at most 31 days, and there are no session recordings. Tools built for analytics alone go deeper and further back.
- It cannot watch its own box. Pair it with the free hosted tier, or accept that a dead machine reports nothing.
One command starts it, and the same command wires your app to it: [get the compose file](https://upcontrol.io/?utm_source=blog&utm_medium=vs&utm_campaign=engine).