Quickstart
From nothing to a working dev stack in about three minutes. Docker and
Docker Compose v2 are the only prerequisites for booting: Compose runs
every runtime, so you need no local Postgres and no local Node. You do
need Bun to develop once it is up, because every root script
(bun run check, bun run regen, bun run rename:project) runs on it.
After boot you have a register/login UI at http://localhost:7331,
the API at http://localhost:7330/api/* (OpenAPI spec served at
/swagger), Postgres with migrations applied, Valkey for cache and
BullMQ, and a generated TypeScript client wired into apps/ui that
fails to compile the moment the API contract drifts. Optional overlays
via env flags: Mailpit, Bull Board, observability, GlitchTip, image
update notifications.
Prerequisites
Section titled “Prerequisites”- Docker + Docker Compose v2 (
docker compose versionreportsv2.xor newer). - About 4 GB of free RAM.
git. Optionallygh, authenticated, to get a GitHub-hosted repo.- Bun 1.4.2 to develop. Not needed to boot.
1. Point an agent at it
Section titled “1. Point an agent at it”Paste this into your coding agent:
Set up boringstack.xyz for meIt reads /agents.md, which carries the install command, the health checks, the machine-readable config surface at /scaffold-manifest.json, and the invariants that keep generated code passing CI. Nothing else needs to be in the prompt.
2. Or run the one command yourself
Section titled “2. Or run the one command yourself”curl -fsSL https://boringstack.xyz/install.sh | sh -s -- --project acmeFive phases, announced as they go: preflight (Compose v2, free ports, memory), scaffold, rename, boot, health check. It never prompts, so it works the same from a terminal, a CI job or an agent. Useful flags:
| Flag | Effect |
|---|---|
--dry-run | Print the resolved plan and exit without writing. |
--json | One JSON object per phase on stdout; human output on stderr. |
--no-boot | Scaffold and rename only; skip Docker. |
--no-rename | Keep the BoringStack identifiers. |
--ghcr-owner, --domain, --dir, --ref | Override the defaults. |
Each phase exits with its own code (3 preflight, 4 scaffold, 5 rename,
6 boot, 7 health), and every failure prints the fix.
3. Or do it step by step
Section titled “3. Or do it step by step”The monorepo is a GitHub template repository,
so gh can create your copy without touching a browser:
gh repo create <your-repo> \ --template boringstack-xyz/boringstack --private --clonecd <your-repo>Without gh, clone and detach from upstream so this is your project rather than
a fork:
git clone --depth 1 \ https://github.com/boringstack-xyz/boringstack <your-repo>cd <your-repo> && rm -rf .git && git initThen rebrand. The script is idempotent and self-verifying: it fails if any
upstream identifier survives the rewrite, and DRY_RUN=1 previews the edits.
./scripts/rename-project.sh <project> <ghcr-owner> <domain>API, UI, docs, Compose and bootstrap infrastructure all live in this one tree:
apps/api/ (Bun + Elysia + Drizzle), apps/ui/ (Vite + React SPA),
apps/docs/ (this site), infra/compose/ (the Docker Compose stack) and
infra/bootstrap/ (optional OpenTofu VPS bootstrap).
4. Boot the local stack
Section titled “4. Boot the local stack”From the repo root:
./setup.sh --upsetup.sh bootstraps infra/compose/compose/.env, generates a
GLITCHTIP_SECRET_KEY, makes the compose scripts executable, and with
--up runs ./dev.sh up -d --build.
Or do the same thing by hand:
cd infra/compose/composecp .env.example .envchmod +x dev.sh ../scripts/*.sh./dev.sh up -d --buildFirst boot pulls base images, builds the api/ui dev images, and runs
migrations. If you set SUPERUSER_EMAIL and SUPERUSER_PASSWORD in
compose/.env, an admin user is also created. About three minutes on
a fast laptop.
5. Sign in
Section titled “5. Sign in”Open http://localhost:7331.
If you set SUPERUSER_EMAIL and SUPERUSER_PASSWORD in
compose/.env before booting, sign in with those credentials. That
path runs scripts/db/seed-superuser.ts, which is the only thing that
sets is_platform_admin. It skips an email that already exists rather
than promoting it, so give it an address you have not registered.
Otherwise click “Sign up” and register. That makes you the owner of your own account, since every signup creates a personal account, an owner membership and a Free plan row in one transaction. It does not make you a platform admin, whatever order you register in. You land on a dashboard that’s intentionally empty. Start building from here.
6. Build your first feature
Section titled “6. Build your first feature”The dashboard is intentionally empty so you start shipping immediately. BoringStack supports two workflows for adding features, both agent-friendly. Pick one per session; switch any time.
Path A: tell the agent what you want
Section titled “Path A: tell the agent what you want”In Claude Code or Cursor, run /add-full-feature and describe the
slice you want to ship. The skill at
.claude/skills/add-full-feature.md coordinates the api half, then
the OpenAPI regen, then the ui half. Good for small changes where
you trust the agent to stay scoped.
Path B: spec loop
Section titled “Path B: spec loop”Run bun run spec:init once per project to wire the spec loop
(creates .specs/next.md, the /spec slash command, and a TypeScript
gate that blocks source writes until you approve a slice). Then in
the agent:
/spec explore <idea>./spec slice.- You approve the slice.
/spec build.
The gate at tools/spec-loop/hooks/gate.ts is the discipline:
useful when the agent’s tendency to over-build is the failure mode
you’re worried about.
The spec loop is opt-in. A fresh bun run spec:init writes four
files (.specs/next.md, .claude/commands/spec.md,
.claude/settings.json, .cursor/hooks.json). Skip it and Path A is
the default. See Spec loop for the deep dive.
What’s running
Section titled “What’s running”- postgres (port 5432): app database; schemas
auth,billing,audit,app,notifications. - valkey (port 6379): cache and BullMQ queues.
- api-migrate (one-shot): runs
db:pushand the optional superuser seed, then exits. - api-dev (port 7330): Bun + Elysia API with hot reload via bind mount.
- ui-dev (port 7331): Vite dev server; proxies
/api/*toapi-dev.
Traefik runs only in the prod profile. In prod it terminates TLS and
path-routes /api/* plus /health to the api container, everything
else to the ui container, all on one domain.
Common next steps
Section titled “Common next steps”- Regenerate cross-app contracts:
bun run regenfrom the repo root. The api must be on :7330 for the OpenAPI fetch to succeed. - Everything ships wired in dev. Cache (Valkey), real-time SSE notifications, Web Push (VAPID keys auto-generated on first boot), Bull-board, Mailpit, observability, and error tracking are all on by default. Inspect them at: Grafana
http://localhost:3010, GlitchTiphttp://glitchtip.localhost, Bull-boardhttp://bullmq.localhost, Mailpithttp://localhost:8025. See Profiles & overlays for the full default matrix and the off switches. - Production deploy: Deployment covers GHCR images, TLS, firewall, and backups.
Stop the stack
Section titled “Stop the stack”From infra/compose/compose/, ./dev.sh down stops everything and
keeps the volumes on disk. To wipe data as well, run
./scripts/compose-down-clean.sh from infra/compose/; it prompts
before deleting, or set CONFIRM=yes to skip the prompt.
After setup
Section titled “After setup”For account-owned feature work, follow the verified agent workflow and account-resource recipe. The commands give both engineers and agents structured evidence from isolated integration services. Existing installations should also read the security upgrade runbook.
Related
Section titled “Related”- Why BoringStack. What ships in the box.
- Repository layout. Monorepo folders and contracts.
- Environment variables. What loads from where.
- Deployment. Production runtime path.
- Provisioning with OpenTofu.
tofu applyto a live VPS.