Skip to content

Self-hosting

Prerequisites, architecture, environment, TLS, and upgrades for a production Pulse deploy.

  • Linux host, 2 vCPU / 2 GB RAM minimum (4 GB recommended).
  • Docker Engine 24+ with the Compose v2 plugin.
  • A domain with A / AAAA records pointing at the host.
  • Ports 80 and 443 reachable from the public internet — Traefik uses port 80 for Let’s Encrypt HTTP-01.
  • Outbound TCP/443 for image pulls + ACME; TCP/25 or 587 if you intend to send mail directly.
[ Browser ] ──▶ [ Traefik ] ──▶ [ pulse-api ] ──▶ [ Postgres + Timescale ]
├──▶ [ Redis ]
│ ▲
└──▶ [ pulse-worker × N ]
  • API — HTTP + WebSocket. Sessions in Postgres, pub/sub via Redis. Migrations on boot.
  • Worker — pulls check jobs from Redis, runs them, writes results back. Stateless; scale horizontally.
  • Traefik — TLS termination, ACME, optional on-demand certs for status-page custom domains.

All config is environment-driven. See the reference table. Every service validates env on boot and exits non-zero if a required variable is missing.

Terminal window
docker compose -f docker/docker-compose.yml up -d
docker compose -f docker/docker-compose.yml ps

The API container exits non-zero with a clear log message if anything is missing — docker compose logs api is your first stop on a failed boot.

Traefik is part of the compose stack and handles certificates via Let’s Encrypt’s HTTP-01 challenge. The ACME state is persisted to a named volume (letsencrypt). Back this file up — losing it forces re-issue and you’ll hit LE rate limits if you bounce often.

Terminal window
# manual snapshot
docker run --rm -v pulse_letsencrypt:/src -v /var/backups/pulse:/dst \
alpine sh -c 'cp /src/acme.json /dst/acme-$(date -u +%FT%TZ).json'

To use Let’s Encrypt’s staging endpoint while you debug a cert, edit the certificatesresolvers block in docker/docker-compose.yml to point at https://acme-staging-v02.api.letsencrypt.org/directory, then docker compose up -d traefik.

Bootstrap signup is open until one user exists, then closes automatically. Browse to /signup, create your operator account, then create members via Settings → Members → Invite.

If you ever lock yourself out, delete the user row in Postgres and the bootstrap path reopens.

Terminal window
cd /opt/pulse
git fetch && git checkout v0.X.Y
docker compose -f docker/docker-compose.yml pull
docker compose -f docker/docker-compose.yml up -d

The API applies migrations on boot; no separate command. Migrations are designed to be backwards-compatible within a minor version — you can roll back to the previous tag without data loss.