Docker multi-stage build knowledge base. Provides patterns for PHP dependency, extension builder, and production stages with cache optimization.
From accnpx claudepluginhub dykyi-roman/awesome-claude-code --plugin accThis skill uses the workspace's default tool permissions.
Patterns and best practices for multi-stage Docker builds in PHP applications.
┌─────────────────────────────────────────────────────────────────────────────┐
│ MULTI-STAGE BUILD PIPELINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Stage 1: deps Stage 2: extensions Stage 3: build │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Composer │ │ PHP Ext │ │ App Build │ │
│ │ Install │ │ Compile │ │ Assets │ │
│ │ │ │ │ │ Optimize │ │
│ │ vendor/ │ │ *.so files │ │ Cache warm │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ │ COPY --from=deps │ COPY --from=extensions│ │
│ └────────────┐ ┌──────┘ ┌───────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────┐ │
│ │ Stage 4: production │ │
│ │ ┌───────────────────┐ │ │
│ │ │ Minimal base │ │ │
│ │ │ + vendor/ │ │ │
│ │ │ + extensions │ │ │
│ │ │ + app code │ │ │
│ │ │ + optimized cfg │ │ │
│ │ └───────────────────┘ │ │
│ │ Final image: ~80MB │ │
│ └─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
| Stage Name | Purpose | Base Image |
|---|---|---|
deps | Composer dependency installation | composer:2 |
extensions | PHP extension compilation | php:8.4-fpm-alpine |
build | Asset compilation, cache warmup | php:8.4-cli-alpine |
dev | Development with Xdebug, tools | php:8.4-fpm-alpine |
production | Final minimal runtime image | php:8.4-fpm-alpine |
# syntax=docker/dockerfile:1
FROM composer:2 AS deps
WORKDIR /app
# Copy only dependency manifests first for layer caching
COPY composer.json composer.lock ./
# Install production deps only (no dev)
RUN composer install \
--no-dev \
--no-scripts \
--no-autoloader \
--prefer-dist \
--no-interaction
# Copy source and dump optimized autoloader
COPY src/ src/
COPY config/ config/
RUN composer dump-autoload --optimize --classmap-authoritative
FROM php:8.4-fpm-alpine AS extensions
RUN apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
icu-dev \
libpq-dev \
libzip-dev \
freetype-dev \
libjpeg-turbo-dev \
libpng-dev \
&& docker-php-ext-configure gd \
--with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
intl \
pdo_pgsql \
zip \
gd \
opcache \
&& pecl install redis apcu \
&& docker-php-ext-enable redis apcu \
&& apk del .build-deps
FROM php:8.4-fpm-alpine AS production
# Runtime-only dependencies (no build tools)
RUN apk add --no-cache \
icu-libs \
libpq \
libzip \
freetype \
libjpeg-turbo \
libpng
# Copy compiled extensions from builder stage
COPY --from=extensions /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=extensions /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
# Copy vendor directory from deps stage
COPY --from=deps /app/vendor /app/vendor
# Copy application code
COPY . /app
WORKDIR /app
# PHP production configuration
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
USER www-data
EXPOSE 9000
CMD ["php-fpm"]
# Cache composer packages across builds
RUN --mount=type=cache,target=/root/.composer/cache \
composer install --no-dev --optimize-autoloader
# Cache APK packages across builds
RUN --mount=type=cache,target=/var/cache/apk \
apk add --no-cache icu-dev libpq-dev
# Ordered from least to most frequently changed:
1. Base image + system packages (rarely changes)
2. PHP extensions (changes with requirements)
3. Composer dependencies (changes with composer.lock)
4. Application configuration (changes occasionally)
5. Application source code (changes frequently)
# Build with cache from registry
docker build \
--cache-from=registry.io/app:deps-cache \
--cache-from=registry.io/app:latest \
--target production \
-t registry.io/app:latest .
# Global ARG (before first FROM) — available in FROM lines only
ARG PHP_VERSION=8.4
FROM php:${PHP_VERSION}-fpm-alpine AS base
# PHP_VERSION is NOT available here unless redeclared
ARG PHP_VERSION
RUN echo "Building with PHP ${PHP_VERSION}"
FROM php:${PHP_VERSION}-cli-alpine AS build
# Must redeclare ARG in each stage that needs it
ARG APP_ENV=prod
RUN echo "Building for ${APP_ENV}"
# Copy from named stage
COPY --from=deps /app/vendor /app/vendor
# Copy from external image
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
# Copy specific extension files
COPY --from=extensions /usr/local/lib/php/extensions/no-debug-non-zts-*/ \
/usr/local/lib/php/extensions/no-debug-non-zts-*/
# Copy with ownership
COPY --from=build --chown=www-data:www-data /app /app
# Enable BuildKit for parallel stage execution
DOCKER_BUILDKIT=1 docker build .
# Or via docker buildx
docker buildx build --target production .
BuildKit automatically detects independent stages and builds them in parallel:
deps ──────────────┐
├──▶ production
extensions ────────┘
│
build ──── (depends on deps, runs after)
# Build only development image
docker build --target dev -t app:dev .
# Build only production image
docker build --target production -t app:prod .
# Build specific intermediate stage for debugging
docker build --target extensions -t app:ext-debug .
| Technique | Savings | Example |
|---|---|---|
| Alpine base | ~80MB | php:8.4-fpm-alpine vs php:8.4-fpm |
| Multi-stage (no build tools) | ~200MB | Separate builder from runtime |
.dockerignore | Variable | Exclude .git, tests/, docs/ |
| No dev dependencies | ~50MB | composer install --no-dev |
| Optimized autoloader | ~5MB | --classmap-authoritative |
| Removed package cache | ~10MB | apk add --no-cache or rm -rf /var/cache/apk/* |
For base image selection guidance, see docker-base-images-knowledge.
For extension installation details, see docker-php-extensions-knowledge.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.