Requirements
Backing services, toolchains, ports, and sizing for self-hosting Pug.
Toolchains
What you need depends on whether you build from source or run prebuilt container images.
| Tool | Version | Needed for | Why |
|---|---|---|---|
| Go | ≥ 1.26 | Building the backend | Compiles the pug server, workers, and CLI. The repo targets 1.26.3. |
| Bun | Latest | Building the dashboard | Installs deps and builds the React SPA. Node 22 is the runtime baseline. |
| Docker | Any recent release | Local infra or container builds | Runs the four backing services locally, and builds the backend images. |
If you deploy from published container images instead of building, you need neither Go nor Bun — only a container runtime. See Deployment.
git clone [email protected]:pug-sh/pug.git # backend: server, workers, pug CLI
cd pugThe dashboard lives in a separate repo (pug-sh/app) and is covered in Dashboard.
Backing services
The backend requires four backing services: PostgreSQL, ClickHouse, NATS, and Dragonfly. The versions below are what the project develops and tests against (from the dev Docker Compose); you may run managed equivalents (e.g. Amazon RDS for Postgres, ClickHouse Cloud) at compatible versions. The Pug server itself appears in the table only for its listening port.
| Component | Image / Version | Container port(s) | Role |
|---|---|---|---|
| PostgreSQL | postgres:18 | 5432 | Auth, orgs, projects, dashboards, and config. Dev host mapping: 5433→5432. |
| ClickHouse | clickhouse/clickhouse-server:26.5 | 8123 (HTTP / /ping), 9000 (native), 9009 (interserver) | Analytics event storage and queries. Requires ulimits.nofile ≥ 262144. |
| NATS | nats:2.14-alpine | 4222 (client), 6222 (cluster), 8222 (monitoring) | JetStream message queue. Must be started with --jetstream. |
| Dragonfly | docker.dragonflydb.io/dragonflydb/dragonfly:v1.38.1 | 6379 | Redis-compatible cache / rate-limit. Dev host mapping: 6380→6379. |
| Pug server | — | 3000 | The Connect RPC API server. Override with PUG_SERVER_PORT. |
NATS JetStream is required. Start NATS with
--jetstream(and--store_dirfor a durable volume). The monitoring server (-m 8222) exposes/healthzon port 8222. Without JetStream, event ingestion will not function.
For how the dev Compose file wires these up locally — health checks, volumes, and the observability overlay — see Development.
Ports summary
Only the Pug server (and your static dashboard host) need to be reachable from outside your private network. All backing services should be kept on an internal network:
| Port | Service | Exposure |
|---|---|---|
3000 | Pug server (Connect RPC) | Public — behind a TLS terminator |
8090 | Worker health / readiness | Internal only — probes (configurable via PUG_WORKER_HEALTH_ADDR) |
5432 | PostgreSQL | Internal only |
4222 | NATS client | Internal only |
8123 / 9000 | ClickHouse HTTP / native | Internal only |
6379 | Dragonfly | Internal only |
Sizing guidance
These are starting-point ballparks, not hard requirements. Actual resource needs depend heavily on event volume and retention.
Development / staging
| Component | CPU | RAM | Disk |
|---|---|---|---|
| Pug server + workers | 2 vCPU | 2 GB | — |
| PostgreSQL | 1 vCPU | 2 GB | 20 GB |
| ClickHouse | 2 vCPU | 4 GB | 50 GB |
| NATS | 1 vCPU | 1 GB | 10 GB (JetStream store) |
| Dragonfly | 1 vCPU | 1 GB | — |
Production (small — up to ~1 k events/s)
| Component | CPU | RAM | Disk |
|---|---|---|---|
| Pug server | 2 vCPU | 2 GB | — |
| Workers (per type) | 2 vCPU | 2 GB | — |
| PostgreSQL | 2 vCPU | 8 GB | 100 GB SSD |
| ClickHouse | 4 vCPU | 16 GB | 500 GB SSD |
| NATS | 2 vCPU | 2 GB | 20 GB SSD |
| Dragonfly | 1 vCPU | 2 GB | — |
The dashboard is static files — it needs no compute of its own, only a web server or CDN.
Production (larger workloads)
Scale the events worker horizontally for higher ingest throughput — each replica is an independent JetStream consumer. Give ClickHouse more CPU, RAM, and disk as event volume and retention grow. NATS benefits from a 3-node cluster for JetStream HA.
Optional: geo enrichment (Cloudflare)
Server-side geo auto-properties ($country, $region, $city, $continent, and related fields) are resolved at ingestion by the server — it reads them from Cloudflare proxy headers (CF-IPCountry, CF-Region, CF-IPCity, CF-IPContinent, etc.) on the inbound request. So this needs Cloudflare (or a proxy that sets the same headers) in front of the server; the visitor IP itself is never persisted.
CF-IPCountryis added automatically when Cloudflare IP Geolocation is enabled on your zone.CF-Region,CF-IPCity, and the remaining location headers require the “Add visitor location headers” Managed Transform to be enabled.
Geo lookup sits behind a small Provider interface (internal/geo), and Cloudflare headers are the only implementation today. The interface is designed to also accept a local database lookup — e.g. MaxMind (MMDB) — from the client IP in a future release. Until then, if Pug runs behind a different proxy or without these headers, geo auto-properties are simply absent from events; there’s no built-in GeoIP database yet.
Further reading
- Configuration — environment variables and secrets
- Deployment — production deployment guide
- Development — local dev stack setup