f in x
Dockerfile Ottimizzato: Multi-Stage Build, Layer Caching e Sicurezza — Guida Operativa
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Analisi dei dati e metriche

Dockerfile Ottimizzato: Multi-Stage Build, Layer Caching e Sicurezza — Guida Operativa

[2026-06-05] Author: Ing. Calogero Bono

Hai un'immagine Docker da 1.2 GB, ci metti 12 minuti a buildare e alla fine scopri che dentro ci sono tool di sviluppo, cache di composer e chiavi SSH. Ti suona familiare? Noi lo vediamo continuamente nei progetti che ci arrivano in consulenza: immagini gonfie, build lente, vulnerabilità in produzione.

Un Dockerfile scritto male costa tempo e sicurezza. Un Dockerfile ottimizzato — con multi-stage build, layer caching e attenzione alla sicurezza — riduce le immagini del 70%, accelera le build del 40% e tiene fuori vulnerabilità evitabili.

Noi, di Meteora Web, non scriviamo Dockerfile solo per far funzionare le cose. Lo scriviamo perché quando deployi in produzione, ogni secondo di build e ogni MB in più sono un costo. Veniamo dalla contabilità: lo sappiamo bene.

Questa guida è pratica. Leggila, applicala, e il tuo prossimo Dockerfile sarà più veloce, più piccolo e più sicuro.

Perché il Multi-Stage Build è un cambio di paradigma

Il problema classico: per compilare un'applicazione PHP o Node ti servono tool di build (Composer, npm, compilatori C). Ma in produzione quei tool non servono a nulla. Pesano, introducono superficie d'attacco e allungano le build.

Il multi-stage build ti permette di avere più fasi (stage) in un unico Dockerfile. Ogni stage parte da una base diversa, ma solo l'ultimo stage finisce nell'immagine finale. Gli stage intermedi — dove installi tool, compili, scarichi dipendenze — vengono scartati.

Risultato: immagini snelle, build veloci, zero tool superflui in produzione.

Esempio concreto: un'applicazione PHP con Laravel

Vediamo un Dockerfile reale che parte da un'immagine enorme e la riduce con multi-stage.

# Stage 1: builder
FROM php:8.2-cli AS builder

WORKDIR /app

# Installiamo solo le estensioni necessarie per composer
RUN apt-get update && apt-get install -y --no-install-recommends \
    libzip-dev \
    unzip \
    git \
    && docker-php-ext-install zip pdo_mysql \
    && rm -rf /var/lib/apt/lists/*

# Copiamo composer installato globalmente (o installiamo)
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# Copia codice e installa dipendenze di produzione
COPY . .
RUN composer install --no-dev --optimize-autoloader

# Stage 2: produzione
FROM php:8.2-fpm-alpine AS production

WORKDIR /var/www/html

# Copiamo solo vendor e codice dallo stage builder
COPY --from=builder /app/vendor ./vendor
COPY --from=builder /app/app ./app
COPY --from=builder /app/config ./config
COPY --from=builder /app/public ./public
COPY --from=builder /app/routes ./routes
COPY --from=builder /app/resources ./resources
COPY --from=builder /app/storage ./storage
COPY --from=builder /app/.env .env

# Estensioni PHP minime per runtime
RUN docker-php-ext-install pdo_mysql

EXPOSE 9000
CMD ["php-fpm"]

L'immagine finale è almeno 200 MB più piccola rispetto a una singola fase con tool inclusi.

Layer Caching: sfrutta la cache, non combatterla

Docker costruisce le immagini per layer. Ogni istruzione RUN, COPY, ADD crea un layer. Se un layer non cambia, Docker lo riusa dalla cache. È il tuo migliore amico per la velocità.

Errori comuni:

  • Copiare tutto il codice prima di installare le dipendenze — ogni cambiamento del codice invalida la cache dei layer successivi, compresi quelli pesanti come RUN composer install.
  • Non ordinare le istruzioni per stabilità — metti prima ciò che cambia raramente (package.json, composer.lock, file di configurazione) e poi ciò che cambia spesso (codice).

Dockerfile ottimizzato per il caching

# Otteniamo il massimo dalla cache
FROM node:18-alpine AS builder
WORKDIR /app

# 1. Prima i file che cambiano raramente
COPY package.json package-lock.json ./
RUN npm ci --only=production

# 2. Poi solo i file che cambiano più spesso
COPY . .
RUN npm run build

Con questo approccio, se modifichi solo il codice sorgente (non le dipendenze), il layer RUN npm ci viene preso dalla cache e la build dura secondi, non minuti.

Sicurezza: ridurre la superficie d'attacco nel Dockerfile

Un'immagine Docker non è solo un contenitore: è un sistema operativo minimo. Ogni pacchetto installato è una potenziale vulnerabilità.

1. Usa immagini ufficiali e pinned

FROM php:8.2-fpm è meglio di FROM php:latest. Latest cambia e può romperti la build. Usa sempre tag specifici come php:8.2-fpm-alpine. Alpine Linux riduce la superficie d'attacco e le dimensioni.

2. Non eseguire mai come root

# Crea un utente non privilegiato
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

Se un attaccante compromette il processo PHP, non ha accesso root al container.

3. Pulisci cache e pacchetti temporanei nello stesso RUN

In un singolo RUN, installa, usa e rimuovi. I layer salvano solo lo stato finale.

RUN apt-get update && apt-get install -y --no-install-recommends \
    libzip-dev \
    && docker-php-ext-install zip \
    && rm -rf /var/lib/apt/lists/*

4. Non copiare file sensibili (chiavi SSH, .env con credenziali)

Usa variabili d'ambiente, secret mount (Docker BuildKit) o vault esterni. Mai COPY id_rsa ..

5. Scansiona le immagini con tool come Trivy o Docker Scout

Integra la scansione nella pipeline CI/CD. Noi usiamo trivy image --severity HIGH,CRITICAL my-image prima di ogni deploy.

Best practice combinate: multi-stage + caching + sicurezza

Ecco un Dockerfile completo per un'applicazione Node.js con Next.js che unisce tutto.

# Stage builder
FROM node:18-alpine AS builder
WORKDIR /app

# Dipendenze (cambiano raramente)
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Backup delle node_modules per lo stage finale
RUN cp -R node_modules /prod_modules

# Copia codice e build
COPY . .
RUN npm run build

# Stage produzione
FROM node:18-alpine AS production

# Utente non root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# Copia solo ciò che serve
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package.json ./

# Usa node_modules dal builder (produzione)
COPY --from=builder /prod_modules ./node_modules

# Esponi porta
EXPOSE 3000

USER appuser

CMD ["npm", "start"]

Analisi: immagine finale ~150 MB (contro ~1 GB con devDependencies), build veloce grazie a caching, zero pacchetti di sviluppo, utente non root.

Strumenti per controllare la qualità del Dockerfile

  • Hadolint — lint per Dockerfile. Installa e lancia: hadolint Dockerfile. Ti segnala best practice non rispettate.
  • Dive — analizza i layer delle immagini. Scopri cosa occupa spazio.
  • Docker Scout (integrato in Docker Desktop) — scansiona vulnerabilità.

In sintesi — cosa fare adesso

  1. Riscrivi il tuo Dockerfile con multi-stage. Separa build e runtime.
  2. Ordina le istruzioni per massimizzare il caching: file stabili prima, codice dopo.
  3. Usa immagini ufficiali pinned e preferisci Alpine per ridurre superficie.
  4. Crea un utente non root e usalo nel CMD.
  5. Pulisci cache e rimuovi tool di build nello stesso layer.
  6. Scansiona l'immagine con Trivy o Docker Scout prima del deploy.

Se applichi questi sei punti, il tuo prossimo deploy sarà più veloce, più sicuro e costerà meno in termini di risorse. Come dicevamo: un sito (o un container) si misura in fatturato e tempi, non in complimenti. Buona containerizzazione.

Sponsored Protocol

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Co-founder di Meteora Web. Ingegnere informatico, sviluppo ecosistemi digitali ad alte prestazioni. AI, automazione, SEO tecnica e infrastrutture web. Scrivo di tecnologia per rendere complesso… semplice.

[ Read Full Dossier ]

Hai bisogno di applicare questa strategia?

Esegui il protocollo di contatto per iniziare un progetto con noi.

> INIZIA_PROGETTO

Sponsored

> MW_JOURNAL

> READ_ALL()