Run the whole platform on your infrastructure.
Concierto is a Go backend + Next.js frontend + Postgres. The whole stack runs in Docker and degrades gracefully when optional services (Stripe, Resend, GitHub OAuth) aren't configured. Use this path when data residency, air-gap, or self-managed compliance is non-negotiable.
What runs where
backend— Go server (Chi router, gorilla/websocket, sqlc-generated DB layer). Owns the API, auth, the daemon claim loop, and the WebSocket fanout.web— Next.js App Router. Proxies/api/*and/wsto the backend; serves the landing, handbook, and workspace UI.postgres— pgvector/pg17. Single database; all tenants live in the same Postgres with row-level filtering byworkspace_id.- Daemon pool (optional) — only needed if you want hosted runtimes for your own users. Most self-host deploys skip this and run agents on user laptops via the Mac app.
Prerequisites
- Docker 24+ with the compose plugin.
- A Postgres-capable host. The sample compose runs pgvector/pg17 in a container; production should use a managed Postgres (RDS, Cloud SQL, Neon, etc.).
- A domain you can point at the web container (Caddy or Nginx in front is fine).
1 · Docker quickstart
Clone the deploy bundle and bring up the stack:
git clone https://github.com/nstack-enterprises/concierto-agent-platform.git
cd concierto-agent-platform/deploy
cp .env.example .env
# edit .env (see step 2 below)
docker compose up -dThe backend runs migrations automatically on first start. Tail the logs to confirm everything is green:
docker compose logs -f backendLook for server starting port=8080 and migrations applied.
2 · Configure environment
Required minimum to boot:
# Where the web app lives
FRONTEND_ORIGIN=https://concierto.acme.internal
ALLOWED_ORIGINS=https://concierto.acme.internal
CORS_ALLOWED_ORIGINS=https://concierto.acme.internal
MULTICA_APP_URL=https://concierto.acme.internal
NEXT_PUBLIC_WS_URL=wss://concierto.acme.internal/ws
# Database
DATABASE_URL=postgres://multica:****@db.acme.internal:5432/multica?sslmode=require
# Auth secret (rotate this; do NOT use the default)
JWT_SECRET=$(openssl rand -base64 48)
# Where the web app talks to the backend internally
REMOTE_API_URL=http://backend:8080Full reference of every supported env var is at deploy/.env.example in the repo.
3 · First admin
Self-host deploys don’t have a magic first-user flow. The cleanest way to bootstrap an admin: sign up through the UI as a normal user, then promote that user to platform_admin via SQL:
UPDATE "user"
SET platform_role = 'platform_admin'
WHERE email = 'you@acme.com';After that, your account has the admin surfaces (cross-workspace usage, feature flag overrides, billing toggle).
Billing (optional)
If you don’t set STRIPE_SECRET_KEY, the billing-related endpoints return 503 and the UI hides the Billing tab. The rest of the product (agents, runtimes, tracks) works fine.
If you do want billing for your own users, configure these in Stripe and add to env:
STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET- Price IDs for the plans:
STRIPE_PRICE_HOSTED_PRO,STRIPE_PRICE_BYOK_TEAM,STRIPE_PRICE_TOPUP_*
Email (optional)
Concierto uses Resend for verification codes and notifications. If RESEND_API_KEYisn’t set, the backend falls back to logging the email body to stdout (handy for development; for production set EMAIL_NOTIFICATIONS_ENABLED=false and gate sign-up behind your own SSO provider).
Updates
Pull the latest images and re-run compose:
docker compose pull
docker compose up -dBackend migrations run on each boot. They’re always additive within a minor version (the schema policy is zero-downtime for patch releases). Major version bumps are called out in CHANGELOG.md with a migration plan.
For air-gapped deploys, mirror the three images (backend, web, pgvector/pgvector:pg17) to your internal registry. Concierto doesn’t phone home — no telemetry, no license check. The only outbound dependency is the model provider your agents call.