Credit : Logo officiel
Deployer PrestaShop avec Docker Compose
Pourquoi je deploie tous mes nouveaux PrestaShop avec Docker Compose
J'ai bascule a Docker en 2022 sur un PrestaShop client qui tournait depuis 2019 sur un serveur Debian 9 completement pourri. Le client voulait refaire son infra. Plutot que de reinstaller a la main avec les memes risques de derive, j'ai propose Docker Compose. Resultat : 25 minutes pour remonter une stack complete, reproductible, versionnable. Depuis, c'est ma methode par defaut pour les nouveaux deploiements et les migrations.
Docker apporte trois choses concretes en e-commerce : isolation (PHP, MariaDB, Nginx), reversibilite (un docker compose down && docker compose up -d et tout est propre), et homogeneite entre dev/staging/prod. Ce guide montre exactement la stack que j'utilise sur PrestaShop 8.1, avec MariaDB 10.11, Nginx Alpine et SSL Let's Encrypt.
Prerequis et installation Docker
Sur Debian 12 ou Ubuntu 22.04 LTS :
# Repos officiels Docker
curl -fsSL https://download.docker.com/linux/debian/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian $(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
docker --version # Docker version 27.x
docker compose version # Docker Compose v2.27.x
Une machine VPS 4 CPU / 8 Go RAM tient une boutique PrestaShop avec 5000 produits et 1500 visites/jour sans broncher.
Structure du projet
Je reproduis la meme arbo sur tous les deploiements pour que tout soit pareil :
/opt/prestashop-docker/
docker-compose.yml
.env
custom-php.ini
nginx/
default.conf
ssl/
fullchain.pem
privkey.pem
data/ # volumes nommes Docker
backups/ # dumps automatiques
scripts/
backup.sh
Le fichier .env : centraliser les secrets
# Base de donnees
MYSQL_ROOT_PASSWORD=RootP@ssword2026!
MYSQL_DATABASE=prestashop
MYSQL_USER=ps_user
MYSQL_PASSWORD=PsP@ssword2026Strong!
# PrestaShop
PS_DOMAIN=shop.monsite.fr
PS_ADMIN_EMAIL=admin@monsite.fr
PS_ADMIN_PASSWORD=AdminP@ss2026Long!
PS_FOLDER_ADMIN=admin-9k3xp
# Performance
PHP_MEMORY_LIMIT=512M
Proteger le fichier : chmod 600 .env.
docker-compose.yml complet
services:
prestashop:
image: prestashop/prestashop:8.1-fpm
container_name: prestashop-app
restart: unless-stopped
volumes:
- prestashop_data:/var/www/html
- ./custom-php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
environment:
- DB_SERVER=database
- DB_PORT=3306
- DB_NAME=${MYSQL_DATABASE}
- DB_USER=${MYSQL_USER}
- DB_PASSWD=${MYSQL_PASSWORD}
- DB_PREFIX=ps_
- PS_DOMAIN=${PS_DOMAIN}
- PS_LANGUAGE=fr
- PS_COUNTRY=FR
- PS_INSTALL_AUTO=1
- PS_ERASE_DB=0
- PS_FOLDER_ADMIN=${PS_FOLDER_ADMIN}
- PS_FOLDER_INSTALL=install-rename
- ADMIN_MAIL=${PS_ADMIN_EMAIL}
- ADMIN_PASSWD=${PS_ADMIN_PASSWORD}
depends_on:
database:
condition: service_healthy
networks:
- prestashop-net
healthcheck:
test: ["CMD", "php-fpm", "-t"]
interval: 30s
timeout: 5s
retries: 3
database:
image: mariadb:10.11
container_name: prestashop-db
restart: unless-stopped
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --innodb-buffer-pool-size=512M
- --max-allowed-packet=64M
volumes:
- db_data:/var/lib/mysql
- ./backups:/backups
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
healthcheck:
test: ["CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 10
networks:
- prestashop-net
nginx:
image: nginx:alpine
container_name: prestashop-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- prestashop_data:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- prestashop
networks:
- prestashop-net
redis:
image: redis:7-alpine
container_name: prestashop-redis
restart: unless-stopped
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
networks:
- prestashop-net
volumes:
prestashop_data:
driver: local
db_data:
driver: local
networks:
prestashop-net:
driver: bridge
Ce qu'il faut noter
- Pas de
version:en tete : deprecie depuis Docker Compose v2 depends_onaveccondition: service_healthyevite les race conditions au boot- MariaDB ecoute uniquement sur le reseau Docker, jamais expose en
ports: - Redis ajoute pour le cache PrestaShop (cf Redis cache WordPress/PrestaShop)
La config Nginx avec HTTP/2 et HSTS
Fichier nginx/default.conf :
upstream php-fpm {
server prestashop-app:9000;
}
server {
listen 80;
server_name shop.monsite.fr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name shop.monsite.fr;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
root /var/www/html;
index index.php;
client_max_body_size 32M;
gzip on;
gzip_comp_level 6;
gzip_types text/css application/javascript application/json image/svg+xml;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass php-fpm;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
fastcgi_buffers 16 32k;
fastcgi_buffer_size 64k;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~ /\. { deny all; }
location ~ /(install|admin-dev) { deny all; }
}
Configuration PHP-FPM custom
Fichier custom-php.ini :
memory_limit = 512M
upload_max_filesize = 32M
post_max_size = 32M
max_execution_time = 300
max_input_vars = 10000
max_input_time = 300
; OPcache pour la prod
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.jit_buffer_size = 128M
opcache.jit = tracing
Generer le certificat SSL Let's Encrypt
Plusieurs approches. La plus simple : certbot sur l'hote, montage des certificats dans le conteneur Nginx :
sudo apt install certbot
sudo systemctl stop nginx 2>/dev/null
docker compose stop nginx
sudo certbot certonly --standalone -d shop.monsite.fr -m admin@monsite.fr --agree-tos
# Copier dans le repertoire monte
sudo cp /etc/letsencrypt/live/shop.monsite.fr/fullchain.pem nginx/ssl/
sudo cp /etc/letsencrypt/live/shop.monsite.fr/privkey.pem nginx/ssl/
sudo chown -R $USER: nginx/ssl/
docker compose up -d nginx
Pour le renouvellement auto, voir certificat SSL wildcard avec Let's Encrypt.
Lancer la stack et installer PrestaShop
docker compose up -d
docker compose logs -f prestashop
docker compose ps
L'installation auto prend 2 a 4 minutes. Ensuite la boutique est sur https://shop.monsite.fr et l'admin sur https://shop.monsite.fr/admin-9k3xp (selon PS_FOLDER_ADMIN).
Verifier que les conteneurs sont en mode healthy :
docker compose ps
# NAME IMAGE STATUS
# prestashop-app prestashop/prestashop:8.1-fpm Up (healthy)
# prestashop-db mariadb:10.11 Up (healthy)
# prestashop-nginx nginx:alpine Up
# prestashop-redis redis:7-alpine Up
Commandes utiles au quotidien
# Shell dans le conteneur app
docker compose exec prestashop bash
# Logs en suivi
docker compose logs -f --tail=100 prestashop
# Backup de la base
docker compose exec -T database \
mysqldump -u ps_user -p${MYSQL_PASSWORD} prestashop \
| zstd > backups/ps_$(date +%Y%m%d).sql.zst
# Restaurer un dump
zstd -d < backups/ps_20260507.sql.zst | \
docker compose exec -T database mysql -u ps_user -p${MYSQL_PASSWORD} prestashop
# Mise a jour de l'image PrestaShop
docker compose pull prestashop
docker compose up -d prestashop
# Voir les ressources utilisees
docker stats
# Tout arreter sans toucher aux volumes
docker compose down
# DANGER : tout supprimer y compris les donnees
# docker compose down -v
Activer le cache Redis dans PrestaShop
Le service Redis est dans la stack mais il faut l'activer cote PrestaShop. Dans app/config/parameters.php ou via l'admin > Parametres avancees > Performances :
'redis_servers' => [
[
'host' => 'redis',
'port' => 6379,
'auth' => '',
'db' => 0,
],
],
Gain mesure sur un client : -45% sur le temps de generation des pages categories grace au cache des objets PrestaShop.
Backup automatise quotidien
Script scripts/backup.sh :
#!/bin/bash
set -euo pipefail
set -a; source /opt/prestashop-docker/.env; set +a
cd /opt/prestashop-docker
DATE=$(date +%Y%m%d_%H%M)
docker compose exec -T database \
mysqldump -u ps_user -p${MYSQL_PASSWORD} \
--single-transaction --routines --triggers \
prestashop | zstd -T0 > backups/db_${DATE}.sql.zst
# Backup des fichiers uploades (img/, modules custom)
docker run --rm -v prestashop-docker_prestashop_data:/data \
-v $(pwd)/backups:/backups alpine \
tar -czf /backups/files_${DATE}.tar.gz -C /data img upload
find backups -name "*.zst" -mtime +14 -delete
find backups -name "*.tar.gz" -mtime +14 -delete
Dans cron :
0 3 * * * /opt/prestashop-docker/scripts/backup.sh >> /var/log/ps-backup.log 2>&1
Erreurs courantes et leur fix
1. "DB connection refused" au demarrage
La base n'est pas encore prete. Le depends_on healthy resout la majorite des cas. Sinon ajuster le retries du healthcheck a 15 et interval a 5s.
2. Page blanche apres install, rien dans les logs Nginx
Logs de l'app PHP :
docker compose logs prestashop | tail -50
docker compose exec prestashop tail -f /var/www/html/var/logs/dev.log
C'est presque toujours memory_limit trop bas ou un module qui plante. Augmenter dans custom-php.ini puis docker compose restart prestashop.
3. "413 Request Entity Too Large" sur upload de produits
Les 3 limites a synchroniser :
- Nginx :
client_max_body_size 32M; - PHP :
upload_max_filesize = 32M; post_max_size = 32M; - PrestaShop : Parametres avancees > Performances > taille max upload
4. SSL expire et site inaccessible
Certbot tourne sur l'hote, pas dans le conteneur. Hook de renouvellement auto :
sudo nano /etc/letsencrypt/renewal-hooks/deploy/copy-to-docker.sh
#!/bin/bash
cp /etc/letsencrypt/live/shop.monsite.fr/*.pem /opt/prestashop-docker/nginx/ssl/
docker compose -f /opt/prestashop-docker/docker-compose.yml restart nginx
5. Pas d'envoi d'email depuis le conteneur
L'image officielle ne contient pas de MTA. Soit configurer SMTP externe (recommande) dans PrestaShop > Parametres avancees > Email :
Serveur SMTP : smtp.sendgrid.net
Utilisateur : apikey
Mot de passe : SG.xxxxxxxxxxxx
Port : 587
Chiffrement : TLS
Soit ajouter un service mailhog ou mailpit dans le compose pour le dev. Ne JAMAIS utiliser sendmail local en production : delivrabilite catastrophique. Cf configurer SPF DKIM DMARC.
6. Volumes accidentellement supprimes
J'ai failli faire la boulette une fois sur un serveur client a Marseille avec docker compose down -v. JAMAIS de -v sur la prod sans backup verifie.
En cas de drame, restaurer depuis le dernier dump et le tar.gz des fichiers.
Mise a l'echelle : load balancer et workers
Quand le trafic monte (>2000 visiteurs simultanes), le single-container PHP-FPM montre ses limites. Plusieurs strategies :
Augmenter les workers FPM
Dans custom-php-fpm.conf monte dans le conteneur :
pm = dynamic
pm.max_children = 30
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500
Regle empirique : max_children = (RAM dispo / memoire moyenne par worker). Sur 8 Go avec 50 Mo par worker, viser 30-40 workers max.
Repliquer le service
Docker Compose en mode swarm ou plusieurs replicas avec un haproxy devant :
prestashop:
deploy:
replicas: 3
resources:
limits:
cpus: '2'
memory: 2G
Le probleme : sessions et cache local. Solution : sessions dans Redis, cache en Redis, uploads sur volume partage NFS ou S3-compatible.
Sortir la base sur un VPS dedie
Des que la base depasse 5 Go ou que le CPU tourne a plus de 70% en pic, je sors MariaDB sur un autre VPS. Modification minimale :
services:
prestashop:
environment:
- DB_SERVER=db.interne.monsite.fr
- DB_PORT=3306
Et on supprime le service database du compose. Bonus : la base est sauvegardable independamment.
Pour aller plus loin
- Installer PrestaShop sur un VPS Ionos
- Optimiser les performances et le SEO PrestaShop
- Sauvegarder et restaurer une base MySQL
- Docker pour debutants : une app Node.js
- Configurer Redis comme cache
Logs et debug dans la stack Docker
Le principal piege quand on debute avec Docker : ne pas savoir ou sont les logs. Reflexes :
# Logs d'un service
docker compose logs prestashop
docker compose logs --tail=100 -f prestashop
# Logs de tous les services en parallele
docker compose logs -f --tail=50
# Filtrer une periode
docker compose logs --since 30m prestashop
# Logs dans le conteneur (logs applicatifs PrestaShop)
docker compose exec prestashop tail -f /var/www/html/var/logs/prod.log
# Stats temps reel
docker stats --no-stream
Pour la persistance, j'envoie tous les logs vers le syslog de l'hote en ajoutant dans chaque service :
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
Sans cette config, les logs Docker peuvent grossir indefiniment et remplir le disque. Cf logs Linux : ou chercher.
Une stack PrestaShop reproductible en 30 minutes
Une fois que vous avez gouter a Docker pour PrestaShop, le retour en arriere est tres dur. Tout est versionne dans Git (sauf .env et nginx/ssl/), reproductible sur n'importe quelle machine, et la migration vers un autre VPS prend 30 minutes : tar du dossier, restore sur la nouvelle machine, docker compose up -d. Pour les gros sites a fort trafic, vous devrez tuner Redis, ajouter Varnish devant Nginx, ou eclater la base sur un serveur dedie. Mais pour 90% des boutiques que je gere, cette stack tient sans broncher des annees.