Skip to Content
DocsSelf-HostingDevelopment

Development

Run the whole Pug stack on your machine — backend, backing services, and (optionally) the dashboard — from clone to a working server you can point an SDK at.

Install the prerequisites from Requirements first: Docker , a Go ≥ 1.26 toolchain, and — if you want the dashboard — Bun.

1. Clone and configure

git clone [email protected]:pug-sh/pug.git cd pug cp .env.example .env

.env.example is pre-filled with defaults that match the dev Docker Compose (Postgres on 5433, ClickHouse on 9000, NATS on 4222, Dragonfly on 6380). Open .env and set at least:

PUG_JWT_SECRET_KEY=<any-long-random-string>

Full variable reference: Configuration.

2. Start infrastructure

make infra-up # `make infra` is an alias

This runs docker compose -f infra/dev/docker-compose.yaml up -d, starting the four backing services — each with a health check, a named volume for persistence, and its own bridge network:

ServiceImageHost port(s)Role
PostgreSQLpostgres:185433 → 5432Auth, orgs, projects, config
NATSnats:2.14-alpine4222 / 6222 / 8222JetStream (started with --jetstream); monitoring on 8222
ClickHouseclickhouse-server:26.58123 / 9000 / 9009Event storage (nofile raised to 262144)
Dragonflydragonfly:v1.38.16380 → 6379Redis-compatible cache

The compose file is for local development only — not a production topology. In production you run these as managed services or your own cluster. Stop the containers with make infra-down.

TargetCompose action
make infra-upStart the four services detached
make infra-up-fgStart them in the foreground (logs to your terminal)
make infra-downStop and remove them

3. Run migrations

Run once before the first boot and again after each upgrade. Order doesn’t matter — they’re safe to re-run.

go run ./cmd/pug postgres migrate go run ./cmd/pug nats migrate go run ./cmd/pug clickhouse migrate

Each reads its connection details from .env (DATABASE_URL, NATS_URL, CLICKHOUSE_URL). There is no make migrate target — migrations are pug commands. Use go run ./cmd/pug … here, or ./bin/pug … after make build.

4. Seed test data (optional)

Populate the demo project with synthetic events and profiles so there’s something to query right away:

go run ./cmd/pug seed --count 10000

pug seed resets Postgres and ClickHouse, then generates events and seeds profiles for the users behind them. The default is 500,000 events — pass --count to keep it light on a laptop. See the CLI reference for all flags (--batch, --no-reset).

There is a single pug seed command — not per-store postgres seed / clickhouse seed. It handles both stores itself.

5. Start the dev server

go run ./cmd/pug dev # `pug start` is an alias

pug dev starts the API server and all workers together in one process. On boot it prints the server URL and the active worker list; the server listens on :3000 (override with PUG_SERVER_PORT). Press Ctrl+C to stop everything cleanly. See CLI reference → dev.

Compiled alternative

To run the compiled binary (or reproduce how production binaries are built):

make build ./bin/pug dev

make build compiles bin/pug plus a standalone binary per worker and migration runner — see Deployment → Build.

6. Run the dashboard (optional)

To use the UI locally, clone and run pug-sh/app alongside the backend:

cd ../app # separate repo bun install bun run dev # http://localhost:5173

Its default VITE_API_BASE_URL is http://localhost:3000, which already matches your local pug dev — no extra config. The backend’s default PUG_CORS_ORIGINS (http://localhost:5173) already allows the Vite dev server, so sign-in works out of the box.

7. Point an SDK at localhost

Set the endpoint to http://localhost:3000 when initializing an SDK:

import { init } from '@pug-sh/browser' init('YOUR_PROJECT_ID', { apiKey: 'pub_YOUR_PUBLIC_KEY', endpoint: 'http://localhost:3000', })

The default endpoint is Pug Cloud, so this override is required to hit your local server. Full per-SDK guidance: Connect your SDKs.

Observability (ClickStack)

By default the backend logs to stdout as text — no collector required. To view traces, metrics, and logs locally, start the optional ClickStack overlay, which adds a HyperDX OTLP collector and UI on top of the dev services:

make clickstack # infra + HyperDX collector (OTLP :4317/:4318) + UI (:3001)

Then set the OTLP endpoint in .env and restart pug dev:

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

Pug auto-detects the endpoint at startup and exports there instead of logging to stdout; open the HyperDX UI at http://localhost:3001. Stop it with make clickstack-down. The overlay is infra/dev/docker-compose.clickstack.yaml, layered onto the base compose file.

Make targets

Everyday

TargetWhat it does
make infra-up / make infra-downStart / stop the dev backing services
make clickstack / make clickstack-downAdd / remove the local OTLP collector + HyperDX UI
make buildCompile all binaries into bin/
make testRun the Go test suite with the race detector
make coverRun tests and write coverage.out
make lintLint Go code (golangci-lint)
make psqlOpen a psql shell in the running Postgres container
make chqlOpen a clickhouse-client shell in the running ClickHouse container

Code generation (when editing backend source)

Pug generates code from three sources; regenerate after editing the corresponding inputs, then commit the output:

TargetRegeneratesRun after editing
make sqlcType-safe Go query code (into internal/gen/repo)SQL query files under schema/postgres
make rpcConnect RPC / protobuf Go (lints protos first).proto files under proto/
make templCompiled Go for the transactional email templatestempl email templates
make gen-tsTypeScript types from the protos.proto files (for the dashboard/SDKs)

Common issues

SymptomFix
make infra-up failsConfirm Docker is running; check for port conflicts on 5433, 4222, 8123, 6380
Migrations failRun make infra-up first and wait for the containers to report healthy
pug dev exits immediatelyCheck .env exists and DATABASE_URL / NATS_URL / CLICKHOUSE_URL are reachable
Events not arrivingConfirm the events worker started (it’s listed in the pug dev output)
SDK requests fail with a network errorSet the SDK endpoint to http://localhost:3000 explicitly — the default is Pug Cloud, not your local server
Dashboard can’t sign inEnsure the backend is running and its PUG_CORS_ORIGINS includes http://localhost:5173

Further reading

Last updated on