24observe
checking… Sign in Start free
Self-host

Two paths. Pick the one that fits your ops.

The self-hosted distribution works two ways: full stack on a single host (everything included, zero external services), or bring your own managed Postgres (you handle the DB, we run the rest). Pick what fits.

Path A · Full stack

Everything on one host. Zero external services.

Best for: small teams, on-prem, air-gapped, learning the codebase, evaluating before paying.

01 · Clone
git clone https://github.com/vikasswaminh/observe24.git && cd observe24
02 · Generate secrets
cat > .env <<EOF JWT_SECRET=$(openssl rand -hex 32) ENCRYPTION_KEY=$(openssl rand -hex 16) POSTGRES_PASSWORD=$(openssl rand -hex 20) CLICKHOUSE_PASSWORD=$(openssl rand -hex 20) EOF chmod 600 .env
03 · Build + start
docker compose build && docker compose up -d
04 · Migrate
docker compose run --rm --entrypoint "" -w /app/packages/db api node dist/migrate.js
05 · Register the first user
curl -X POST http://localhost/api/v1/auth/register \ -H 'content-type: application/json' \ -d '{"email":"[email protected]","password":"secretLongEnough"}'

Total time: ~5 minutes on a fresh host. Container resource ceilings are baked into compose — runs comfortably on a 4-core / 8 GB instance.

Path B · Bring your own Postgres

Use a managed Postgres (Neon, Supabase, RDS, anything PG 16+).

Best for: teams who already have a managed-DB story, want PITR / branching / read-replicas out of the box, or don't want to operate Postgres themselves. The compose stack drops the bundled Postgres + backup containers; everything else stays.

01 · Provision Postgres
Create a Postgres 16+ database (any provider). Note the pooled connection string.
02 · Apply migrations
for f in packages/db/drizzle/*.sql; do psql "$DATABASE_URL_DIRECT" -f "$f" done
03 · Set DATABASE_URL
echo "DATABASE_URL=$YOUR_POOLED_CONNECTION_STRING" >> .env
04 · Start without bundled Postgres
docker compose up -d api worker scheduler redis clickhouse caddy prometheus grafana loki promtail
Skip postgres and postgres_backup — your provider handles both.

Note: when using a transaction-pooled connection (e.g. Neon's -pooler endpoint or PgBouncer), the application client is configured to disable prepared statements, which is required for transaction pooling to work correctly.

What runs after up

Behind one reverse proxy.

API

REST + OpenAPI on port 80

OpenAPI spec at /openapi.json, Swagger UI at /docs, Prometheus metrics at /metrics.

OBSERVABILITY

Grafana, Prometheus, Loki bundled

Grafana on :3002, Prometheus on :9090, Loki on :3100. Datasources pre-wired.

DATA

Postgres + ClickHouse + Redis

Postgres for entities, ClickHouse for time-series check results, Redis for queues + sessions. (Bundled in Path A; you provide Postgres in Path B.)

BACKUPS

Daily dumps · 7-day retention (Path A only)

Automated with a tiny cron container. In Path B your provider handles PITR — usually better.

System requirements

Runs on modest hardware.

CPU
2 cores (4 recommended)
RAM
4 GB (8 GB recommended for full-stack; 2 GB enough for BYO-Postgres)
Disk
20 GB (90-day check-result TTL dominates usage)
OS
Any Linux with Docker Engine 20+ and Compose v2
Ports
80 inbound; outbound HTTPS to the endpoints you monitor

Sovereignty by default.

Your database, your backups, your domain — no vendor lock-in, and monitoring that runs entirely on your own infrastructure.