Production-ready TypeScript monorepo boilerplate.
Nebula ships with a Postgres + Redis + Drizzle stack, observability, and developer tooling pre-wired across a Turborepo + pnpm workspace, so you can skip the setup grind and start building features on day one. This is the empty variant - no auth, email, or SMS modules included, just the core plumbing.
- Stack Overview
- Dependency Injection Container
- Getting Started
- Environment Setup
- Database Commands
- Testing & Validation
- Monitoring
- Port Map
- Quick Start Summary
| Layer | Technology |
|---|---|
| Monorepo | Turborepo + pnpm workspaces |
| Language | TypeScript |
| Runtime | Node.js |
| Framework | Express |
| Database | PostgreSQL |
| ORM | Drizzle |
| Cache / Queue / Sessions | Redis |
| Validation | Zod (@repo/zod) |
| Logging | Pino + request logger middleware, Winston (for Loki) |
| Rate Limiting | express-rate-limit + Redis-backed store |
| Testing | Vitest + Supertest |
| Dev Tooling | Husky, Commitlint, Prettier, ESLint, tsdown |
| Containers | Docker + Docker Compose |
| Monitoring | Prometheus + Grafana + Loki |
Same pattern as before - no Inversify, no decorators, no reflect-metadata. Plain composition functions, wired per-service:
createContainer()
createServices()
createRepositories()
createValidators()
createMiddlewares()
createControllers()
pnpm installFor local, non-Docker development:
cp apps/server/.env.example apps/server/.envFor Docker-based local/production-like setup:
cp apps/server/.env.example apps/server/.env.production.localImportant: Docker Compose reads
./apps/server/.env.production.localfor the app service (env_file).
pnpm devRuns turbo run dev --concurrency=30. The server starts with tsx --watch ./src/index.ts, listening on PORT from .env (default: 3000).
pnpm buildRun the compiled server:
PORT=3000 node apps/server/dist/index.mjsdocker compose up --buildDetached:
docker compose up -dStop:
docker compose downcp apps/server/.env.example apps/server/.envKey values include:
PORT=3000
NODE_ENV=development
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
# DATABASE_URL="postgresql://postgres:postgres@nebula-postgres:5432/postgres" #docker-compose
REDIS_USERNAME=default
REDIS_PASSWORD=default
REDIS_HOST=localhost
# REDIS_HOST=nebula-redis-server #docker-compose
REDIS_PORT=6379| Variable | Purpose |
|---|---|
PORT |
App server port |
NODE_ENV |
Runtime environment |
DATABASE_URL |
PostgreSQL connection string |
REDIS_USERNAME |
Redis auth username |
REDIS_PASSWORD |
Redis auth password |
REDIS_HOST |
Redis connection host |
REDIS_PORT |
Redis connection port |
pnpm --filter server db:generate
pnpm --filter server db:migrate
pnpm --filter server db:push
pnpm --filter server db:seedpnpm test
pnpm check:types
pnpm check:lintEach runs via Turborepo across all packages, cached and parallelized.
- Grafana, Prometheus, and Loki come up with the Docker Compose stack.
- From
terraform/:
cd terraform
terraform init
terraform plan
terraform apply -auto-approveNote: Uses the Grafana Terraform provider; modules rely on JSON dashboard templates in the repo.
Default Docker Compose UI ports:
| Service | URL | Notes |
|---|---|---|
| Grafana | http://localhost:3005 | Dashboards from Terraform. Default creds: admin:admin |
| Prometheus | http://localhost:9090 | Explore metrics, run ad-hoc queries |
| Loki | http://localhost:3100 | Log aggregation |
| RedisInsight | http://localhost:5540 | Inspect Redis data |
| Service | Address |
|---|---|
| App server | http://localhost:3000 |
| ↳ Health check | http://localhost:3000/health |
| ↳ Metrics | http://localhost:3000/metrics |
| PostgreSQL | localhost:5432 |
| Redis | localhost:6379 |
| RedisInsight | http://localhost:5540 |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3005 |
| Loki | http://localhost:3100 |
Shortest path to running locally:
pnpm install
cp apps/server/.env.example apps/server/.env
pnpm devShortest path to the full Docker stack:
cp apps/server/.env.example apps/server/.env.production.local
docker compose up