# 33 — Produzione: Docker Compose e Caddy Leggi `CLAUDE.md`. ## Obiettivo Deploy completo sulla VPS con sottodomini sullo stesso dominio. ## Cosa fare - Dockerfile multi-stage per `apps/api`, `apps/public` (server SSR Node), `medusa`; `apps/private` buildata come statico e servita da Caddy. - `docker-compose.prod.yml`: postgres, redis, medusa, api, public, caddy. Rete interna; solo Caddy espone 80/443. Volumi: dati Postgres, uploads (condiviso api/caddy), file Medusa, log. - `Caddyfile` con dominio da variabile (`{$DOMAIN}`): - `{$DOMAIN}` e `www` → `public` (redirect www → root); - `area.{$DOMAIN}` → statico `apps/private` con fallback SPA a `index.html`; - `api.{$DOMAIN}` → `api`; - `shop-admin.{$DOMAIN}` → dashboard Medusa, protetto da basic auth e/o whitelist IP; - `media.{$DOMAIN}` → sola cartella `uploads/public` (mai `private`); - Medusa Store API **non** esposta pubblicamente, tranne l'endpoint webhook Stripe; - header di sicurezza (HSTS, X-Content-Type-Options, Referrer-Policy, CSP di base compatibile con Stripe), compressione, cache lunga per asset con hash. - Script npm `deploy:build`, `deploy:up`, `db:migrate:prod` e procedura di backup giornaliero (dump di entrambi i database + cartella uploads) con rotazione, da integrare con il backup già presente sulla VPS. - Documenta i record DNS necessari. ## Criteri di accettazione - `docker compose -f docker-compose.prod.yml up -d` su una macchina pulita porta su tutti i servizi con HTTPS automatico. - `api.{$DOMAIN}/health` → 200; area privata ricarica correttamente qualunque route (fallback SPA). - I file in `uploads/private` non sono raggiungibili via URL. - Dashboard Medusa non accessibile senza credenziali/IP consentito. - Ripristino da backup provato e documentato. - `CLAUDE.md` aggiornato con la procedura di deploy.