DEV Community

Ritom Puzari
Ritom Puzari

Posted on Originally published at puzaricloud.in

Self-hosting a full monitoring stack in one command: servers, logs, APM and status pages

Teams self-host monitoring for two reasons: the data cannot leave the building, or the SaaS bill grew faster than the fleet. The catch is that "self-hosted monitoring" usually means five services. This post is about what those five are, what running them really costs, and how to collapse them into one install.

The usual stack and what each piece costs you

Need Common choice What you maintain
Host metrics Prometheus + node_exporter retention sizing, cardinality, remote storage once it grows
Dashboards + alerts Grafana + Alertmanager provisioning, dashboards as code, alert routing
Logs Loki + Promtail chunk storage, label discipline, query limits
Uptime and status page Uptime Kuma one more SQLite database to back up
Traces Tempo or Jaeger object storage and sampling

Each is good software. Together they are roughly a day to set up properly, an afternoon a month to keep patched, and one incident a quarter when a disk fills or an upgrade changes a config format. For a platform team that is fine. For two developers doing ops on the side, it is the reason monitoring quietly stops getting attention.

What a single install has to do to replace that

  • Collect host metrics, processes, containers and services with one agent that needs no configuration.
  • Ship logs from the same agent, with search and alerting on patterns.
  • Give you request-level visibility without instrumenting every service. Passive capture of HTTP on the host gets you per-endpoint counts, error rates and p95 latency with zero code changes; SDKs add traces where you want them.
  • Run outside-in checks (HTTP, TCP, DNS, TLS, cron heartbeats) and a status page.
  • Update itself safely, verify what it installs, and keep working when the licence server or the internet is unreachable.

The one-command version

Vigil's self-hosted edition installs like this on any Ubuntu, Debian, RHEL-family or Alpine host:

curl -fsSL https://puzaricloud.in/selfhosted/install.sh | sudo sh -s -- \
  --domain vigil.example.com --email you@example.com --license 'VGL1.…'
Enter fullscreen mode Exit fullscreen mode

It installs Docker if missing, downloads the release bundle and its detached Ed25519 signature, and verifies the signature with openssl pkeyutl -verify -rawin against a public key embedded in the installer before extracting anything. Ed25519 was chosen over RSA because the signature is 64 bytes, verification is a single call in OpenSSL 1.1.1 or newer with no hash negotiation to get wrong, and the same key signs licence keys, so there is one root of trust to protect. The installer then writes a Caddyfile with automatic HTTPS (ACME through Let's Encrypt, or tls internal for private networks) and starts Postgres, a worker and two web replicas behind Caddy's load balancer with active health checks.

Two replicas are what make updates safe. vigil update downloads and verifies the new bundle, builds the image, then replaces the first web container and waits for its health check to pass before touching the second, so there is never a moment without a healthy backend. If the new version fails its health check within two minutes the previous image is retagged and both replicas roll back. vigil backup runs pg_dump in custom format plus the config files every six hours by cron and keeps 14 daily and 8 weekly archives.

For private networks, --internal-tls makes Caddy issue certificates from its own local CA and --no-phone-home keeps the instance fully offline. A licence key is a signed JSON payload (VGL1.<payload>.<signature>) that the instance verifies locally against the embedded public key, so the instance runs with no outbound connection at all. On first start each installation generates its own Ed25519 identity, and the licence that finally unlocks it is bound to that identity's fingerprint, which is what stops one key being copied across many hosts. When a key lapses there are 14 days of grace; after that the instance becomes read-only rather than stopping monitoring, because an expired licence should never be the reason you miss an outage.

You can inspect exactly what the installer does before running it: install.sh and its signature are published next to the public key.

Resource use

Postgres is the only stateful part. A fleet of 20 servers reporting every 30 seconds with logs enabled lands around 2 GB of database growth a month at the default retention.

When you should still run the big stack

If you need distributed tracing across dozens of services with high-cardinality labels, or you already have Prometheus expertise and dashboards you love, keep them. The one-install approach is for teams who want monitoring to be a thing they have, not a thing they run.


Originally published on the PuzariCloud engineering blog. Drafted with AI assistance and reviewed, edited and tested by the author, who builds Vigil by PuzariCloud, the monitoring service the examples use.

Top comments (0)