Obexal Docs

Docs/Auto-hébergement/Déployer avec Docker Compose

Déployer avec Docker Compose

Lancer la stack de production avec compose.prod.yml et l'overlay TLS Caddy : DNS, fichier d'environnement, premier lancement, contrôles de santé et mises à jour.

Le déploiement de référence est un hôte unique sous Docker Compose : compose.prod.yml décrit la stack applicative, et l'overlay compose.tls.yml place Caddy devant elle pour terminer le HTTPS avec des certificats ACME automatiques.

Avant de commencer

Vérifiez d'abord les prérequis. Il vous faut l'arborescence source d'Obexal sur l'hôte (les images sont construites localement à partir d'elle), un domaine que vous contrôlez, et un relais SMTP UE. Chaque variable mentionnée ci-dessous est détaillée dans la référence de configuration.

1. Prévoir les hôtes DNS

La stack sert trois hôtes, plus un apex optionnel. Tous les enregistrements pointent vers l'IP publique de l'hôte.

Hôte (exemple)ServiceRôle
api.example.comauth-serviceL'API et l'issuer OIDC : c'est la valeur de OIDC_ISSUER
admin.example.comadminLa console d'administration
accounts.example.comlogin-uiL'interface de connexion par défaut
example.com (optionnel)fichiers statiquesL'apex : adaptez ou retirez son bloc dans deploy/caddy/Caddyfile

Tout autre nom d'hôte HTTPS atteignant Caddy est traité comme un domaine custom de tenant : un certificat n'est émis à la demande que si l'auth-service confirme que le domaine est vérifié (GET /v1/tls/authorize). Voir Domaines personnalisés.

2. Configurer l'environnement

Créez un fichier .env.prod à côté des fichiers Compose. Ne le committez jamais. Un fichier de production minimal :

APP_ENV=production
OIDC_ISSUER=https://api.example.com
SESSION_SECURE=true

# Chiffrement au repos (base64, 32 octets) : générer avec `openssl rand -base64 32`
ENCRYPTION_KEY=$GENERATED_ENCRYPTION_KEY

# Conteneurs locaux (préférer des services managés UE en production)
POSTGRES_USER=obexal
POSTGRES_PASSWORD=$GENERATED_DB_PASSWORD
POSTGRES_DB=obexal

# Identifiants du stockage objet (MinIO, logos de tenant)
S3_ACCESS_KEY=$GENERATED_S3_ACCESS_KEY
S3_SECRET_KEY=$GENERATED_S3_SECRET_KEY

# E-mail sortant (relais UE ; le transport "log" est refusé en production)
MAIL_TRANSPORT=smtp
SMTP_HOST=smtp.example.eu
SMTP_PORT=587
SMTP_USERNAME=$SMTP_USERNAME
SMTP_PASSWORD=$SMTP_PASSWORD
MAIL_FROM=no-reply@example.com

# Caddy (overlay TLS)
OBEXAL_ACME_EMAIL=ops@example.com
OBEXAL_API_HOST=api.example.com
OBEXAL_ADMIN_HOST=admin.example.com
OBEXAL_LOGIN_HOST=accounts.example.com

# Intégré aux builds des interfaces
NEXT_PUBLIC_OBEXAL_API_URL=https://api.example.com
NEXT_PUBLIC_LOGIN_UI_URL=https://accounts.example.com
Attention

Renseignez TRUSTED_PROXIES avec la plage réseau de votre reverse proxy. Sans cela, chaque client semble provenir de l'IP du proxy et le rate limiting par IP est inopérant.

3. Lancer la stack

Une seule commande construit les trois images applicatives depuis les sources et démarre tout :

docker compose -f compose.prod.yml -f compose.tls.yml --env-file .env.prod up -d --build

Le service one-shot migrate applique d'abord les migrations SQL ; l'auth-service ne démarre qu'une fois les migrations terminées et PostgreSQL et Redis déclarés sains.

Note

Une fois Caddy en place, les services applicatifs ne devraient pas publier de ports sur l'hôte : retirez leurs entrées ports: dans compose.prod.yml (ou transformez-les en expose:). Seul Caddy a besoin des ports 80 et 443.

4. Vérifier le déploiement

docker compose -f compose.prod.yml -f compose.tls.yml --env-file .env.prod ps

curl -fsS https://api.example.com/healthz   # liveness, sans dépendance
curl -fsS https://api.example.com/readyz    # readiness : PostgreSQL et Redis

L'API expose aussi GET /metrics au format Prometheus (compteurs de requêtes, histogramme de latence, requêtes en vol). Protégez-le avec METRICS_TOKEN ou gardez-le sur le réseau interne uniquement. Chaque conteneur a de plus un healthcheck Docker : l'auth-service distroless se sonde lui-même via une sous-commande healthcheck intégrée, l'image ne contenant aucun shell.

Terminez par une vraie connexion de bout en bout sur une organisation de test.

Des migrations forward-only

Les migrations sont embarquées dans le binaire de l'auth-service et appliquées en ordre lexical, chacune dans sa propre transaction, enregistrée dans schema_migrations. Leur application est idempotente. Il n'y a pas de migrations descendantes : c'est un choix assumé pour un système d'identité, où des migrations de retour génériques détruisent des données. Revenir en arrière signifie redéployer l'image précédente, et ne restaurer une sauvegarde que si une migration a introduit un changement incompatible. Voir Sauvegardes et restauration.

Des images durcies

  • auth-service : un binaire Go statique dans une image distroless, non-root, sans shell, sans gestionnaire de paquets.
  • admin : sortie standalone de Next.js sur node:20-slim, non-root.
  • login-ui : un export statique servi par nginx-unprivileged.

Les images de base sont épinglées sur des versions mineures précises. En production, figez-les par digest (@sha256:) via un miroir de registre UE.

Mettre à jour vers une nouvelle version

Sauvegardez toujours avant de mettre à jour : les migrations sont forward-only.

./scripts/backup.sh
git pull
docker compose -f compose.prod.yml -f compose.tls.yml --env-file .env.prod build
docker compose -f compose.prod.yml -f compose.tls.yml --env-file .env.prod run --rm migrate
docker compose -f compose.prod.yml -f compose.tls.yml --env-file .env.prod up -d auth-service admin login-ui

Vérifiez ensuite /readyz, les logs, et une connexion de bout en bout sur une organisation de test.