Docker Compose pour le développement : infrastructure locale
La différence entre une expérience d'intégration agréable (« cloner et exécuter pnpm dev:infra ») et une expérience pénible (« d'abord configurer PostgreSQL, puis configurer Redis, puis... ») dépend de la façon dont votre configuration Docker Compose capture les exigences de votre infrastructure. A well-crafted docker-compose.dev.yml lets any developer on any machine have the exact same infrastructure running in minutes.
Ce guide couvre les modèles d'une pile de développement local de qualité production : configuration des services, vérifications de l'état, mise en réseau, gestion des volumes et intégration avec la séquence de démarrage de votre application.
Points clés à retenir
- Use a non-default port for PostgreSQL locally (5433) to avoid conflicts with system installations
- Les contrôles de santé sur les dépendances de service évitent les erreurs de démarrage « connexion refusée »
- Named volumes persist database data between container restarts — bind mounts don't work reliably on Windows
- Utilisez
env_filepour charger les variables d'environnement de votre fichier.env.localdans des conteneurs- Separate
docker-compose.dev.ymlfromdocker-compose.prod.yml— they serve different purposes- The
depends_on.condition: service_healthypattern waits for actual readiness, not just container start- Use
profilesto make optional services (email, monitoring) opt-in- Exécutez
docker compose(v2) et nondocker-compose(v1) — la syntaxe du plugin est à jour
La pile de développement complète
# infrastructure/docker-compose.dev.yml
name: ecosire-dev
services:
# ─── PostgreSQL ─────────────────────────────────────────────────
postgres:
image: postgres:17-alpine
container_name: ecosire-postgres
environment:
POSTGRES_DB: ecosire_dev
POSTGRES_USER: ecosire
POSTGRES_PASSWORD: dev_password_change_in_prod
ports:
- "5433:5432" # 5433 externally — avoids conflicts with system PostgreSQL
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d # Run SQL on first start
command: >
postgres
-c shared_buffers=256MB
-c effective_cache_size=1GB
-c work_mem=16MB
-c maintenance_work_mem=128MB
-c checkpoint_completion_target=0.9
-c wal_buffers=16MB
-c max_connections=100
-c log_min_duration_statement=100
-c log_statement=ddl
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ecosire -d ecosire_dev"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# ─── Redis ──────────────────────────────────────────────────────
redis:
image: redis:7-alpine
container_name: ecosire-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: >
redis-server
--maxmemory 512mb
--maxmemory-policy allkeys-lru
--appendonly yes
--appendfsync everysec
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# ─── Authentik (Identity Provider) ──────────────────────────────
authentik-server:
image: ghcr.io/goauthentik/server:2024.12
container_name: ecosire-authentik
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: ecosire
AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true"
volumes:
- authentik_media:/media
- authentik_certs:/certs
ports:
- "9000:9000" # HTTP
- "9443:9443" # HTTPS
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "ak healthcheck"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s # Authentik takes time to initialize
restart: unless-stopped
authentik-worker:
image: ghcr.io/goauthentik/server:2024.12
container_name: ecosire-authentik-worker
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: ecosire
AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
volumes:
- authentik_media:/media
- authentik_certs:/certs
- /var/run/docker.sock:/var/run/docker.sock # For Authentik's proxy
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
# ─── Mailpit (Email Testing) ─────────────────────────────────────
mailpit:
image: axllent/mailpit:latest
container_name: ecosire-mailpit
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
environment:
MP_MAX_MESSAGES: 200
MP_SMTP_AUTH_ACCEPT_ANY: true
MP_SMTP_AUTH_ALLOW_INSECURE: true
restart: unless-stopped
profiles:
- email # Optional — use `docker compose --profile email up`
# ─── pgAdmin (Database GUI) ─────────────────────────────────────
pgadmin:
image: dpage/pgadmin4:latest
container_name: ecosire-pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: admin
PGADMIN_CONFIG_SERVER_MODE: "False"
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
ports:
- "5050:80"
volumes:
- pgadmin_data:/var/lib/pgadmin
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
profiles:
- tools # Optional
networks:
default:
name: ecosire-dev-network
volumes:
postgres_data:
name: ecosire-postgres-data
redis_data:
name: ecosire-redis-data
authentik_media:
name: ecosire-authentik-media
authentik_certs:
name: ecosire-authentik-certs
pgadmin_data:
name: ecosire-pgadmin-data
Scripts Package.json
Câblez les commandes Docker Compose à vos scripts monorepo :
{
"scripts": {
"dev:infra": "docker compose -f infrastructure/docker-compose.dev.yml up -d",
"dev:infra:down": "docker compose -f infrastructure/docker-compose.dev.yml down",
"dev:infra:logs": "docker compose -f infrastructure/docker-compose.dev.yml logs -f",
"dev:infra:reset": "docker compose -f infrastructure/docker-compose.dev.yml down -v && pnpm dev:infra",
"dev:infra:email": "docker compose -f infrastructure/docker-compose.dev.yml --profile email up -d",
"dev:infra:tools": "docker compose -f infrastructure/docker-compose.dev.yml --profile tools up -d"
}
}
L'indicateur --profile permet aux services facultatifs (tests de courrier électronique avec Mailpit, interface graphique de base de données avec pgAdmin) de rester inactifs jusqu'à ce qu'ils soient explicitement demandés.
Scripts d'initialisation de base de données
Placez les fichiers SQL dans infrastructure/init-scripts/ — ils s'exécutent au premier démarrage du conteneur :
-- infrastructure/init-scripts/01-create-databases.sql
-- Create all databases Authentik needs separately from the app DB
CREATE DATABASE authentik;
GRANT ALL PRIVILEGES ON DATABASE authentik TO ecosire;
-- Create test database for CI
CREATE DATABASE ecosire_test;
GRANT ALL PRIVILEGES ON DATABASE ecosire_test TO ecosire;
-- infrastructure/init-scripts/02-extensions.sql
-- Enable PostgreSQL extensions
\c ecosire_dev;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- Trigram search
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- Composite GIN indexes
\c ecosire_test;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
Les scripts d'initialisation s'exécutent par ordre alphabétique. The \c database_name psql metacommand switches the active database.
Intégration des variables d'environnement
Votre application lit à partir de .env.local dans la racine monorepo. The Docker services need to know how to connect to each other using service names (not localhost):
# .env.local (monorepo root)
# PostgreSQL — use 5433 externally (host) or 5432 internally (container network)
DATABASE_URL=postgresql://ecosire:dev_password_change_in_prod@localhost:5433/ecosire_dev
# Redis
REDIS_URL=redis://localhost:6379
# Authentik — use 9000 for external calls from your dev machine
AUTHENTIK_URL=http://localhost:9000
# Use service name for server-to-server calls within Docker network
AUTHENTIK_INTERNAL_URL=http://authentik-server:9000
# Email (Mailpit SMTP)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false
# Application
NODE_ENV=development
Pour les applications exécutées dans Docker et qui doivent communiquer avec d’autres services, utilisez les noms de service. For applications running on your host machine (NestJS, Next.js in dev mode), use localhost with the host-mapped ports.
Analyse approfondie des contrôles de santé
Health checks prevent cascading startup failures. Le depends_on.condition: service_healthy attend la disponibilité réelle, pas seulement le démarrage du conteneur :
# Without health checks — can fail because PostgreSQL isn't ready
depends_on:
- postgres
# With health checks — waits for PostgreSQL to accept connections
depends_on:
postgres:
condition: service_healthy
Contrôles de santé personnalisés pour vos propres services :
// apps/api/src/health/health.controller.ts
@Get()
@Public()
@HealthCheck()
async check() {
return this.health.check([
() => this.db.isHealthy('database'),
() => this.redis.isHealthy('redis'),
]);
}
# If your API is also dockerized
api:
image: ecosire-api:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
Gestion des volumes
Les volumes nommés conservent les données entre les redémarrages. Comprenez quand utiliser chaque type de volume :
| Tapez | Persistance | Performances | Utiliser pour |
|---|---|---|---|
| Volume nommé | Oui | Excellent | Données de base de données |
| Lier le support | Oui | Bon (Linux), Mauvais (macOS) | Rechargement à chaud du code source |
| tmpfs | Non | Excellent | Fichiers temporaires, secrets |
# Use bind mounts for source code (enables hot reload)
volumes:
- ./apps/api/src:/app/src # Code changes reflected immediately
# Use named volumes for data
volumes:
- postgres_data:/var/lib/postgresql/data
# Use tmpfs for ephemeral data
volumes:
- type: tmpfs
target: /tmp
Sur macOS avec Docker Desktop, les montages de liaison utilisent gRPC FUSE qui est nettement plus lent que sous Linux. Pour les serveurs de développement NestJS et Next.js, exécutez-les directement sur votre machine hôte (pas dans Docker) pour obtenir les performances natives du système de fichiers.
Production Docker Composer
Le fichier de composition de production est structurellement différent : pas de ports locaux, de politiques de redémarrage, de limites de ressources de production :
# infrastructure/docker-compose.prod.yml
name: ecosire-prod
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
# No port mapping — only accessible within Docker network
restart: always
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 512M
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 1gb
volumes:
- redis_data:/data
restart: always
# No port mapping — internal only
volumes:
postgres_data:
redis_data:
La production n'expose pas les ports en externe : les applications se connectent via le réseau Docker interne. Nginx gère le trafic externe.
Pièges courants et solutions
Piège 1 : Le port est en conflit avec les services système
PostgreSQL, Redis et d'autres services s'exécutent souvent en tant que services système. Mappez toujours vers des ports non standard en cours de développement :
- PostgreSQL :
5433:5432(et non5432:5432) - Redis : conserver
6379:6379(rarement en conflit) - Exécutez
lsof -i :5432pour vérifier ce qui utilise le port par défaut
Piège 2 : Problèmes d'autorisation de volume sous Linux
Les volumes Docker sous Linux utilisent la propriété root par défaut. Si l'utilisateur de votre conteneur n'est pas root, définissez la propriété correcte :
postgres:
image: postgres:17-alpine
user: "999:999" # postgres user's UID:GID
# Or use init container to fix permissions
Piège 3 : l'initialisation d'Authentik prend plus de 60 secondes
Authentik exécute les migrations de bases de données au premier démarrage. Le start_period: 60s dans le bilan de santé lui donne l'heure. Si les services dépendants démarrent avant qu’Authentik ne soit prêt, ils échoueront. Utilisez la condition service_healthy et donnez-lui suffisamment de start_period.
Piège 4 : limites des ressources Docker Desktop sur Mac
Docker Desktop par défaut alloue 2 processeurs et 2 Go de RAM, ce qui n'est pas suffisant pour que PostgreSQL + Redis + Authentik s'exécutent simultanément. Augmentation des paramètres de Docker Desktop > Ressources à au moins 4 processeurs et 6 Go de RAM.
Piège 5 : docker-compose contre docker compose
L'ancien docker-compose (v1, écrit en Python) est obsolète. Utilisez docker compose (v2, le plugin). Vérifiez votre version : docker compose version. Si vous voyez Docker Compose version v2.x.x, vous utilisez la version v2.
Questions fréquemment posées
Dois-je exécuter mes services d'application (NestJS, Next.js) dans Docker pendant le développement ?
Généralement non : pour un développement actif, exécutez vos services d’application sur votre machine hôte pour un rechargement à chaud plus rapide et un débogage plus facile. Utilisez Docker uniquement pour les services d'infrastructure (bases de données, caches, fournisseurs d'identité) qui sont stables et ne nécessitent pas de redémarrages fréquents. L'exception est si votre application possède des dépendances natives qui diffèrent entre votre système d'exploitation de développement et l'environnement de production.
Comment gérer les migrations de bases de données dans le workflow Docker Compose ?
Exécutez les migrations depuis votre machine hôte après avoir démarré l'infrastructure : pnpm dev:infra && pnpm db:migrate. N'exécutez pas de migrations dans un conteneur Docker pendant le développement : vous perdez la vérification de type et l'intégration IDE qui garantissent la sécurité des migrations Drizzle. Pour la création initiale de la base de données, utilisez les scripts initdb.d de Docker.
Comment sauvegarder et restaurer mes volumes Docker locaux ?
Utilisez docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data pour sauvegarder. Restaurez avec la même approche en utilisant tar xzf. Pour le développement, vous pouvez également effectuer un dump avec pg_dump et restaurer avec psql puisque le port est exposé.
Comment partager l'état de Docker Compose avec d'autres membres de l'équipe ?
Le fichier Docker Compose est partagé via git, mais les données en volumes sont locales. Chaque développeur commence avec une base de données vide et exécute des migrations/graines pour la remplir. Utilisez des scripts de départ (engagés dans le dépôt) pour créer des données de test cohérentes. Le docker-compose.dev.yml partagé garantit que tout le monde utilise les mêmes versions et configurations de service.
Pourquoi utiliser Mailpit au lieu d'un véritable e-mail en développement ?
Mailpit est un serveur SMTP local qui capture tous les e-mails sortants et fournit une interface utilisateur Web pour les afficher. Il évite l'envoi accidentel de vrais e-mails à de vrais utilisateurs pendant le développement, ne nécessite pas d'informations d'identification SMTP et vous permet de vérifier les modèles d'e-mails sans consulter votre boîte de réception. Configurez votre application pour utiliser SMTP_HOST=localhost SMTP_PORT=1025 et visitez http://localhost:8025 pour voir les e-mails capturés.
Prochaines étapes
Une configuration Docker Compose bien conçue pour le développement local est un investissement qui rapporte des dividendes chaque fois qu'un nouveau développeur rejoint ou que vous démarrez une nouvelle machine. ECOSIRE exécute PostgreSQL 17, Redis 7 et Authentik dans Docker Compose pour le développement local dans toute l'équipe.
Besoin d'aide pour concevoir votre infrastructure de développement local ou conteneuriser votre application pour la production ? Découvrez nos services DevOps pour voir comment nous pouvons vous aider.
Rédigé par
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
Articles connexes
Guide de déploiement AWS EC2 pour les applications Web
Guide de déploiement AWS EC2 complet : sélection d'instances, groupes de sécurité, déploiement Node.js, proxy inverse Nginx, SSL, mise à l'échelle automatique, surveillance CloudWatch et optimisation des coûts.
ERP cloud ou sur site en 2026 : le guide définitif
ERP cloud ou sur site en 2026 : analyse du coût total, comparaison de la sécurité, évolutivité, conformité et modèle de déploiement adapté à votre entreprise.
Création de modules Odoo personnalisés : didacticiel du développeur
Tutoriel étape par étape pour créer des modules Odoo 19 personnalisés. Couvre la structure des modules, les modèles, les vues, la sécurité, les assistants et les meilleures pratiques pour le code prêt pour la production.