Docker ha cambiato il modo in cui sviluppiamo applicazioni PHP. Non devi più installare PHP, MySQL, Nginx o Apache direttamente sulla tua macchina: un container gestisce tutto, e puoi distruggere e ricreare l'ambiente senza lasciare tracce.
In questa guida ti mostro un setup minimale, funzionante e replicabile per sviluppare qualsiasi progetto PHP in locale, dal classico WordPress a un'applicazione Laravel moderna.
Contenuto articolo
Perché Docker e non XAMPP o MAMP?
XAMPP e MAMP vanno bene per iniziare, ma hanno limiti precisi:
- Conflitti di versione — PHP 7.4 sulla macchina, il server di produzione usa 8.2, e il bug si vede solo in produzione
- Servizi diversi — Il tuo portatile ha MySQL, il server di produzione usa MariaDB o PostgreSQL
- Niente isolamento — Se rompi qualcosa, rischi di rompere tutto il sistema
Docker risolve tutto questo: ogni progetto ha il suo ambiente, con le versioni esatte che servono, e puoi condividerlo con il team senza che nessuno debba installare niente di specifico sulla propria macchina.
Il file docker-compose.yml minimale
La configurazione base per un progetto PHP con database MySQL e server web Nginx richiede tre servizi: php, mysql e nginx.
# docker-compose.yml versione minima funzionante
version: '3.8'
services:
app:
image: php:8.2-fpm
volumes:
- ./app:/var/www/html
depends_on:
- db
web:
image: nginx:alpine
ports:
- '8080:80'
volumes:
- ./app:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: app
MYSQL_USER: developer
MYSQL_PASSWORD: devpass
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
Questo file definisce tre container che comunicano su una rete Docker interna. La porta 8080 sulla tua macchina viene instradata alla porta 80 del container Nginx.
La configurazione Nginx
Il container Nginx ha bisogno di un file di configurazione per sapere come parlare con PHP-FPM:
# nginx.conf
server {
listen 80;
index index.php index.html;
root /var/www/html;
location / {
try_files / /index.php?;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME ;
include fastcgi_params;
}
}
Il parametro fastcgi_pass app:9000 dice a Nginx di inviare i file PHP al container app sulla porta 9000, che è la porta standard di PHP-FPM.
Primo avvio e verifica
Crea la struttura delle cartelle e il file index.php di test:
# Crea la struttura delle cartelle
mkdir -p app nginx
# Crea il file index.php di test
echo '<?php
phpinfo();
' > app/index.php
Ora avvia i container:
# Avvia tutti i servizi in background
docker-compose up -d
# Verifica che tutti i container siano attivi
docker-compose ps
Apri il browser e vai su http://localhost:8080. Se tutto funziona, vedrai la pagina phpinfo() con la configurazione di PHP 8.2.
Installare le estensioni PHP necessarie
L'immagine ufficiale php:8.2-fpm è minimale. Per WordPress o Laravel servono alcune estensioni. Puoi estendere l'immagine con un Dockerfile:
# Dockerfile
FROM php:8.2-fpm
# Installa le estensioni necessarie
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
Modifica il docker-compose.yml per usare il Dockerfile invece dell'immagine:
# Nel docker-compose.yml, sostituisci la riga:
# image: php:8.2-fpm
# Con:
build:
context: .
dockerfile: Dockerfile
Gestire i permessi file
Su Linux e macOS non avrai problemi di permessi. Su Windows con WSL2, potresti trovarti con file creati da root dentro i container. Per evitarlo:
# Imposta l'utente che matchi il tuo UID locale
services:
app:
user: '1003:1000'
# ... resto della config
Oppure, più semplicemente, usa un file .env:
# .env
UID=1003
GID=1003
Accesso al database da fuori Docker
Per collegare un client MySQL esterno (come TablePlus o DBeaver) al container:
# Aggiungi al servizio db nel docker-compose.yml:
db:
image: mysql:8.0
ports:
- '3306:3306'
# ... resto della config
Ora puoi collegarti a localhost:3306 con le credenziali definite nel file MYSQL_*.
Comandi essenziali da ricordare
| Comando | Funzione |
|---|---|
docker-compose up -d |
Avvia tutti i servizi in background |
docker-compose down |
Ferma e rimuove i container |
docker-compose down -v |
Ferma e rimuove anche i volumi (cancella il DB) |
docker-compose exec app bash |
Apre una shell dentro il container PHP |
docker-compose logs -f |
Mostra i log di tutti i container in tempo reale |
docker-compose restart |
Riavvia tutti i servizi |
WordPress in Docker: la versione completa
Per WordPress il discorso si allunga, perché servono volumi persistenti per wp-content, un container per Redis (utile per la cache), e la gestione dei certificati HTTPS in sviluppo. Il risultato è un setup del genere:
# Versione WordPress completa
services:
wordpress:
image: wordpress:6.4-php8.2
volumes:
- wordpress_data:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
depends_on:
- db
- redis
redis:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
wordpress_data:
db_data:
Conclusione
Docker non è magia: è un modo strutturato di dichiarare il tuo ambiente. Una volta che hai un docker-compose.yml funzionante, puoi distruggere tutto, ricrearlo su un'altra macchina, condividerlo con il team, e siete tutti sulla stessa configurazione. È il minimalismo che scale.
I prossimi passi:
- Prova ad aggiungere PHPMyAdmin come container
- Configura MailHog per intercettare le email in sviluppo
- Integra Blackfire o Tideways per il profiling
FAQ
Dipende da cosa fai girare. Un setup PHP+Nginx+MySQL minimale usa circa 500MB di RAM in idle. In confronto a un server LAMP completo, è molto meno.
Sì, ma devi usare WSL2 backend, non Docker Desktop Toolbox. WSL2 richiede Windows 10 21H2 o successivo, o Windows 11.
Con Xdebug attivo nel Dockerfile e una configurazione lato IDE che punta a host.docker.internal:9003, puoi usare breakpoints direttamente nel container. VS Code ha un launch.json già pronto per questo scenario.




Lascia un commento