- go run cmd/uptop/main.go broke when PR #108 split config.go out of main.go — use ./cmd/uptop package path (same fix #104 applied to Dockerfile and goreleaser) - binary section claimed Linux amd64 only; goreleaser ships linux/darwin/windows x amd64/arm64 + deb/rpm since #104 - state canonical repo (Gitea) vs mirror (GitHub) up front - UPTOP_ADMIN_KEY is the only documented first-run path
uptop
Self-hosted uptime monitoring with a TUI over SSH.
No browser. No client install. Just ssh -p 23234 your-server.
What is this
An uptime monitor you manage entirely from the terminal. It runs as a server, exposes an SSH endpoint, and drops you into a full TUI — monitors, alerts, logs, nodes, all there.
Built on RDGames/go-upkeep. Rewritten for clustering, config-as-code, and a proper dashboard.
Canonical repo: gitea.lerkolabs.com/lerkolabs/uptop — GitHub is a mirror; releases are published to both.
Features
- 6 check types — HTTP, Push (heartbeat), Ping, Port, DNS, Groups
- 10 alert providers — Discord, Slack, Email, Ntfy, Webhook, Telegram, PagerDuty, Pushover, Gotify, Opsgenie
- Config as code — define monitors in YAML, apply declaratively, version control your setup
- HA clustering — leader/follower with automatic failover
- Prometheus metrics —
/metricsendpoint, wire it straight to Grafana - Public status page — HTML + JSON, toggle with an env var
- SQLite or Postgres — SQLite for single-node, Postgres for production
- Uptime Kuma import — migrate from Kuma with one command
Group monitors roll up child status for display but don't fire their own alerts yet — attach alerts to the children.
Screenshots
![]() |
![]() |
![]() |
![]() |
![]() |
|
Quick start
go run ./cmd/uptop
ssh -p 23234 localhost
Want some data to look at first:
go run ./cmd/uptop -demo
Install
Docker (recommended)
services:
uptop:
image: lerkolabs/uptop:latest
restart: unless-stopped
ports:
- "23234:23234"
- "8080:8080"
environment:
- UPTOP_DB_TYPE=sqlite
- UPTOP_DB_DSN=/data/uptop.db
- UPTOP_STATUS_ENABLED=true
# - UPTOP_ADMIN_KEY=ssh-ed25519 AAAA... you@host
volumes:
- ./data:/data
sysctls:
- net.ipv4.ping_group_range=0 2147483647
First run: set UPTOP_ADMIN_KEY to your SSH public key.
The sysctls line enables unprivileged ICMP inside the container — without it, ping monitors get no response and silently report DOWN.
Binary (Linux, macOS, Windows)
Download from Releases — amd64 and arm64 tarballs (zip for Windows), plus .deb/.rpm packages and checksums.txt.
From source
go install gitea.lerkolabs.com/lerkolabs/uptop/cmd/uptop@latest
Upgrading: Pull the new image (or binary) and restart. Database migrations run automatically on startup.
Config as code
Export your current monitors:
uptop export -o monitors.yaml
Apply a config file:
uptop apply -f monitors.yaml
uptop apply -f monitors.yaml --dry-run # see what would change
uptop apply -f monitors.yaml --prune # delete anything not in the YAML
Full reference in docs/config-as-code.md.
Environment variables
| Variable | Default | Description |
|---|---|---|
UPTOP_PORT |
23234 |
SSH server port |
UPTOP_HTTP_PORT |
8080 |
HTTP server port (status page, push, metrics) |
UPTOP_DB_TYPE |
sqlite |
sqlite or postgres |
UPTOP_DB_DSN |
uptop.db |
Database path or connection string |
UPTOP_STATUS_ENABLED |
false |
Enable public status page |
UPTOP_STATUS_TITLE |
System Status |
Status page title |
UPTOP_ENCRYPTION_KEY |
AES-256-GCM key for alert credentials (details) | |
UPTOP_CLUSTER_MODE |
leader |
leader, follower, or probe |
UPTOP_PEER_URL |
Leader URL for follower and probe nodes | |
UPTOP_CLUSTER_SECRET |
Shared key for cluster + API auth | |
UPTOP_INSECURE_SKIP_VERIFY |
false |
Skip TLS verification for checks |
UPTOP_ALLOW_PRIVATE_TARGETS |
false |
Allow monitoring RFC1918/loopback addresses |
UPTOP_ADMIN_KEY |
SSH public key seeded as first admin on startup | |
UPTOP_TRUSTED_PROXIES |
Comma-separated CIDRs/IPs whose X-Forwarded-For is trusted (details) |
See .env.example for all options including TLS, probes, and advanced settings.
Running behind a reverse proxy
By default uptop ignores the X-Forwarded-For header and rate-limits by the direct connection address — so a client can't spoof the header to bypass limits. If uptop sits behind a reverse proxy (nginx, Caddy, Cloudflare, an ALB), set UPTOP_TRUSTED_PROXIES to the proxy's address(es) so the real client IP is used instead:
# single nginx/Caddy on the same host
UPTOP_TRUSTED_PROXIES=127.0.0.1
# a proxy subnet, or Cloudflare ranges
UPTOP_TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12
Only requests whose immediate peer is in this list have their X-Forwarded-For honored (right-most non-trusted hop wins). Bare IPs are treated as single hosts; invalid entries are warned about and skipped. Leave it unset if uptop is exposed directly.
Encryption
Set UPTOP_ENCRYPTION_KEY to encrypt alert credentials (SMTP passwords, webhook URLs, API tokens) at rest with AES-256-GCM. Generate a key:
openssl rand -hex 32
Without this, credentials are stored as plaintext in the database. uptop warns on startup if unset. To encrypt credentials on an existing install, run uptop migrate-secrets with the key set.
Data retention
uptop prunes its own history in the background — no external cleanup jobs needed:
| Data | Kept |
|---|---|
| Check history | newest 1,000 checks per monitor |
| State changes (UP/DOWN transitions) | newest 5,000 per monitor |
| Logs | newest 200 entries |
| Maintenance windows | 7 days after they end (configurable) |
Sparklines, uptime percentages, and SLA reports are computed from these windows, so very long-horizon stats aren't retained. Export to Prometheus via /metrics if you need unlimited history.
Clustering
uptop supports three modes: leader (default single node), follower (HA failover — takes over if the leader goes down), and probe (stateless distributed checks from multiple regions).
See docs/clustering.md for setup guides, or the working examples in deploy/.
Migrating from Uptime Kuma
Export your Kuma backup JSON, then:
curl -X POST http://localhost:8080/api/import/kuma \
-H "X-Uptop-Secret: your-secret" \
-H "Content-Type: application/json" \
-d @kuma-backup.json
License
MIT — see LICENSE.




