Skip to Content
DocsSelf-HostingRequirements

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.

ToolVersionNeeded forWhy
Go≥ 1.26Building the backendCompiles the pug server, workers, and CLI. The repo targets 1.26.3.
BunLatestBuilding the dashboardInstalls deps and builds the React SPA. Node 22 is the runtime baseline.
DockerAny recent releaseLocal infra or container buildsRuns 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 pug

The 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.

ComponentImage / VersionContainer port(s)Role
PostgreSQLpostgres:185432Auth, orgs, projects, dashboards, and config. Dev host mapping: 5433→5432.
ClickHouseclickhouse/clickhouse-server:26.58123 (HTTP / /ping), 9000 (native), 9009 (interserver)Analytics event storage and queries. Requires ulimits.nofile ≥ 262144.
NATSnats:2.14-alpine4222 (client), 6222 (cluster), 8222 (monitoring)JetStream message queue. Must be started with --jetstream.
Dragonflydocker.dragonflydb.io/dragonflydb/dragonfly:v1.38.16379Redis-compatible cache / rate-limit. Dev host mapping: 6380→6379.
Pug server3000The Connect RPC API server. Override with PUG_SERVER_PORT.

NATS JetStream is required. Start NATS with --jetstream (and --store_dir for a durable volume). The monitoring server (-m 8222) exposes /healthz on 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:

PortServiceExposure
3000Pug server (Connect RPC)Public — behind a TLS terminator
8090Worker health / readinessInternal only — probes (configurable via PUG_WORKER_HEALTH_ADDR)
5432PostgreSQLInternal only
4222NATS clientInternal only
8123 / 9000ClickHouse HTTP / nativeInternal only
6379DragonflyInternal only

Sizing guidance

These are starting-point ballparks, not hard requirements. Actual resource needs depend heavily on event volume and retention.

Development / staging

ComponentCPURAMDisk
Pug server + workers2 vCPU2 GB
PostgreSQL1 vCPU2 GB20 GB
ClickHouse2 vCPU4 GB50 GB
NATS1 vCPU1 GB10 GB (JetStream store)
Dragonfly1 vCPU1 GB

Production (small — up to ~1 k events/s)

ComponentCPURAMDisk
Pug server2 vCPU2 GB
Workers (per type)2 vCPU2 GB
PostgreSQL2 vCPU8 GB100 GB SSD
ClickHouse4 vCPU16 GB500 GB SSD
NATS2 vCPU2 GB20 GB SSD
Dragonfly1 vCPU2 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-IPCountry is 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

Last updated on