144 lines
6.7 KiB
Markdown
144 lines
6.7 KiB
Markdown
# 06 — Deployment
|
||
|
||
> **Purpose:** Condensed infrastructure reference for LLMs working on deployment, CI/CD, or ops tasks. For full setup details (VPS provisioning, Forgejo configuration, backup scripts), see the human-readable DEPLOYMENT.md.
|
||
> **Last updated:** 2026-05-15
|
||
> **Depends on:** 00-project-overview.md
|
||
|
||
---
|
||
|
||
## Infrastructure Overview
|
||
|
||
```
|
||
Internet
|
||
↓
|
||
Caddy (Docker container, ports 80/443)
|
||
├── lilastudy.com → web container (nginx:alpine, static files)
|
||
├── api.lilastudy.com → api container (Express, port 3000)
|
||
└── git.lilastudy.com → forgejo container (git + registry, port 3000)
|
||
|
||
SSH (port 2222) → forgejo container (git push/pull)
|
||
```
|
||
|
||
**VPS:** Hetzner, Debian 13, ARM64 (aarch64), 4GB RAM
|
||
**Domain:** lilastudy.com, wildcard `*.lilastudy.com` configured
|
||
**Only Caddy faces the internet.** All other services communicate over internal Docker network.
|
||
|
||
---
|
||
|
||
## Docker Compose Stack
|
||
|
||
Services on shared `lila-network`:
|
||
|
||
| Service | Image | Ports (internal) | Notes |
|
||
| -------- | ------------------------------------------------ | ---------------- | -------------------------------------------------- |
|
||
| caddy | caddy:alpine | 80, 443 | Only container with published ports |
|
||
| api | `git.lilastudy.com/forgejo-lila/lila-api:latest` | 3000 | Multi-stage Dockerfile, runs migrations on startup |
|
||
| web | `git.lilastudy.com/forgejo-lila/lila-web:latest` | 80 | nginx:alpine, SPA fallback via try_files |
|
||
| database | postgres:16 | 5432 | Named volume `lila-db` for persistence |
|
||
| forgejo | forgejo:... | 3000, 2222 | Git + container registry, SSH on 2222 |
|
||
|
||
**No ports exposed on internal services.** Only Caddy (80/443) and Forgejo SSH (2222) are public.
|
||
|
||
---
|
||
|
||
## Build & Deploy Flow
|
||
|
||
```
|
||
Dev laptop: git push to main
|
||
↓
|
||
Forgejo Actions triggers (runner on VPS)
|
||
↓
|
||
Build API image (target: runner)
|
||
Build Web image (target: production, VITE_API_URL baked in)
|
||
↓
|
||
Push both to git.lilastudy.com registry
|
||
↓
|
||
SSH into VPS, docker compose pull, restart containers
|
||
↓
|
||
API container runs migrations on startup (migrate.js before server.js)
|
||
↓
|
||
App updated (~2–5 min total)
|
||
```
|
||
|
||
**Cross-compilation:** Images built natively on ARM64 VPS (no QEMU). Dev laptop used for initial pushes before CI/CD was set up.
|
||
|
||
---
|
||
|
||
## Environment-Driven Config
|
||
|
||
Same code runs in dev and production. Environment variables control behavior:
|
||
|
||
| Variable | Dev | Production |
|
||
| ----------------- | ------------------------------------ | ------------------------------------------------- |
|
||
| `DATABASE_URL` | `postgres://...@localhost:5432/lila` | `postgres://...@database:5432/lila` |
|
||
| `BETTER_AUTH_URL` | `http://localhost:3000` | `https://api.lilastudy.com` |
|
||
| `CORS_ORIGIN` | `http://localhost:5173` | `https://lilastudy.com` |
|
||
| `COOKIE_DOMAIN` | undefined | `.lilastudy.com` |
|
||
| `VITE_API_URL` | `http://localhost:3000` | `https://api.lilastudy.com` (baked at build time) |
|
||
|
||
**Note:** `VITE_API_URL` is baked into the frontend at Docker build time via `--build-arg`. It cannot be changed at runtime.
|
||
|
||
---
|
||
|
||
## Database
|
||
|
||
### Migrations
|
||
|
||
Drizzle migrations run automatically on API container startup. The Dockerfile entrypoint:
|
||
|
||
```dockerfile
|
||
CMD ["node", "dist/src/migrate.js", "&&", "node", "dist/src/server.js"]
|
||
```
|
||
|
||
**Deploy order enforced automatically:** migrations before server starts.
|
||
|
||
### Backups
|
||
|
||
- Daily cron job at 3:00 AM: `pg_dump` → compressed SQL → `~/backups/`
|
||
- 7-day retention on VPS
|
||
- Dev laptop auto-syncs new backups on login via `rsync`
|
||
- **Offsite storage:** Planned (Hetzner Object Storage or S3-compatible)
|
||
|
||
### Seeding
|
||
|
||
Idempotent (`onConflictDoNothing`). Safe to re-run for adding new languages without affecting existing data or user tables.
|
||
|
||
---
|
||
|
||
## Auth & OAuth
|
||
|
||
**Better Auth** embedded in Express API. No separate auth service.
|
||
|
||
**Social providers:**
|
||
|
||
- Google OAuth — consent screen in testing mode (100 user cap). Must publish before reaching 80 users.
|
||
- GitHub OAuth — configured for both dev and production redirect URIs
|
||
|
||
**Cross-subdomain cookies:** `COOKIE_DOMAIN=.lilastudy.com` (leading dot) makes auth cookie valid across all subdomains.
|
||
|
||
---
|
||
|
||
## Known Issues & Limitations
|
||
|
||
| Issue | Impact | Status |
|
||
| --------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------- |
|
||
| lila-web has no healthcheck | Vite dev server has no health endpoint; `depends_on` uses API healthcheck as proxy | Acceptable for dev |
|
||
| Valkey memory overcommit warning | Harmless in dev. Fix before production: `vm.overcommit_memory = 1` | Documented |
|
||
| No centralized monitoring/logging | No uptime alerts or log aggregation on VPS | Planned (BACKLOG.md) |
|
||
| Backups only on VPS + dev laptop | No offsite protection against VPS failure | Planned (BACKLOG.md) |
|
||
| Google OAuth in testing mode | 100 user cap | Must publish before 80 users |
|
||
|
||
---
|
||
|
||
## Key Files
|
||
|
||
| File | Purpose |
|
||
| ------------------------------- | ------------------------------------------------------ |
|
||
| `docker-compose.yml` (root) | Local dev stack |
|
||
| `docker-compose.yml` (VPS) | Production stack |
|
||
| `apps/api/Dockerfile` | Multi-stage: deps → dev → builder → runner |
|
||
| `apps/web/Dockerfile` | Multi-stage: deps → dev → builder → production (nginx) |
|
||
| `apps/web/nginx.conf` | SPA fallback routing |
|
||
| `Caddyfile` | Reverse proxy routing, automatic HTTPS |
|
||
| `.forgejo/workflows/deploy.yml` | CI/CD pipeline |
|
||
| `apps/api/src/migrate.ts` | Drizzle migration runner |
|