Quick start
Two ways to get a local instance: the published image (nothing to build) or the source tree with hot reload.
Option A — Docker Compose (published image)
Create the environment file
git clone https://github.com/circle-rd/auth-service.git
cd auth-service
cp .env.example .env
Edit .env for a local stack:
DOMAIN=localhost
PUBLIC_SCHEME=http
BETTER_AUTH_URL=http://localhost:3001
BETTER_AUTH_SECRET=<output of: openssl rand -base64 32>
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=<at least 12 characters, not the .env.example placeholder>
POSTGRES_PASSWORD=<any value>
Start the stack
docker compose pull
docker compose up -d
PostgreSQL starts first, then auth-service applies its migrations, creates the superadmin and listens on http://localhost:3001.
Verify
curl http://localhost:3001/health
# → {"status":"ok","version":"<x.y.z>"}
curl http://localhost:3001/.well-known/openid-configuration
Open http://localhost:3001/auth and sign in with ADMIN_EMAIL and ADMIN_PASSWORD:

Then continue with Managing applications to register your first OAuth client.
Option B — from source (development)
Prerequisites: Node.js 22+, pnpm 10+, Docker.
Start PostgreSQL only
Compose reads .env and requires BETTER_AUTH_SECRET for the whole file, so create .env first (see option A).
pnpm install
docker compose up -d postgres
Configure the environment
pnpm dev reads .env without variable expansion, so write final values (the ${DOMAIN} references of .env.example are only expanded by Docker Compose):
BETTER_AUTH_URL=http://localhost:3001
DATABASE_URL=postgres://auth:<POSTGRES_PASSWORD>@localhost:5433/<POSTGRES_DB>
Keep ADMIN_EMAIL, ADMIN_PASSWORD and BETTER_AUTH_SECRET as in option A.
Apply the schema
In development the server does not migrate by itself, so apply the migrations once and after each schema change:
pnpm db:migrate
Start the backend
pnpm dev
The server restarts on every change (tsx watch) and listens on http://localhost:3001. The superadmin is created on first boot.
Start the admin frontend (optional)
cd frontend
pnpm install
pnpm dev
Vite serves the Vue admin SPA with hot reload on http://localhost:5173 and proxies API calls to port 3001. To work on the UI without any backend, run VITE_USE_MOCK=true pnpm dev: every API call is answered with fictional demo data.
Without the Vite server, the backend serves the SPA itself from frontend-dist/ once you ran pnpm build:frontend.
Available scripts
| Script | Description |
|---|---|
pnpm dev | Backend with hot reload |
pnpm build | Backend TypeScript and frontend SPA |
pnpm build:server / pnpm build:frontend | Only the backend, or only the frontend |
pnpm start | Run the compiled backend (dist/) |
pnpm db:generate | Generate a Drizzle migration from schema changes |
pnpm db:migrate | Apply pending migrations |
pnpm db:push | Push the schema without migration files (throwaway databases only) |
pnpm test | Unit tests (Vitest) |
pnpm test:integration | Integration tests (an ephemeral PostgreSQL container is started by testcontainers) |
pnpm test:e2e | Playwright end-to-end tests |
pnpm lint / pnpm format:check | ESLint and Prettier checks |
pnpm llm:setup, llm:provision, llm:smoke, llm:reconcile | LiteLLM gateway tooling, see LiteLLM gateway |