A production-ready Go SaaS boilerplate with an explicit, dependency-injected architecture. It ships with everything needed to start a real product: authentication, RBAC, Stripe billing, background jobs, and a server-rendered component-based UI — no globals, no magic wiring.
- HTTP —
chi/v5router with a hardened middleware stack (RealIP, request ID, structured logging, panic recovery, security headers, CSRF, rate limiting, CORS). - Database — PostgreSQL via
pgx/v5pool, type-safe queries generated bysqlc, schema migrations managed bygoose. - Web UI —
templserver-rendered templates, templui components, Datastar for interactivity, and Tailwind CSS. - Auth — JWT access/refresh tokens, server-side session cookies, OAuth (Google & GitHub), and TOTP-based two-factor authentication.
- Authorization — Casbin RBAC with
user/adminroles. - Billing — Stripe checkout, customer portal, subscriptions, and idempotent webhook synchronization.
- Background jobs — Asynq (Redis-backed) worker handling email, cleanup, and billing tasks.
- Ops — health/readiness probes, graceful shutdown, structured
sloglogging, Docker Compose, and CI.
The application is wired together explicitly with dependency injection. A single app.App struct (internal/app) holds all shared dependencies and is constructed once in cmd/api/main.go, then threaded through the router. There are no package-level singletons.
cmd/
api/ HTTP server entry point
worker/ Asynq background-job processor
migrate/ goose migration runner (up/down)
seed/ seeds test users
internal/
app/ central App container (DI)
config/ env-based typed configuration
server/ router + middleware stack + route registration
middleware/ auth context, CSRF, rate limit, security headers, logging
auth/ JWT, sessions, OAuth, TOTP, auth service & handlers
authz/ Casbin RBAC enforcer + model
modules/
users/ user profile & admin management (types/service/handlers)
billing/ Stripe checkout, portal, webhooks
queue/ Asynq client/server + task type constants
database/ pgx pool construction
sqlc/ GENERATED type-safe queries (do not edit)
observability/ logging setup + health handlers
email/ SMTP sender
errors/ shared error types
sql/queries/ sqlc query definitions (source)
migrations/ goose SQL migrations (schema source of truth)
tasks/ background job handlers (email, cleanup, billing)
web/
templates/ .templ sources + generated *_templ.go
static/ static assets and built CSS/JS
content/docs/ markdown documentation rendered at /docs
deployments/ Dockerfile + docker-compose.yml
Requests flow through layers: handlers (HTTP, validation, response shaping) → services (business logic) → sqlc.Queries (data access). The codebase exposes two surfaces in parallel — a JSON API under /api/v1/* and server-rendered web pages.
Prerequisites:
- Go 1.25+
- Docker / Docker Compose
sqlcCLI,templCLIgolangci-lint(formake check)
cp .env.example .env # JWT_SECRET and SESSION_SECRET are required
make setup # docker-up + migrate-up + generate + css + seed
make run # start the API serverThen open:
- App:
http://localhost:8080 - Showcase:
http://localhost:8080/showcase - Docs:
http://localhost:8080/docs
To exercise background jobs, run the worker in a second terminal:
go run ./cmd/workerSeeded test accounts (password password): admin@test.com (admin) and user@test.com (user).
- Marketing:
/,/features,/pricing - Docs:
/docs,/docs/{slug},/docs/{section}/{slug},/docs/components/{name} - Auth pages:
/login,/signup,/forgot-password,/reset-password,/verify-email - App (authenticated):
/dashboard,/settings,/settings/profile,/settings/billing,/settings/security - Admin (role-gated):
/admin,/admin/users,/admin/users/{id} - OAuth:
/auth/callback/{provider} - Stripe webhook:
/webhooks/stripe(raw body, no auth/CSRF) - Health:
/healthz,/readyz - JSON API:
/api/v1/auth/*,/api/v1/users/*,/api/v1/billing/*
All configuration is read from environment variables and parsed into typed structs in internal/config/config.go. See .env.example for the full list. JWT_SECRET and SESSION_SECRET are required; most other values have sensible development defaults. Key groups: app/HTTP, database pool, Redis, auth (JWT/session/bcrypt), OAuth (Google/GitHub), Stripe, SMTP email, and queue concurrency.
make generate # regenerate sqlc + templ
make css # build Tailwind CSS
make test # go test ./... -race
make lint # golangci-lint
make check # lint + race tests + regenerate (drift-sensitive)
make build # build api, worker, migrate binaries into bin/
make dev # hot reload via air (falls back to go run)internal/sqlc/*.go, web/templates/**/*_templ.go, and web/static/css/style.css are generated — edit the sources (sql/queries/, .templ files, migrations/) and regenerate. make check fails if regenerated output drifts from what's committed, so run it before committing.
make migrate-create name=add_something # new goose migration
make migrate-up # apply migrations
make migrate-down # roll back
make seed # seed test usersThe schema lives in migrations/ (source of truth for both the DB and sqlc). After changing schema or queries, run make sqlc.
make docker-up # start Postgres + Redis (deployments/docker-compose.yml)
make docker-build # build images
make docker-down # stop- Single-tenant architecture.
- CORS origins are controlled by
CORS_ALLOWED_ORIGINS. - Background jobs are processed by
cmd/worker; the API and worker are separate processes. - The session cookie
Secureflag follows TLS /X-Forwarded-Proto. - Stripe webhook idempotency is enforced via the
webhook_eventstable. - Casbin policies are in-memory by default; swap the string adapter for a DB-backed adapter in production.
See AGENTS.md for contributor and AI-agent conventions.
Released under the MIT License.