Tables on tabs with few rows (Alerts, Nodes, Maint, Users) now sit in the upper third of the viewport instead of flush against the tab bar. Dense tabs like Monitors and Logs fill naturally and are unaffected.
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 (UPTOP_METRICS_PUBLIC=trueto expose without auth) - 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
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Themes
Five built-in themes: Flexoki Dark, Tokyo Night, Catppuccin Mocha, Nord, Gruvbox. Press T to cycle.
Quick start
UPTOP_ADMIN_KEY="$(cat ~/.ssh/id_ed25519.pub)" go run ./cmd/uptop
ssh -p 23234 localhost
Want some data to look at first:
UPTOP_ADMIN_KEY="$(cat ~/.ssh/id_ed25519.pub)" 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_METRICS_PUBLIC |
false |
Expose /metrics without auth |
UPTOP_MAINT_RETENTION |
168h |
How long ended maintenance windows are kept |
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 (UPTOP_MAINT_RETENTION) |
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.






