Docker + Symfony + WSL2 : les 3 problèmes du premier jour

Nouveau projet Symfony 7. Stack cible : PostgreSQL 16, Redis 7, Apache (pas Nginx — préférence personnelle, on y reviendra). Environnement de dev : WSL2 sous Windows. En théorie, Docker + WSL2 c'est fluide depuis quelques versions.

En pratique, il y a 3 problèmes qui arrivent systématiquement le premier jour, qui ne sont pas documentés ensemble, et qui font perdre une heure à chaque fois. Voilà comment les éviter.

Le stack final

Le docker-compose.yml complet :

services:
  app:
    build: .
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
    depends_on:
      - db
      - redis
    environment:
      DATABASE_URL: "postgresql://app:password@db:5432/touspourris"

  db:
    image: postgres:16-alpine
    ports:
      - "5433:5432"   # 5433 en local, pas 5432
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: password
      POSTGRES_DB: touspourris
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

Le détail qui compte : le port 5433:5432 pour PostgreSQL. C'est le cœur du premier problème.

Problème 1 — Le conflit de port PostgreSQL

Si tu as PostgreSQL installé localement sur WSL2 — ce qui arrive souvent sur un poste de dev — il écoute déjà sur le port 5432. docker compose up lance le container PostgreSQL et essaie de binder 5432:5432 : conflit immédiat.

Error response from daemon: driver failed programming external connectivity on endpoint db:
Bind for 0.0.0.0:5432 failed: port is already allocated

Solution : mapper sur 5433 côté host (5433:5432 dans docker-compose.yml). Le container continue d'écouter sur 5432 en interne — les autres services Docker le joignent sans changer leur config. La variable DATABASE_URL pointe sur db:5432, pas sur l'hôte. Seul le port exposé vers l'hôte change.

Pour se connecter depuis l'hôte (DBeaver, psql) : localhost:5433. C'est tout.

Problème 2 — Les permissions Docker group sous WSL2

Après installation de Docker Engine (pas Docker Desktop) :

sudo apt install docker.io
sudo usermod -aG docker $USER

Réflexe : lancer docker ps. Résultat :

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

La raison : usermod -aG docker $USER modifie le groupe pour les nouvelles sessions, pas la session courante. Deux solutions :

# Option 1 : activer le groupe dans la session courante (temporaire)
newgrp docker

# Option 2 : redémarrer WSL2 entièrement (permanent)
# Dans PowerShell Windows :
wsl --shutdown
# Puis relancer WSL2

newgrp docker ouvre un sous-shell avec le nouveau groupe. Ça marche, mais seulement pour le terminal courant. Le wsl --shutdown est la solution définitive — WSL2 redémarre avec les groupes correctement appliqués.

Problème 3 — Docker daemon pas démarré au lancement WSL2

WSL2 ne lance pas systemd par défaut (ou selon les distros). Docker daemon ne tourne donc pas automatiquement au démarrage. Première commande Docker du matin :

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Deux solutions :

# Option 1 : démarrer manuellement à chaque fois
sudo service docker start

# Option 2 : activer systemd dans WSL2 (Ubuntu 22.04+)
# Dans /etc/wsl.conf :
[boot]
systemd=true

Avec systemd=true, Docker démarre automatiquement comme un service système. C'est la solution propre si ta distro le supporte. Après modification de /etc/wsl.conf, un wsl --shutdown depuis PowerShell pour que le changement prenne effet.

Apache vs Nginx dans Docker

Choix délibéré d'Apache plutôt que Nginx pour le container app. Raison principale : familiarité avec la config Apache pour du Symfony (.htaccess standard, mod_rewrite activé). Nginx est souvent recommandé pour les perfs en prod, mais en dev la différence est nulle et le coût de configuration supplémentaire n'est pas justifié.

Le Dockerfile pour Symfony + Apache :

FROM php:8.3-apache

RUN apt-get update && apt-get install -y \
    git zip unzip libpq-dev \
    && docker-php-ext-install pdo pdo_pgsql \
    && a2enmod rewrite

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html
COPY . .

RUN composer install --no-dev --optimize-autoloader

RUN chown -R www-data:www-data var/

L'extension pdo_pgsql est indispensable — sans elle, Doctrine lève une exception au premier appel. a2enmod rewrite active le module Apache pour que le routeur de Symfony fonctionne. Le chown final évite les erreurs de permissions sur le cache et les logs.

Le Makefile comme interface unique

Avec Docker, les commandes deviennent longues. Un Makefile à la racine résout ça :

up:
	docker compose up -d

down:
	docker compose down

shell:
	docker compose exec app bash

console:
	docker compose exec app php bin/console $(cmd)

migrate:
	docker compose exec app php bin/console doctrine:migrations:migrate --no-interaction

Usage : make up, make shell, make console cmd="cache:clear". Tout le monde sur le projet utilise les mêmes commandes sans connaître la syntaxe Docker exacte. Et les nouveaux arrivants ont un point d'entrée documenté sans avoir à lire le docker-compose.yml en entier.

Conclusion

Les 3 problèmes — port conflict, group permissions, daemon startup — sont indépendants mais arrivent tous le premier jour. Aucun n'est documenté dans les guides Docker officiels pour WSL2. En les résolvant une fois proprement (5433 pour PG, wsl --shutdown, systemd=true), le setup devient stable et reproductible.

Le reste — Symfony, PostgreSQL, Redis — fonctionne comme sur n'importe quel stack Docker standard. Le problème n'était pas Docker, c'était l'environnement WSL2 en dessous.

📄 CLAUDE.md associé

Commentaires (0)