My App

Environnements

Dev local, staging, prod — Docker Compose, variables d'environnement, domaines

Un seul docker-compose.yml à la racine pour toute l'infra locale (Postgres, Redis, Signoz, Reacher, RustFS). Profiles Docker pour lancer seulement ce dont on a besoin.

Les 3 environnements

EnvDBRedisMonitoringUsage
devlaptop, Docker DesktopPostgres localRedis localSignoz local (optionnel)développement quotidien
stagingDokploy sur VPS stagingPostgres dédiéRedis dédiéSignoz stagingQA, démo client
prodDokploy sur VPS prodPostgres HARedis persistentSignoz produtilisateurs réels

Dev local

Pré-requis

  • Bun 1.3+ (bun.sh)
  • Docker Desktop (ou Colima)
  • Node 20+ (pour certains outils comme drizzle-kit)
  • ~8 GB RAM libre si on lance Signoz (ClickHouse mange)

Démarrage

# 1. Installer les deps
bun install

# 2. Copier les fichiers .env d'exemple
cp apps/server/.env.example apps/server/.env
cp apps/pwa/.env.example apps/pwa/.env      # à créer quand apps/pwa existera
cp apps/dashboard/.env.example apps/dashboard/.env

# 3. Lancer l'infra minimale (Postgres + Redis)
bun db:start

# 4. Push le schema Drizzle
bun db:push

# 5. Lancer les apps en dev
bun dev                # web + server + docs

Profiles Docker

Le docker-compose.yml racine utilise des profiles pour ne pas tout lancer systématiquement :

ProfileServicesCommandeQuand l'utiliser
dbpostgres, redisbun db:startDev quotidien — requis
validatorsreacherbun infra:startQuand on teste un flow qui envoie des emails
monitoringsignoz-* (ClickHouse + Collector + UI + Alertmanager)bun infra:monitoringQuand on teste l'observabilité — gourmand en RAM
storagerustfsbun infra:storageQuand on touche aux uploads

Scripts raccourcis racine :

bun db:start          # postgres + redis (profile db)
bun infra:start       # db + validators (stack dev courant)
bun infra:monitoring  # ajoute Signoz
bun infra:storage     # ajoute RustFS
bun infra:full        # tout d'un coup
bun infra:stop        # stop sans supprimer les volumes
bun infra:down        # stop + supprime containers (garde volumes)
bun infra:clean       # down + supprime volumes (⚠️ perte données)

Endpoints locaux

ServiceURLCredentials
Postgreslocalhost:5432bell / bell_dev_password / db bell
Postgres (test DB)localhost:5432bell / bell_dev_password / db bell_test
Redislocalhost:6379pas de password en dev
Server (Elysia)http://localhost:3000
OpenAPI / Swaggerhttp://localhost:3000/openapi
Web / Dashboardhttp://localhost:3001 (split pwa/dashboard à venir)
Fumadocshttp://localhost:4000
Reacherhttp://localhost:8080
Signoz UIhttp://localhost:3301
RustFS Consolehttp://localhost:9001bell / bell_dev_password
RustFS S3http://localhost:9000idem
Drizzle Studiobun db:studio ouvre un browser

Fichier .env du server

Exemple pour apps/server/.env :

apps/server/.env
# ─── Core ─────────────────────────────────────────────────────
PORT=3000
NODE_ENV=development

# ─── Database ─────────────────────────────────────────────────
DATABASE_URL=postgresql://bell:bell_dev_password@localhost:5432/bell
DATABASE_TEST_URL=postgresql://bell:bell_dev_password@localhost:5432/bell_test

# ─── Redis (BullMQ + SSE pub/sub) ─────────────────────────────
REDIS_URL=redis://localhost:6379

# ─── Auth (Better Auth) ───────────────────────────────────────
BETTER_AUTH_SECRET=<generate via `openssl rand -base64 32`>
BETTER_AUTH_URL=http://localhost:3000
CORS_ORIGIN=http://localhost:3001,http://localhost:3002

# ─── AI providers (cf. ADR-10) ────────────────────────────────
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
AI_MODEL_CONCIERGE=claude-haiku-4-5
AI_MODEL_CONCIERGE_STRONG=claude-sonnet-4-6
AI_MODEL_AUXILIARY=gpt-4o-mini
AI_MODEL_EMBEDDINGS=text-embedding-3-small
AI_PROMPT_CACHING_ENABLED=true
AI_RAG_GLOBAL_ENABLED=false

# ─── Stripe ────────────────────────────────────────────────────
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# ─── SMTP (Gmail Workspace) ───────────────────────────────────
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=noreply@hoaiy.com
SMTP_PASS=<app password>
SMTP_FROM=Bell <noreply@hoaiy.com>

# ─── Reacher (email validation) ───────────────────────────────
REACHER_ENABLED=true
REACHER_URL=http://localhost:8080

# ─── OpenTelemetry → Signoz ───────────────────────────────────
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
DEPLOYMENT_ENV=development

# ─── Feature flags ────────────────────────────────────────────
OPENAPI_ENABLED=true

Validation : toutes les env vars sont validées au boot par @bell/env (Zod). Si une variable est manquante ou mal formée, le serveur refuse de démarrer avec un message clair.

Fichier .env des fronts (à venir)

apps/pwa/.env
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
NEXT_PUBLIC_APP_URL=http://localhost:3001

Les fronts utilisent le proxy Next.js rewrites (cf. ADR-02) pour éviter le cross-origin en dev.

Staging

VPS : un VPS chez Hostinger/Proxmox, même stack que prod mais data isolée. Branchement Dokploy.

  • Domaines : staging-app.hoaiy.com, staging-staff.hoaiy.com, staging-api.hoaiy.com, staging-docs.hoaiy.com
  • Cookies : domain: .hoaiy.com partagé prod/staging → attention, on isole via sameSite + sub-domaines dédiés
  • Postgres / Redis dédiés (containers séparés de la prod)
  • Signoz : instance séparée ou partagée avec tag deployment.environment=staging

Promotion dev → staging

Automatique via GitHub Actions : push sur main → build Docker → push vers registry → Dokploy webhook → deploy.

Seed staging

Seed identique à la prod au reset (scénarios de démo). Mews connecté à la sandbox.

Prod

VPS : Proxmox Hostinger (Larnaca, Chypre — choix RGPD). Dokploy orchestre les containers.

Domaines

AppProd
PWA Guestbell-app.hoaiy.com
Dashboardbell-staff.hoaiy.com
APIbell-api.hoaiy.com
Docsbell-docs.hoaiy.com (basic auth)
Signoz UIbell-obs.hoaiy.com (basic auth + IP allowlist)

DNS : wildcard *.hoaiy.com → Traefik reverse proxy.

Cert TLS

Let's Encrypt via Traefik, auto-renouvelé.

Secrets prod

Aucun secret en plain text dans le repo. Les .env prod sont injectés au deploy via Dokploy (dashboard UI) qui les écrit dans un volume chiffré. Les valeurs sensibles (STRIPE_SECRET_KEY, BETTER_AUTH_SECRET, DB passwords) sont uniques prod ≠ staging ≠ dev.

docker-compose.prod.yml

Le fichier prod (docker-compose.prod.yml) est un overlay du docker-compose.yml dev :

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Il ajoute :

  • Les services apps (server, worker, pwa, dashboard, fumadocs) avec builds
  • Labels Traefik pour le routing + TLS
  • Overrides infra (passwords, pas d'exposition de ports, Redis require pass)

Backups

  • Postgres : dump quotidien via cron sur l'hôte → upload vers Cloudflare R2 (S3 compatible), retention 30 jours.
  • Redis : snapshot AOF persistent sur volume Docker (pas de perte BullMQ si restart).
  • Uploads RustFS : réplication vers R2.
  • Signoz ClickHouse : retention 30 jours en interne, pas de backup long terme (observabilité = éphémère).

Monitoring des backups

Un job cron envoie une alerte Signoz si un dump Postgres n'a pas tourné dans les 26 dernières heures.

Rollback

  • Code : Dokploy rollback vers l'image précédente en un clic.
  • Schema DB : via migrations Drizzle réversibles (drizzle-kit migrate:rollback).
  • Data : restore depuis le dump R2 si besoin (temps de restore estimé : ~15 min pour 10 GB).

Variables d'environnement — référence

Voir packages/env/src/server.ts et packages/env/src/web.ts pour le schema Zod complet. Tout changement de schema = update simultané des .env.example.

Catégories :

CatégorieVariables
CorePORT, NODE_ENVserver, worker
DatabaseDATABASE_URL, DATABASE_TEST_URLserver, worker
RedisREDIS_URLserver, worker
AuthBETTER_AUTH_SECRET, BETTER_AUTH_URL, CORS_ORIGINserver
AIANTHROPIC_API_KEY, OPENAI_API_KEY, AI_MODEL_*, AI_PROMPT_CACHING_ENABLEDserver, worker
StripeSTRIPE_*server, worker
MailSMTP_*server, worker
ReacherREACHER_ENABLED, REACHER_URLserver, worker
OpenTelemetryOTEL_EXPORTER_OTLP_ENDPOINT, DEPLOYMENT_ENVtoutes les apps
Feature flagsOPENAPI_ENABLED, autresserver
FrontsNEXT_PUBLIC_SERVER_URL, NEXT_PUBLIC_APP_URLpwa, dashboard

Debug par environnement

Dev qui ne démarre pas

Checklist :

  1. bun db:start a-t-il bien démarré ? docker ps doit montrer bell-postgres healthy.
  2. .env présent dans apps/server/ ? Au besoin cp apps/server/.env.example apps/server/.env.
  3. Schema pushé ? bun db:push doit finir sans erreur.
  4. Port 3000 libre ? lsof -i :3000 → si pris, tuer le process.

Prod qui lag

  1. Signoz → dashboard API par module, voir si un module spike la p95.
  2. Bull Board (bell-obs.hoaiy.com/queues) → y a-t-il des queues qui s'empilent ?
  3. Postgres pg_stat_activity → y a-t-il une requête qui tourne longtemps ?
  4. Redis CLIENT LIST → connexions normales (SSE + BullMQ + app) ?

Cf. monitoring pour les runbooks détaillés.

Lien avec les autres pages

On this page