Remplacer XAMPP par Docker : environnement LAMP multi-sites sur Windows
Mettre en place un stack LAMP avec Docker Compose sur Windows pour remplacer XAMPP ou WAMP. Comparatif perfs chiffré, guide pas-à-pas avec Apache, PHP 8.4, MySQL et PhpMyAdmin.

Tu galères à maintenir plusieurs projets PHP sur ta machine ? Entre XAMPP qui rame, les conflits de versions et le fameux “ça marche sur mon PC mais pas sur le serveur”, voilà pourquoi Docker est devenu mon setup de dev par défaut.
Ce guide montre comment mettre en place un serveur LAMP complet avec Docker Compose sur Windows : Apache, PHP 8.4, MySQL et PhpMyAdmin, capable d’héberger autant de projets que tu veux en parallèle.
Pourquoi remplacer XAMPP par Docker ?
Le problème avec XAMPP/WAMP
Quand on développe avec une stack PHP classique, on installe tout en dur sur sa machine. Le résultat, c’est prévisible :
- “Ça marche sur mon PC mais pas sur le serveur” (versions différentes)
- Conflits entre projets qui nécessitent des versions PHP différentes
- Impossible de travailler proprement sur deux projets avec des configs distinctes
- Désinstallation compliquée quand quelque chose foire
Ce que Docker change
Docker crée des conteneurs : des environnements isolés qui embarquent tout ce dont un projet a besoin. Pas d’installation sur ta machine, pas de conflits, tout reproductible en une commande.
Le chiffre qui parle : sur une même machine sous Ubuntu, la stack LAMP installée en dur consomme 45% du CPU et 4,6 Go de RAM au repos, dont les deux tiers pour l’interface graphique et les services inutiles. Avec Docker Compose, la même stack tourne à 12% du CPU et 900 Mo de RAM.
Et surtout : un docker compose up reproduit le même environnement sur n’importe quelle machine, dev comme prod.
Les concepts Docker à connaître avant de commencer
Quelques définitions rapides pour que la suite soit claire :
Image : un modèle en lecture seule. Pense à une recette de cuisine : elle décrit ce qu’il faut, mais ne produit rien tant qu’on ne l’exécute pas.
Conteneur : une instance en cours d’exécution d’une image. Le plat que tu as cuisiné à partir de la recette. Tu peux en faire plusieurs à partir de la même image.
Volume : un espace de stockage persistant. Quand tu supprimes un conteneur, ses données disparaissent - sauf celles dans un volume. C’est le disque dur externe du conteneur.
Dockerfile : un fichier texte avec les instructions pour construire une image personnalisée.
docker-compose.yml : le fichier qui orchestre plusieurs conteneurs en même temps. La carte du restaurant avec tous les plats qui doivent être servis ensemble.
Structure du projet
Voici l’arborescence à créer. On va remplir chaque fichier dans les étapes suivantes :
C:\Dev\PHP\
|-- .env # Variables d'environnement (mots de passe, versions)
|-- .gitignore # Fichiers ignorés par Git
|-- Dockerfile.php # Instructions pour construire l'image PHP/Apache
|-- docker-compose.yml # Orchestration de tous les services
|-- apache-config/
| |-- localhost.conf # Configuration du site principal
|-- www/ # Tes fichiers sources (synchronisés avec le conteneur)
| |-- index.php # Page d'accueil (ou dashboard)
| |-- info.php # phpinfo()
Information
www/ est synchronisé avec le conteneur Apache via un bind mount. Tout fichier que tu y places est immédiatement accessible dans le navigateur, sans relancer Docker.Règles à retenir dès le départ
- Docker Desktop doit être lancé avant toute commande
docker - Toutes les commandes
docker composes’exécutent depuis le dossier racine du projet - Le fichier
.envne doit jamais être versionné (il contient les mots de passe) - Après modification du
Dockerfile, reconstruire avecdocker compose up -d --build - Les données MySQL persistent dans un volume : supprimer un conteneur ne supprime pas les données
Étape 1 : Installer Docker Desktop
- Télécharge Docker Desktop depuis docker.com
- Installe-le et active WSL 2 quand c’est proposé (ou via PowerShell en admin :
wsl --install) - Redémarre, puis vérifie :
docker --version
# Docker version 29.x.x
Attention
Étape 2 : Le fichier .env
Le .env centralise toutes les variables de configuration utilisées par docker-compose.yml. Crée-le à la racine du projet :
# MySQL
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=test
MYSQL_USER=app_user
MYSQL_PASSWORD=app_password
# Connexion PHP vers MySQL
DB_HOST=mysql-server
DB_DATABASE=test
DB_USER=root
DB_PASS=root
# PhpMyAdmin
PMA_VERSION=5.2.2
PMA_HOST=mysql-server
PMA_USER=root
PMA_PASSWORD=root
Information
DB_HOST=mysql-server et pas localhost ? Dans Docker, les conteneurs communiquent par le nom du service défini dans docker-compose.yml. Le service MySQL s’appelle mysql-server, c’est donc ce nom qu’on utilise.Étape 3 : Le Dockerfile
Il n’existe pas d’image Docker officielle qui contient exactement notre stack (PHP + Apache + extensions + Composer + Xdebug). On construit la nôtre à partir de l’image officielle PHP.
Crée le fichier Dockerfile.php à la racine :
# Image de base : PHP 8.4 avec Apache intégré
FROM php:8.4-apache
# Paquets système nécessaires pour compiler les extensions PHP
RUN apt-get update && apt-get install -y --no-install-recommends \
libzip-dev libpq-dev libicu-dev libonig-dev \
zip unzip curl git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Extensions PHP
RUN docker-php-ext-install \
pdo pdo_mysql pdo_pgsql mysqli zip \
intl mbstring opcache
# Xdebug + APCu
RUN pecl install xdebug apcu \
&& docker-php-ext-enable xdebug apcu
# Composer (gestionnaire de dépendances PHP, équivalent de npm)
RUN curl -sS https://getcomposer.org/installer | \
php -- --install-dir=/usr/local/bin --filename=composer
# Modules Apache
RUN a2enmod rewrite proxy proxy_http headers
# Configuration Apache personnalisée
COPY ./apache-config /etc/apache2/sites-enabled
WORKDIR /var/www/html
Extensions installées et leur rôle :
| Extension | Rôle |
|---|---|
pdo + pdo_mysql | Connexion BDD (couche abstraite) |
mysqli | Connexion MySQL classique (PhpMyAdmin) |
zip | Manipulation d’archives (Composer) |
intl + mbstring | Internationalisation et UTF-8 (Symfony) |
opcache | Cache bytecode PHP (performances) |
xdebug | Débogage pas-à-pas |
apcu | Cache utilisateur en mémoire |
Attention
docker compose up -d --build.Étape 4 : Le docker-compose.yml
C’est le fichier principal qui orchestre les 3 conteneurs. Crée docker-compose.yml à la racine :
services:
# Base de données MySQL
mysql-server:
image: mysql:8.4
container_name: mysql-db
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 3
restart: "no"
# Serveur web Apache + PHP
php-apache-server:
build:
context: .
dockerfile: Dockerfile.php
container_name: php-apache
volumes:
- ./www:/var/www/html
- ./apache-config:/etc/apache2/sites-enabled
ports:
- "80:80"
environment:
DB_HOST: ${DB_HOST}
DB_DATABASE: ${DB_DATABASE}
DB_USER: ${DB_USER}
DB_PASS: ${DB_PASS}
PHPMYADMIN_HOST: phpmyadmin
depends_on:
mysql-server:
condition: service_healthy
phpmyadmin:
condition: service_started
mem_limit: 512m
memswap_limit: 512m
restart: "no"
# PhpMyAdmin
phpmyadmin:
image: phpmyadmin:${PMA_VERSION}
container_name: phpmyadmin
environment:
PMA_HOST: ${PMA_HOST}
PMA_USER: ${PMA_USER}
PMA_PASSWORD: ${PMA_PASSWORD}
PMA_ABSOLUTE_URI: http://localhost/phpmyadmin/
depends_on:
mysql-server:
condition: service_healthy
restart: "no"
volumes:
mysql_data:
Quelques points importants :
depends_on + healthcheck : Apache ne démarre que quand MySQL est réellement prêt à accepter des connexions, pas juste démarré.
mem_limit: 512m : limite la RAM du conteneur PHP à 512 Mo pour éviter qu’un script mal écrit ne bouffe toute ta machine.
restart: "no" : les conteneurs ne démarrent pas automatiquement avec Docker Desktop. Tu gardes le contrôle.
PhpMyAdmin via proxy : au lieu d’exposer PhpMyAdmin sur localhost:8081, il est accessible via http://localhost/phpmyadmin/ grâce au proxy Apache. Plus propre.
Étape 5 : Configuration Apache
Crée le fichier apache-config/localhost.conf :
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html
# Proxy PhpMyAdmin
ProxyPreserveHost On
ProxyRequests Off
RedirectMatch ^/phpmyadmin$ /phpmyadmin/
ProxyPass /phpmyadmin/ http://phpmyadmin:80/
ProxyPassReverse /phpmyadmin/ http://phpmyadmin:80/
<Location "/phpmyadmin/">
ProxyPassReverse /
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
</Location>
<Directory /var/www/html>
AllowOverride All
Require all granted
DirectoryIndex index.php index.html
</Directory>
<FilesMatch "\.php$">
SetHandler application/x-httpd-php
</FilesMatch>
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
</VirtualHost>
Étape 6 : Lancer l’environnement
Ouvre un terminal dans C:\Dev\PHP (WSL, PowerShell ou CMD) et lance :
docker compose up -d --build
Docker va :
- Lire le
.envet charger les variables - Télécharger les images MySQL et PhpMyAdmin (première fois uniquement)
- Construire l’image PHP/Apache depuis le Dockerfile
- Créer le réseau interne entre les conteneurs
- Démarrer MySQL, attendre qu’il soit prêt (healthcheck)
- Démarrer PhpMyAdmin et PHP/Apache
# Vérifier que les 3 conteneurs tournent
docker compose ps
NAME STATUS PORTS
mysql-db Up (healthy) 0.0.0.0:3306->3306/tcp
php-apache Up 0.0.0.0:80->80/tcp
phpmyadmin Up 80/tcp
Accès :
http://localhost: tes fichiers PHP danswww/http://localhost/phpmyadmin/: interface MySQLhttp://localhost/info.php: page phpinfo() (si le fichier existe)
Les commandes Docker du quotidien
Démarrer et arrêter
docker compose up -d # Démarrer les conteneurs
docker compose up -d --build # Démarrer + reconstruire les images
docker compose stop # Arrêter (conserve les conteneurs)
docker compose start # Relancer des conteneurs arrêtés
docker compose down # Arrêter ET supprimer les conteneurs (données préservées)
Observer et déboguer
docker compose ps # Statut des conteneurs
docker compose logs # Logs de tous les services
docker compose logs php-apache-server # Logs d'un service spécifique
docker compose logs -f # Logs en temps réel (Ctrl+C pour quitter)
Entrer dans un conteneur
Pour utiliser Composer ou exécuter des commandes PHP directement :
docker compose exec php-apache-server bash
# À l'intérieur du conteneur :
php -v
composer --version
cd /var/www/html/mon-projet && composer require vlucas/phpdotenv
exit
Gestion des données
docker volume ls # Lister les volumes
docker compose down # Supprime les conteneurs, garde les données
docker compose down --volumes # Supprime conteneurs ET données (irréversible)
Attention
docker compose down --volumes supprime définitivement les bases de données MySQL. Ne l’utilise que si tu veux repartir de zéro.Gérer plusieurs projets (VirtualHosts)
Par défaut, tout dossier créé dans www/ est accessible via http://localhost/nom-du-dossier/ sans aucune config. Pour les projets Symfony ou Laravel qui nécessitent un DocumentRoot propre, il faut un VirtualHost dédié.
1. Crée le dossier du projet dans www/
2. Crée un fichier de config Apache dans apache-config/mon-projet.conf :
<VirtualHost *:80>
ServerName mon-projet.local
DocumentRoot /var/www/html/mon-projet
<Directory /var/www/html/mon-projet>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
3. Modifie le fichier hosts Windows (Bloc-notes en administrateur, C:\Windows\System32\drivers\etc\hosts) :
127.0.0.1 mon-projet.local
4. Relance Apache :
docker compose up -d
Information
localhost/mon-projet/ suffit, aucune config nécessaire. Le VirtualHost dédié sert surtout pour Symfony/Laravel qui ont besoin d’un DocumentRoot à la racine du projet.Dépannage : problèmes courants
“docker” n’est pas reconnu : Docker Desktop n’est pas lancé. Attends l’icône verte dans la barre des tâches.
MySQL ne démarre pas : conflit sur le port 3306, souvent WAMP/XAMPP encore actif. Arrête-le ou change le port dans docker-compose.yml : "3307:3306".
Erreur PhpMyAdmin “Error in processing request” : vérifie que PMA_ABSOLUTE_URI: http://localhost/phpmyadmin/ est bien présent dans le service phpmyadmin.
Conflit sur le port 80 : un autre serveur web tourne (IIS, WAMP, Skype). Arrête-le ou change le port : "8080:80".
VirtualHost qui ne répond pas : vérifie le fichier hosts (ouvert en administrateur ?), vide le cache DNS avec ipconfig /flushdns.
Les modifs PHP ne s’affichent pas : cache navigateur, force le rafraîchissement avec Ctrl + Shift + R.
Pour aller plus loin
Sauvegarder et restaurer MySQL
# Sauvegarder
docker compose exec mysql-server mysqldump -u root -proot --all-databases > backup.sql
# Restaurer
docker compose exec -T mysql-server mysql -u root -proot < backup.sql
Mettre à jour les versions
Les versions sont centralisées dans .env et docker-compose.yml :
| Composant | Où modifier | Exemple |
|---|---|---|
| PHP | Dockerfile.php (ligne FROM) | php:8.4-apache |
| MySQL | docker-compose.yml (image) | mysql:8.4 |
| PhpMyAdmin | .env (PMA_VERSION) | 5.2.2 |
Après modification : docker compose up -d --build
Attention
Sources et références :



