Reciprok Docs

Environnements

4 environnements, Local, Preprod, Prod, Training. Tous self-host via Dokploy

4 environnements bien séparés pour développer en confiance, valider en preprod, livrer en prod, et entraîner l'IA sans polluer les données réelles.

Vue d'ensemble

EnvHébergementDonnéesURLsBranche
LocalDocker Compose (docker-compose.dev.yml)Seed fakerhttp://localhost:3001 (web), :3000 (api)n'importe
PreprodDokploy (Hostinger VPS), docker-compose.preprod.ymlSnapshot prod via dump shareableapp.preprod.reciprok.co (web), api.preprod.reciprok.co (api)develop
ProdDokploy (Hostinger VPS), docker-compose.prod.ymlRéellesapp.reciprok.co (web), api.reciprok.co (api), monitoring.reciprok.co (Signoz)main
TrainingDokploy (Hostinger)Snapshot prod nightlytraining.reciprok.comain

Aucun environnement n'utilise de service tiers managé pour la base ou l'app (pas de Neon, Supabase, Vercel, etc.). Toute l'infra tourne sur Hostinger VPS + Dokploy.

Local

Stack Docker Compose

docker-compose.dev.yml
services:
  postgres:
    image: pgvector/pgvector:pg18
    environment:
      POSTGRES_USER: reciprok
      POSTGRES_PASSWORD: reciprok
      POSTGRES_DB: reciprok
    ports: ["5432:5432"]
    volumes:
      - postgres-data:/var/lib/postgresql/data

  rustfs:
    image: rustfs/rustfs:latest
    ports: ["9000:9000", "9001:9001"]
    volumes:
      - rustfs-data:/data
      - rustfs-logs:/logs

  mailcatcher:
    image: dockage/mailcatcher
    ports: ["1025:1025", "1080:1080"]

volumes:
  postgres-data:
  rustfs-data:
  rustfs-logs:
  • Postgres : pgvector en local, données seed via bun db:seed
  • RustFS : alternative S3-compatible à MinIO, écrite en Rust, drop-in remplacement, plus performant et plus léger. Évite de polluer le bucket R2 prod. Console à localhost:9001 (creds par défaut rustfsadmin / rustfsadmin)
  • Mailcatcher : capture les emails sortants, les rend visibles à localhost:1080

Variables d'env

.env.local (ignoré git) :

DATABASE_URL=postgres://reciprok:reciprok@localhost:5432/reciprok
S3_ENDPOINT=http://localhost:9000
S3_BUCKET=reciprok-dev
S3_ACCESS_KEY_ID=rustfsadmin
S3_SECRET_ACCESS_KEY=rustfsadmin
S3_FORCE_PATH_STYLE=true
SMTP_HOST=localhost
SMTP_PORT=1025
ANTHROPIC_API_KEY=sk-ant-...     # vraie clé en dev (pas d'alternative locale viable)
OPENAI_API_KEY=sk-...
WEB_ORIGIN=http://localhost:3000

Les clés API IA sont les vraies (Anthropic, OpenAI). Les coûts en dev sont minimes (compteur en place dès le jour 1).

Commandes

bun docker:up      # démarre Postgres + RustFS + Mailcatcher
bun db:migrate     # applique les migrations Drizzle
bun db:seed        # peuple avec ~50 membres et 5 demandes fictives
bun dev            # lance server (Elysia) + web (Next.js) en parallèle (turbo)

Preprod

Objectif

Valider les déploiements avant prod, tester sur des données réalistes mais anonymisées.

Spécificités

  • Image Docker : apps/{server,web}/Dockerfile reconstruites à chaque deploy par Dokploy depuis la branche develop. Pas de registry intermédiaire.
  • Base de données : Postgres self-host (service postgres du compose preprod), pgvector inclus. Schéma identique à prod.
  • Données : seed manuel via db:import d'un dump shareable produit par un admin (SHAREABLE=1 bun run db:export), pas de pipeline d'anonymisation auto pour l'instant.
  • Storage : RustFS self-host (service rustfs), bucket reciprok-preprod.
  • Email : Resend Pro avec RESEND_DEV_TO qui redirige tous les envois vers une adresse de test (eviter de spam les vrais destinataires).
  • WhatsApp : numéro de test Meta Cloud (pas de message réel sortant).
  • Domaines : app.preprod.reciprok.co (web) + api.preprod.reciprok.co (api). Routage via Dokploy UI → labels Traefik injectés automatiquement.

Pipeline de déploiement

Validation manuelle obligatoire avant tout merge sur main.

CI smart, Nx affected

Le job principal affected du workflow ci.yml n'exécute que les check-types, tests et builds des projets impactés par le diff entre la branche cible et HEAD. Concrètement :

  • Une PR qui ne touche que apps/web → seuls le check-types, les tests et le build de web (et de ses dépendances internes) tournent
  • Une PR qui ne touche que apps/fumadocs → seuls les jobs de fumadocs
  • Une PR qui touche packages/db → tout ce qui dépend de la DB est rejoué (server, web, workers)
  • Un push sur main → recalcul à partir du dernier commit "successful"

Ça repose sur :

  • nrwl/nx-set-shas@v4 qui calcule NX_BASE et NX_HEAD automatiquement selon le contexte (PR ou push)
  • nx affected -t <task> qui filtre les projets impactés via le graphe de dépendances Nx
  • Un cache Nx local persisté entre runs (clé bun.lock + SHA)

Avantage : un changement ciblé sur le frontend ne bloque pas une heure de pipeline pour rejouer le backend qui n'a pas bougé.

Prod

Objectif

L'environnement réel utilisé par l'équipe Reciprok et les organisateurs.

Spécificités

  • Image Docker : reconstruite par Dokploy depuis la branche main (déploiement manuel uniquement, pas d'autodeploy On Push).
  • Base de données : Postgres self-host (service postgres du compose prod) + pgvector. Backups quotidiens via Dokploy "Backups" → bucket S3 externe.
  • Storage : RustFS self-host (service rustfs), bucket reciprok-prod. Migré depuis Vercel Blob en 2026 via db:migrate-images.
  • Email : Resend production (domaine reciprok.co vérifié).
  • WhatsApp : numéro production Meta Cloud, webhook sur https://api.reciprok.co/webhooks/whatsapp.
  • Domaines : app.reciprok.co (web) + api.reciprok.co (api) + monitoring.reciprok.co (Signoz).
  • Monitoring : stack Signoz embarquée dans le compose (signoz + clickhouse + zookeeper + otel-collector). Voir operations/monitoring.

Stratégie de déploiement

  • Pas de zero-downtime au démarrage, l'app sert ~5 utilisateurs internes, une coupure de 30 secondes au déploiement est acceptable
  • Rolling update prévu plus tard via Traefik + 2 instances Dokploy si le besoin se présente
  • Migration de DB : exécutée avant le déploiement de l'image. Si la migration échoue, le déploiement est annulé.

Backups

Cron quotidien sur le serveur Postgres :

#!/bin/bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
pg_dump -Fc reciprok > /tmp/reciprok_${TIMESTAMP}.dump
rclone copy /tmp/reciprok_${TIMESTAMP}.dump r2:reciprok-backups/daily/
rm /tmp/reciprok_${TIMESTAMP}.dump

Politique de rétention :

  • Daily : 30 jours
  • Monthly : 12 mois (1er du mois)
  • Yearly : 5 ans (1er janvier)

Test de restauration mensuel sur l'env preprod (procédure documentée).

Training

Objectif

Environnement dédié à l'entraînement et l'évaluation de l'IA sur des données réelles, sans risque de modifier la prod.

Cas d'usage

  • Tester de nouveaux prompts système sur des conversations historiques
  • Évaluer le pipeline de search sur les vraies demandes passées
  • Régénérer les embeddings après changement de modèle
  • Lancer des batchs d'analyse historique (KB curation, comparaisons concurrentielles)

Spécificités

  • Restore nightly d'un snapshot prod via le cron de backup
  • Lecture seule depuis le dashboard, modifications uniquement via scripts ou worker
  • Aucun email/WhatsApp sortant : tous les providers sont remplacés par des stubs qui loggent uniquement
  • Domaine interne : training.reciprok.com (accès restreint à l'IP Hostinger + auth)
  • Compteur de coûts IA séparé : pour ne pas mélanger avec les coûts prod

Restore nightly

#!/bin/bash
LATEST=$(rclone lsf r2:reciprok-backups/daily/ --include "*.dump" | sort | tail -n1)
rclone copy r2:reciprok-backups/daily/${LATEST} /tmp/
pg_restore -c -d reciprok_training /tmp/${LATEST}
rm /tmp/${LATEST}

Lancé via cron Dokploy à 4h du matin.

Comparatif rapide

AspectLocalPreprodProdTraining
PostgresDocker localDokployDokployDokploy
StorageRustFSR2 preprodR2 prodR2 prod (RO)
EmailMailcatcherResend testResend prodStub
WhatsAppStubMeta sandboxMeta prodStub
DonnéesSeedAnonymiséesRéellesSnapshot prod
Coût IA tracéOui (dev)Oui (preprod)Oui (prod)Oui (training, séparé)
Auto-deploy,branche developtag v* sur mainnightly restore

Secrets

Tous les secrets sont gérés via Dokploy Secrets (variables d'env chiffrées par environnement). Pas de fichier .env commité.

Liste minimale par env :

  • DATABASE_URL
  • ANTHROPIC_API_KEY
  • OPENAI_API_KEY
  • RESEND_API_KEY
  • META_WHATSAPP_TOKEN
  • R2_ACCESS_KEY / R2_SECRET_KEY
  • BETTER_AUTH_SECRET
  • WEB_ORIGIN

Rotation : trimestrielle pour les clés tierces, annuelle pour BETTER_AUTH_SECRET.

Voir aussi

  • operations/monitoring, la stack d'observabilité associée
  • operations/costs, le budget par env
  • ADR-06, pourquoi Postgres self-host plutôt que Neon

On this page