First clone
Accounts, API, UI, and Compose boot together.
The first clone includes multi-tenant schema, Stripe webhooks, BullMQ on Valkey, Mailpit overlay, and compose/dev.sh for local Postgres + api-dev + ui-dev.
Architecture
The MIT templates compose the full stack: apps/api (Bun, Elysia, Drizzle), apps/ui (Vite, React, generated OpenAPI client), infra/compose (Postgres, Valkey, Traefik), and optional infra/bootstrap for VPS bootstrap. Auth, billing, queues, email, env validation, and deploy scripts are wired and documented; custom ESLint plugins enforce folder and contract shape in CI.
auth
sessions, OAuth, verification
ops
deploys, logs, backups
lint
ESLint plugins + validate
First clone
The first clone includes multi-tenant schema, Stripe webhooks, BullMQ on Valkey, Mailpit overlay, and compose/dev.sh for local Postgres + api-dev + ui-dev.
OpenAPI
TypeBox routes emit /openapi.json; apps/ui regenerates src/lib/api/schema.d.ts. Drift fails typecheck before review.
Lint
Custom @boring-stack-pkg/eslint-plugin-* rules encode route, service, env, and queue layout. AGENTS.md states intent; ESLint enforces it.
| BoringStack is a good fit if… | It is probably the wrong fit if… |
|---|---|
| You need accounts, auth, billing, email, and background jobs with Postgres + Valkey on your own VPS. | You only need a static site or a no-code prototype with no custom backend. |
| You want Postgres, Valkey, Traefik, and Docker Compose under your control. | You want a serverless-first Vercel/Netlify path where the platform owns runtime decisions. |
| You want API and UI as separate deployable apps with an OpenAPI contract between them. | You prefer a single full-stack framework with one app boundary. |
| You want ESLint rules and validate CI to enforce architecture, including agent edits. | You rely on conventions, README prose, and review alone. |
| You want Compose on one VPS now, with an optional path to managed Postgres or Kubernetes later. | You need Kubernetes, multi-region, or managed-everything infrastructure on day one. |
Auth, sessions, OAuth, email, env validation, audit log, billing hooks, and queues are wired and documented.
OpenAPI at /swagger/json feeds the generated UI client; contract drift fails typecheck instead of shipping.
lintCustom ESLint plugins encode route, service, env, and queue shape. validate is the merge gate.
infraCompose, Traefik TLS, optional OpenTofu, observability, logs, and backups already have a home.
queuesNotification events, dispatch jobs, email, and queues live in the API instead of ad-hoc frontend retries.
Postgres, Valkey, workers, and web traffic can start on one capable VPS before you buy managed services.
The name is deliberate. Boring means proven primitives wired with explicit boundaries.
Postgres, HTTP APIs, browsers, Docker Compose, TLS, Redis-protocol queues: pieces with long production track records. They are linked in a layout you can operate: compose/dev.sh locally, compose/prod.sh on a VPS, OpenTofu optional for first boot.
The inventory of choices is on Stack at a glance. This page explains repo boundaries and default trade-offs.
Many starters defer infra cost until traffic grows. BoringStack defaults to self-hosted Postgres, Valkey, and Traefik so monthly COGS stays tied to VPS size, not platform usage meters.
The core runtime is open source and self-hostable:
That does not mean “never use SaaS.” Stripe, Cloudflare, Sentry, Resend, SendGrid, Neon, Supabase, and other managed services can all make sense. BoringStack does not force your core application onto a platform meter before you have revenue. You can start nearly free on one capable VPS, then buy managed services only where the trade-off is worth it.
Monthly cost across stages
Select a traffic tier. The three columns are solution categories (not named vendors), using public rate cards as of 2026-05. See the methodology page for assumptions and math.
MAU
~2k
req / day
80k
egress
50 GB / mo
DB storage
5 GB
~2k MAU, 80k req/day, moderate egress.
BoringStack
VPS + Cloudflare + B2
$8/ mo
You own the box. You own the pager.
Managed-PaaS bundle
10×Edge functions + managed Postgres
$80/ mo
Vendor lock-in; cold starts; bandwidth meter.
Hosted SaaS platform
25×All-in-one with per-seat / per-event pricing
$200/ mo
Price scales with users, not with usage.
Prices as of 2026-05. Every figure rounded; comparisons use public list-price rate cards, not enterprise negotiations. See the cost methodology page for the math, the assumptions, and the citations.
| Decision point | BoringStack | Typical Vercel/serverless starter | Hosted backend starter | One-off SaaS boilerplate |
|---|---|---|---|---|
| Deployment path | Single-host VPS first; optional OpenTofu bootstrap | Platform-first; VPS path is usually DIY | Hosted service first | Varies by vendor |
| Core data plane | Postgres + Valkey under your control | Usually external services | Hosted database/auth/storage | Varies |
| COGS posture | Open-source core; one VPS can carry the whole app | Usage-based platform bill grows with traffic | Vendor pricing becomes part of the app shape | Varies |
| API/UI contract | OpenAPI generated client | Often framework-coupled or hand-rolled | SDK/client generated by provider | Varies |
| Auth/billing/queues/email | Wired as product infrastructure | Mostly app-specific assembly | Auth often built in; queues/billing/email vary | Often included, quality varies |
| Architecture enforcement | Custom lint plugins and validate gates | Mostly conventions | Mostly provider boundaries | Usually prose and examples |
| Best for | Teams that self-host Postgres/Valkey and want OpenAPI + lint gates | Fast frontend-heavy apps on a managed platform | Apps that fit the provider SDK and hosted DB model | Buying a finished opinionated app shell |
The choice is not moral. BoringStack is intentionally biased toward ownership, explicit boundaries, low recurring infra cost, and code that remains understandable after many human and agent edits.
flowchart TB
subgraph ui [apps/ui]
spa[Vite + React SPA]
end
subgraph api [apps/api]
http[Bun + Elysia API]
jobs[BullMQ workers]
end
subgraph infra [infra/compose]
edge[Traefik TLS routing]
data[(Postgres + Valkey)]
end
spa -->|OpenAPI JSON| http
http --> data
jobs --> data
edge --> spa
edge --> http
Three stacked layers: the apps/ui SPA sits above the apps/api (HTTP server plus BullMQ workers), which sits on the infra layer (Traefik routing in front of Postgres and Valkey). The SPA reaches the API over a typed OpenAPI contract; both the API and the workers read and write the same data plane.
Security, validation, persistence, background jobs, secrets, audit trail. The system of record lives here.
Rendering, interaction, client state, i18n. Vite keeps local feedback fast. The UI calls the API through a generated, typed client.
TLS, routing, databases, queue store, optional metrics and logs. In production, same-origin path routing (/ for the SPA, /api/* for the API) keeps browser security simple. The code boundaries stay separate.
Separation of concerns goes deeper on what each layer delivers and how releases stay independent.
The templates come from codebases that have been in production for many years. File layout, feature anatomy, env discipline, queue patterns, and deploy defaults are decisions you would otherwise make again on every project. You extend what ships. You do not reinvent the spine.
AGENTS.md, CLAUDE.md, and AGENT_CONTRACT.md explain intent. Custom ESLint plugins enforce it. Machine-checked rules survive refactors, new teammates, and agent-generated diffs. They cover where routes live, how env is read, and how queues are shaped. When lint fails, the fix is specific.
Architecture lives in tooling. Prose is context. Read Lint as the contract.
Email, notifications, and other background work already have a home in the API. When volume, team boundaries, or compliance need a dedicated process, you extract a piece and keep the same job contracts. See Background work.