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 aliasThis 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:
| Service | Image | Host port(s) | Role |
|---|---|---|---|
| PostgreSQL | postgres:18 | 5433 → 5432 | Auth, orgs, projects, config |
| NATS | nats:2.14-alpine | 4222 / 6222 / 8222 | JetStream (started with --jetstream); monitoring on 8222 |
| ClickHouse | clickhouse-server:26.5 | 8123 / 9000 / 9009 | Event storage (nofile raised to 262144) |
| Dragonfly | dragonfly:v1.38.1 | 6380 → 6379 | Redis-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.
| Target | Compose action |
|---|---|
make infra-up | Start the four services detached |
make infra-up-fg | Start them in the foreground (logs to your terminal) |
make infra-down | Stop 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 migrateEach 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 10000pug 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 seedcommand — not per-storepostgres seed/clickhouse seed. It handles both stores itself.
5. Start the dev server
go run ./cmd/pug dev # `pug start` is an aliaspug 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 devmake 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:5173Its 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:4317Pug 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
| Target | What it does |
|---|---|
make infra-up / make infra-down | Start / stop the dev backing services |
make clickstack / make clickstack-down | Add / remove the local OTLP collector + HyperDX UI |
make build | Compile all binaries into bin/ |
make test | Run the Go test suite with the race detector |
make cover | Run tests and write coverage.out |
make lint | Lint Go code (golangci-lint) |
make psql | Open a psql shell in the running Postgres container |
make chql | Open 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:
| Target | Regenerates | Run after editing |
|---|---|---|
make sqlc | Type-safe Go query code (into internal/gen/repo) | SQL query files under schema/postgres |
make rpc | Connect RPC / protobuf Go (lints protos first) | .proto files under proto/ |
make templ | Compiled Go for the transactional email templates | templ email templates |
make gen-ts | TypeScript types from the protos | .proto files (for the dashboard/SDKs) |
Common issues
| Symptom | Fix |
|---|---|
make infra-up fails | Confirm Docker is running; check for port conflicts on 5433, 4222, 8123, 6380 |
| Migrations fail | Run make infra-up first and wait for the containers to report healthy |
pug dev exits immediately | Check .env exists and DATABASE_URL / NATS_URL / CLICKHOUSE_URL are reachable |
| Events not arriving | Confirm the events worker started (it’s listed in the pug dev output) |
| SDK requests fail with a network error | Set the SDK endpoint to http://localhost:3000 explicitly — the default is Pug Cloud, not your local server |
| Dashboard can’t sign in | Ensure the backend is running and its PUG_CORS_ORIGINS includes http://localhost:5173 |
Further reading
- CLI reference — every
pugcommand and flag - Dashboard — build and configure the UI
- Configuration — all environment variables
- Deployment — take this to production