1. Prerequisiti e installazione Podman

Installare Podman su Windows 11

Scarica e installa Podman per Windows dall'indirizzo ufficiale:

https://podman.io/

Scarica il file .exe dell'ultima versione stabile (es. podman-v5.x.x-setup.exe) ed eseguilo.

Inizializzare la Podman Machine

Podman su Windows utilizza una macchina virtuale Linux in background. Inizializzala con risorse adeguate allo sviluppo:

podman machine init --memory 4096

Viene creata una macchina virtuale con nome podman-machine-default. Volendo posso anche dare un nome alla macchina usando il comando podman machine init nome-macchina

Per avviare e fermare la macchina usa questi comandi

podman machine start
podman machine stop

I container possono girare in due modalità

  1. Rootless: I container girano senza privilegi di root
  2. Rootful: I container girano come root dentro la VM

Se voglio far girare la macchine con privilegi di root lanciare il seguente comando e poi fermare e riavviare la macchina

podman machine set --rootful
podman machine stop
podman machine start

Per vedere la macchina virtuale installata

podman machine list

Nel caso in cui desideri cancellare e ricreare la macchina usa

podman machine rm

Verifica che tutto funzioni:

podman version
podman info

Per vedere la macchina installa con wsl lanciare il seguente comando

wsl -l -v

Installare podman-compose

podman-compose permette di orchestrare più container tramite un file compose.yaml, ed è l'equivalente di docker-compose per Podman.

pip install podman-compose

Se non hai Python installato, scaricalo da https://python.org e assicurati di aggiungere Python al PATH durante l'installazione.

2. Struttura del progetto

Crea questa struttura di cartelle. La cartella D:\temp\wamp-podman contiene tutta la configurazione dei container. I tuoi siti web risiedono nella sottocartella www e vengono montati nel container senza essere copiati al suo interno.

D:\temp\wamp-podman\
│
├── .env                          ← tutte le variabili configurabili qui
├── compose.yaml                  ← orchestrazione dei container
│
├── php-apache\
│   ├── Dockerfile                ← immagine custom PHP 8.5.4-fpm + Apache
│   ├── apache\
│   │   ├── vhosts.conf           ← configurazione virtual host Apache
│   │   └── start.sh              ← script di avvio PHP-FPM + Apache
│   └── php\
│       ├── custom.ini            ← impostazioni PHP aggiuntive
│       ├── xdebug.ini            ← configurazione Xdebug
│       └── fpm-pool.conf         ← configurazione pool PHP-FPM
│
└── www\                          ← i tuoi siti (solo montata nel container)
    ├── sito1\
    ├── sito2\
    └── ...

3. File di configurazione centrale .env

Questo è il punto unico dove modificare tutte le variabili dell'ambiente. Non dovrai mai toccare altri file per cambiare porte, credenziali o percorsi.

Percorso: D:\temp\wamp-podman\.env

# ─────────────────────────────────────────────
#  PERCORSI
# ─────────────────────────────────────────────

# Percorso della cartella che contiene i tuoi siti web.
# Usa la sintassi con slash (/).
SITES_ROOT=D:/temp/wamp-podman/www

# ─────────────────────────────────────────────
#  PORTE HOST
# ─────────────────────────────────────────────

# Porta su cui Apache risponde su Windows (http://localhost:APACHE_PORT)
APACHE_PORT=8080

# Porta interna del container su cui Apache ascolta
# Per wordpress tenere allineato questo valore con APACHE_PORT
APACHE_INTERNAL_PORT=8080

# Porta su cui phpMyAdmin è raggiungibile da Windows
PHPMYADMIN_PORT=8081

# Porta interna del container su cui phpMyAdmin ascolta
PHPMYADMIN_INTERNAL_PORT=80

# Porta per MariaDB (utile per client esterni come DBeaver)
MARIADB_PORT=3306

# ─────────────────────────────────────────────
#  MARIADB
# ─────────────────────────────────────────────

MARIADB_ROOT_PASSWORD=root

# ─────────────────────────────────────────────
#  XDEBUG
# ─────────────────────────────────────────────

# Porta su cui VSCode ascolta per le connessioni Xdebug
XDEBUG_PORT=9003

# ─────────────────────────────────────────────
#  NOMI CONTAINER
# ─────────────────────────────────────────────

COMPOSE_PROJECT_NAME=devenv

4. Dockerfile per PHP-FPM + Apache

Questo Dockerfile usa come base php:8.5.4-fpm, che include PHP-FPM ma non un web server. Apache viene installato tramite apt-get e configurato per comunicare con PHP-FPM tramite FastCGI usando il modulo mod_proxy_fcgi. PHP-FPM ascolta su socket Unix per prestazioni migliori rispetto a TCP.

Percorso: D:\temp\wamp-podman\php-apache\Dockerfile

FROM php:8.5.4-fpm

RUN apt-get update

RUN apt-get install -y apache2 libapache2-mod-fcgid

RUN apt-get install -y libzip-dev libpng-dev libjpeg62-turbo-dev libwebp-dev libfreetype6-dev libonig-dev libxml2-dev libcurl4-openssl-dev libssl-dev libmagickwand-dev libicu-dev libxslt1-dev libsqlite3-dev unzip curl git nano

RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && docker-php-ext-install gd
RUN docker-php-ext-install bcmath calendar exif gettext
RUN docker-php-ext-install intl mbstring mysqli
RUN docker-php-ext-install pcntl pdo pdo_mysql pdo_sqlite
RUN docker-php-ext-install soap sockets xsl zip

RUN pecl install imagick
RUN docker-php-ext-enable imagick

RUN pecl install redis
RUN docker-php-ext-enable redis

RUN pecl install xdebug
RUN docker-php-ext-enable xdebug

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

# proxy_fcgi permette ad Apache di passare le richieste PHP a PHP-FPM via FastCGI
# setenvif è richiesto da proxy_fcgi
RUN a2enmod proxy_fcgi setenvif rewrite headers expires deflate ssl vhost_alias

COPY apache/vhosts.conf /etc/apache2/sites-available/000-default.conf

COPY php/custom.ini /usr/local/etc/php/conf.d/custom.ini
COPY php/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
COPY php/fpm-pool.conf /usr/local/etc/php-fpm.d/www.conf

COPY apache/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh

RUN usermod -u 1000 www-data
RUN groupmod -g 1000 www-data

WORKDIR /var/www/html

EXPOSE 80

CMD ["/usr/local/bin/start.sh"]

Script di avvio

Poiché il container deve eseguire sia PHP-FPM che Apache, serve uno script che li avvii entrambi. L'immagine base php:8.5.4-fpm ha come CMD predefinito solo php-fpm: lo script lo sostituisce avviando entrambi i processi.

Percorso: D:\temp\wamp-podman\php-apache\apache\start.sh

#!/bin/bash
export APACHE_LOG_DIR=/var/log/apache2
export APACHE_RUN_DIR=/var/run/apache2
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
export APACHE_LOCK_DIR=/var/lock/apache2
export APACHE_PID_FILE=/var/run/apache2/apache2.pid

# Porta dinamica da variabile d'ambiente, default 80 se non impostata
APACHE_INTERNAL_PORT=${APACHE_INTERNAL_PORT:-80}

# Imposta la porta di ascolto
echo "Listen ${APACHE_INTERNAL_PORT}" > /etc/apache2/ports.conf

# Sostituisce la porta nei VirtualHost
sed -i "s/\*:APACHE_PORT/\*:${APACHE_INTERNAL_PORT}/g" /etc/apache2/sites-available/000-default.conf

mkdir -p /var/run/php
chown www-data:www-data /var/run/php

php-fpm &

apache2ctl -D FOREGROUND

5. Configurazione Apache

Apache riceve le richieste HTTP e passa quelle PHP a PHP-FPM tramite FastCGI usando la direttiva SetHandler proxy:unix. Il socket Unix /var/run/php/php-fpm.sock è il canale di comunicazione tra Apache e PHP-FPM all'interno del container.

Percorso: D:\temp\wamp-podman\php-apache\apache\vhosts.conf

# ─── Virtual Host di default ─────────
<VirtualHost *:APACHE_PORT>
    ServerName localhost
    DocumentRoot /var/www/html

    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/var/run/php/php-fpm.sock|fcgi://localhost"
    </FilesMatch>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

# ─── Virtual Host dedicato per progetti Laravel ──────────
# La DocumentRoot punta alla sottocartella /public.
# Duplica questo blocco per ogni progetto Laravel aggiuntivo.
# Ricorda di aggiungere il dominio al file hosts di Windows:
#   C:\Windows\System32\drivers\etc\hosts
#   127.0.0.1   sito1.localhost

<VirtualHost *:APACHE_PORT>
    ServerName sito1.localhost
    DocumentRoot /var/www/html/sito1/public

    <Directory /var/www/html/sito1/public>
        AllowOverride All
        Require all granted
    </Directory>

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/var/run/php/php-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

# ─── Virtual Host wildcard per WordPress e siti senza /public ────────────────
# Gestisce automaticamente tutti i siti la cui DocumentRoot
# è la cartella radice del progetto, senza aggiungere un VirtualHost per ognuno.

<VirtualHost *:APACHE_PORT>
    ServerName wildcard.localhost
    ServerAlias *.localhost
    UseCanonicalName Off
    VirtualDocumentRoot /var/www/html/%1

    <Directory /var/www/html>
        AllowOverride All
        Require all granted
    </Directory>

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/var/run/php/php-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

Configurazione pool PHP-FPM

Sovrascrive la configurazione di default del pool PHP-FPM per usare un socket Unix invece di TCP, che è più performante per comunicazioni locali all'interno del container.

Percorso: D:\temp\wamp-podman\php-apache\php\fpm-pool.conf

[www]
user = www-data
group = www-data

listen = /var/run/php/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6

6. Configurazione PHP

Percorso: D:\temp\wamp-podman\php-apache\php\custom.ini

; ─── Impostazioni generali ───────────────────────────────────────────────────
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On

; ─── Limiti di risorse ───────────────────────────────────────────────────────
memory_limit = 512M
max_execution_time = 120
max_input_time = 120
max_input_vars = 3000

; ─── Upload ──────────────────────────────────────────────────────────────────
upload_max_filesize = 64M
post_max_size = 64M

; ─── Timezone ────────────────────────────────────────────────────────────────
date.timezone = Europe/Rome

; ─── OPcache ─────────────────────────────────────────────────────────────────
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
; 0 = nessuna cache del timestamp: Apache vede sempre i file aggiornati
opcache.revalidate_freq = 0

; ─── Sessioni ────────────────────────────────────────────────────────────────
session.gc_maxlifetime = 3600

7. Configurazione Xdebug per VSCode

Percorso: D:\temp\wamp-podman\php-apache\php\xdebug.ini

zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20250925/xdebug.so

[xdebug]
xdebug.mode = debug
xdebug.start_with_request = yes

; host.containers.internal è l'indirizzo speciale che Podman usa per
; raggiungere il sistema host (Windows) dal container.
xdebug.client_host = host.containers.internal

; Deve corrispondere a XDEBUG_PORT nel file .env
xdebug.client_port = 9003

xdebug.log_level = 0
xdebug.idekey = VSCODE

8. File compose.yaml

Percorso: D:\temp\wamp-podman\compose.yaml

name: ${COMPOSE_PROJECT_NAME}

networks:
  devnet:
    driver: bridge

volumes:
  mariadb_data:
    driver: local

services:

  php-apache:
    build:
      context: ./php-apache
      dockerfile: Dockerfile
    container_name: ${COMPOSE_PROJECT_NAME}_php
    restart: unless-stopped
    ports:
      - "${APACHE_PORT}:${APACHE_INTERNAL_PORT}"
    volumes:
      - "./www:/var/www/html:z"
    environment:
      - XDEBUG_CONFIG=client_host=host.containers.internal
      - APACHE_INTERNAL_PORT=${APACHE_INTERNAL_PORT}
    extra_hosts:
      - "host.containers.internal:host-gateway"
    networks:
      - devnet
    depends_on:
      - mariadb

  mariadb:
    image: mariadb:12.2
    container_name: ${COMPOSE_PROJECT_NAME}_db
    restart: unless-stopped
    ports:
      - "${MARIADB_PORT}:3306"
    volumes:
      - mariadb_data:/var/lib/mysql:z
    environment:
      MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
    networks:
      - devnet

  phpmyadmin:
    image: phpmyadmin:latest
    container_name: ${COMPOSE_PROJECT_NAME}_pma
    restart: unless-stopped
    ports:
      - "${PHPMYADMIN_PORT}:${PHPMYADMIN_INTERNAL_PORT}"
    environment:
      PMA_HOST: mariadb
      PMA_PORT: 3306
      UPLOAD_LIMIT: 64M
    networks:
      - devnet
    depends_on:
      - mariadb

9. Avvio e gestione dell'ambiente

Apri PowerShell nella cartella D:\temp\wamp-podman.

Prima build

Da eseguire solo la prima volta, o dopo modifiche al Dockerfile:

cd D:\temp\wamp-podman
podman-compose build

Nel caso in cui si verificono problemi, a container avviati, per ricostruire tutto

# ferma e rimuove i container in esecuzione. I dati nel volume MariaDB restano intatti.
podman-compose down

# ricostruisce le immagini leggendo il Dockerfile da zero, ignorando qualsiasi layer memorizzato nella cache. 
# Necessario quando modifichi il Dockerfile o i file che copia
podman-compose build --no-cache

# crea e avvia i container basandosi sulle immagini appena costruite
podman-compose up -d

Avviare, fermare e verificare i container

# Avvia tutti i container in background
podman-compose up -d

# Verifica che i container siano in esecuzione
podman ps

# Ferma i container senza rimuoverli
podman-compose stop

# Ferma e rimuove i container (i dati del DB rimangono nel volume)
podman-compose down

Accedere ai servizi

Siti web       →  http://localhost:8080
Sito specifico →  http://sito1.localhost:8080
phpMyAdmin     →  http://localhost:8081

Log e shell

# Log in tempo reale di tutti i container
podman-compose logs -f

# Log di un singolo container
podman logs -f devenv_php

# Aprire una shell nel container PHP
podman exec -it devenv_php bash

All'interno del container puoi eseguire comandi Composer e Artisan direttamente nell'ambiente PHP:

composer install
php artisan migrate
php -m

10. Configurazione VSCode per il debug

Installare l'estensione PHP Debug

In VSCode installa l'estensione PHP Debug di Xdebug (id: xdebug.php-debug).

Creare il file launch.json

Percorso: D:\temp\wamp-podman\www\sito1\.vscode\launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for Xdebug (Podman)",
      "type": "php",
      "request": "launch",
      "port": 9003,
      "pathMappings": {
        "/var/www/html/sito1": "${workspaceFolder}"
      }
    }
  ]
}

Il nodo pathMappings è fondamentale: mappa il percorso del file dentro il container con il percorso corrispondente su Windows. Aggiorna /var/www/html/sito1 con il nome effettivo della cartella del tuo progetto.

Avviare una sessione di debug

  1. Apri il progetto in VSCode
  2. Vai nel pannello Run and Debug (Ctrl+Shift+D)
  3. Seleziona Listen for Xdebug (Podman)
  4. Premi F5 o clicca il triangolo verde
  5. Naviga nel browser: Xdebug si connette automaticamente e si ferma ai breakpoint

11. Uso quotidiano

Avvio/Stop rapido

podman machine start
cd D:\temp\wamp-podman
podman-compose up -d
podman-compose stop

Modificare una variabile

Apri D:\temp\wamp-podman\.env, modifica il valore desiderato, poi riavvia i container:

podman-compose down
podman-compose up -d

Aggiungere un nuovo sito

Crea la cartella del sito in D:\temp\wamp-podman\www\nuovo-sito\, poi aggiungi una riga al file hosts di Windows (come amministratore):

127.0.0.1   nuovo-sito.localhost

Il file hosts si trova in C:\Windows\System32\drivers\etc\hosts.

Se il sito usa Laravel, aggiungi un VirtualHost dedicato in vhosts.conf con DocumentRoot che punta a /var/www/html/nuovo-sito/public, poi ricostruisci:

podman-compose down
podman-compose build
podman-compose up -d

Se il sito è WordPress o ha la DocumentRoot nella root del progetto, il virtual host wildcard lo gestisce automaticamente senza modifiche.

Gestire i database

# Via browser
http://localhost:8081

# Via CLI nel container
podman exec -it devenv_db mariadb -u root -p

Backup e ripristino del database

# Esporta tutti i database
podman exec devenv_db mariadb-dump -u root -prootpassword --all-databases > backup.sql

# Ripristino
podman exec -i devenv_db mariadb -u root -prootpassword < backup.sql

Controllare i moduli PHP attivi

podman exec devenv_php php -m

Avviare Podman machine automaticamente con Windows

Per evitare di eseguire manualmente podman machine start ogni giorno, crea un Task nel Task Scheduler di Windows con questi parametri:

Trigger:   All'avvio di Windows
Programma: podman
Argomenti: machine start