← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Docker Multi-Stage Builds

devops PHP 5.0+ Intermediate

Also Known As

multi-stage build Docker builder pattern minimal Docker image

TL;DR

Dockerfile builds using multiple FROM stages — build dependencies (Composer, Node, test tools) in earlier stages, copy only production artifacts to the final minimal image.

Explanation

Multi-stage builds eliminate the need for separate Dockerfiles for build and production. The builder stage installs Composer and dev dependencies, runs tests, and compiles assets. The final stage copies only the production files — resulting in an image without Composer, dev packages, Xdebug, or build tools. A typical PHP app goes from a 800MB single-stage image to a 80MB multi-stage image. Smaller images: faster CI pulls, smaller attack surface, and lower registry storage costs.

Diagram

flowchart TD
    subgraph Builder Stage
        BASE1[php:8.3-fpm-alpine]
        COMP[Composer install<br/>dev dependencies]
        TEST[Run PHPUnit tests]
        BASE1 --> COMP --> TEST
    end
    subgraph Production Stage
        BASE2[php:8.3-fpm-alpine]
        COPY[COPY only src + vendor<br/>no Composer no dev deps]
        BASE2 --> COPY
    end
    TEST -->|copy artifacts| COPY
    COPY --> IMG[Final image<br/>80MB vs 800MB]
    style IMG fill:#238636,color:#fff

Common Misconception

Multi-stage builds are only for compiled languages — PHP apps benefit just as much: removing Composer, Xdebug, and dev dependencies from production images reduces size by 80% and attack surface significantly.

Why It Matters

A production PHP image with Xdebug, Composer, and dev dependencies installed is both slower to deploy and has a larger security attack surface — multi-stage builds solve both problems.

Common Mistakes

  • COPY --from=builder . . — copies everything including dev files; be specific about what to copy.
  • Not using .dockerignore — the build context includes node_modules, .git, and test files without it.
  • Running tests in the production stage — tests belong in the builder stage.
  • Not pinning base image versions — FROM php:latest changes unexpectedly.

Code Examples

✗ Vulnerable
# Single stage — dev deps in production:
FROM php:8.3-fpm
RUN apt-get install -y git zip unzip
COPY composer.json composer.lock ./
RUN curl -sS https://getcomposer.org/installer | php
RUN php composer.phar install  # Installs dev deps including Xdebug!
COPY . .
# Image: ~800MB with all dev tools
✓ Fixed
# Multi-stage — minimal production image:
FROM php:8.3-fpm-alpine AS builder
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
COPY . .
RUN vendor/bin/phpunit  # Tests in build stage

# Production — no Composer, no dev deps:
FROM php:8.3-fpm-alpine
WORKDIR /app
COPY --from=builder /app/public ./public
COPY --from=builder /app/src ./src
COPY --from=builder /app/vendor ./vendor
# Image: ~80MB

Added 16 Mar 2026
Edited 22 Mar 2026
Views 27
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 1 ping W 0 pings T
No pings yet today
Perplexity 8 Amazonbot 6 Unknown AI 2 Ahrefs 2 SEMrush 2 Majestic 1 Google 1
crawler 22
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Use a multi-stage Dockerfile: first stage runs composer install, second stage copies only vendor/ and src/ without build tools — keeps production image small and secure
📦 Applies To
PHP 5.0+ web cli
🔗 Prerequisites
🔍 Detection Hints
Single-stage Dockerfile with composer and build tools in production image; image >1GB; no .dockerignore file
Auto-detectable: ✓ Yes docker hadolint trivy dive
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Low ✗ Manual fix Fix: Medium Context: File

✓ schema.org compliant