Full-stack monorepo for learning DevOps practices: Turborepo, Express API, React web app, PostgreSQL Primary-Secondary-Secondary replication, and a complete observability stack.
| Layer | Technology |
|---|---|
| Monorepo | pnpm + Turborepo |
| API | Node.js, TypeScript, Express, Vitest |
| Web | Vite, React, TypeScript |
| Database | PostgreSQL 16 (1 primary + 2 read replicas) |
| Tracing | OpenTelemetry → Collector → Jaeger |
| Metrics | OpenTelemetry → Collector → Prometheus |
| Logs | pino JSON → Filebeat → Logstash → Elasticsearch → Kibana |
- Node.js 20+
- pnpm 9+
- Docker & Docker Compose
- ~8 GB RAM recommended for the full stack (ELK + 3 Postgres instances)
cp .env.example .env
pnpm install
pnpm docker:upWait for all services to become healthy (replicas can take 1–2 minutes on first boot).
| Service | URL |
|---|---|
| Web (nginx) | http://localhost:8088 |
| API | http://localhost:3000 |
| API health | http://localhost:3000/health |
| API readiness | http://localhost:3000/ready |
| Jaeger UI | http://localhost:16686 |
| Prometheus | http://localhost:9090 |
| Kibana | http://localhost:5601 |
| Elasticsearch | http://localhost:9200 |
| Mailpit (dev email) | http://localhost:8025 |
Runs infrastructure in Docker; API and web use volume mounts with hot reload:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build- Web dev server: http://localhost:5173
- API: http://localhost:3000
Start only infrastructure:
docker compose up -d postgres-primary postgres-replica-1 postgres-replica-2 otel-collector jaeger prometheus elasticsearch logstash kibana filebeat
cp .env.example .env
pnpm install
pnpm --filter @repo/shared build
pnpm --filter api db:migrate
pnpm devpnpm --filter api test:watch # API tests
pnpm --filter web test:watch # Web tests
pnpm test # All packages
pnpm deploy:check # lint + typecheck + test + buildGET /health→ 200GET /ready→ 200/503 based on DB health- 404/400/500 error shapes
- Request ID header propagation
GET /genres→ 200, seeded genre list (unit tests mock DB)
Integration tests (apps/api/tests/integration/) query the real database and require migrated seed data. They run automatically with pnpm --filter api test when Postgres is available. Skip them in CI or environments without a database:
INTEGRATION_TEST=0 pnpm --filter api testBrowser → Web (nginx) → API → PostgreSQL Primary (writes)
→ PostgreSQL Replicas (reads)
→ OTel Collector → Jaeger / Prometheus
→ JSON logs → Filebeat → Logstash → Elasticsearch → Kibana
See .env.example. Key variables:
DATABASE_PRIMARY_URL— write connectionDATABASE_REPLICA_1_URL/DATABASE_REPLICA_2_URL— read connectionsOTEL_EXPORTER_OTLP_ENDPOINT— OpenTelemetry collectorLOG_LEVEL— pino log level
- Open http://localhost:5601
- Create index pattern:
logs-* - Search by
trace_idorrequestIdto correlate logs with Jaeger traces
Another process is using the host port. Either stop it:
ss -tlnp | grep 8080
# or set a different host port:
WEB_PORT=8090 pnpm docker:upDefault web port is 8088 (not 8080) to reduce conflicts with other local dev servers.
Another Redis (or service) is bound to 6379. The compose file maps Redis to host port 6380 by default:
ss -tlnp | grep 6379
# or override the host port:
REDIS_PORT=6381 pnpm docker:upSet REDIS_URL=redis://localhost:6380 in .env when connecting from the host (e.g. pnpm --filter api dev).
Replicas run pg_basebackup on first start. Check logs:
docker compose logs postgres-replica-1 postgres-replica-2Restart after primary is healthy:
docker compose restart postgres-replica-1 postgres-replica-2ELK is memory-heavy. Ensure Docker has ≥8 GB RAM. Restart:
docker compose restart elasticsearch logstash kibanaVerify collector is running:
docker compose logs otel-collector apiRequires primary + at least one replica in recovery mode. Wait for replica healthchecks or inspect:
docker compose pspnpm docker:down # removes volumes
pnpm docker:updocker compose exec postgres-primary psql -U app -d arektaMovie reservation features are built one API module at a time using test-driven development:
- Read the current phase plan in local
Phases/(gitignored — personal workflow docs) - Write failing tests → implement →
pnpm --filter api test - Verify with Postman: import
postman/arekta-ticketmaster.postman_collection.json - Run
pnpm deploy:check, then commit and push before starting the next module
Module order: Foundation → Auth → Genres → Movies → Showtimes → Seats → Reservations → Payments → Notifications → Admin → Web UI.
apps/api/ Express API
apps/web/ React frontend
packages/shared/ Shared types (Zod schemas)
docker/ Postgres, OTel, Prometheus, ELK configs
postman/ Postman collection (versioned)
Phases/ Local TDD phase plans (gitignored)
- PostgreSQL PSS setup is for local learning — no automatic failover (Patroni not included).
- Writes go to primary; reads round-robin across replicas.
- Render deployment is a separate follow-up after MVP validation locally.