Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungWhat is Programming And How i can Enjoy it?(24.09.2026 um 11:54 Uhr)
Sichere ProgrammierungYou Don't Need Adobe Commerce Cloud to Survive Black Friday(24.09.2026 um 11:55 Uhr)
Malware / Trojaner / VirenBeyond Lazarus: Organization of DPRK cyber capabilities(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenBeyond Lazarus: Organization of DPRK Cyber Capabilities(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenThe fake worker threat and the rise of human infiltration(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenPolinRider Spreads Through Compromised GitHub Accounts and Packagist(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenWeaselBiscuit Strips BeaverTail and OtterCookie Down to Essentials(24.09.2026 um 11:59 Uhr)
Sichere ProgrammierungWhat is Programming And How i can Enjoy it?(24.09.2026 um 11:54 Uhr)
Sichere ProgrammierungYou Don't Need Adobe Commerce Cloud to Survive Black Friday(24.09.2026 um 11:55 Uhr)
Malware / Trojaner / VirenBeyond Lazarus: Organization of DPRK cyber capabilities(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenBeyond Lazarus: Organization of DPRK Cyber Capabilities(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenThe fake worker threat and the rise of human infiltration(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenPolinRider Spreads Through Compromised GitHub Accounts and Packagist(24.09.2026 um 11:59 Uhr)
Malware / Trojaner / VirenWeaselBiscuit Strips BeaverTail and OtterCookie Down to Essentials(24.09.2026 um 11:59 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Docker Image with Multi-Stage Builds & Docker Images Optimization

“Docker is not a virtualization technology. It is an application delivery technology.” - Mike Coleman (Docker) Table of Contents Introduction Overview: Multi-Stage Build Strategy (Builder + Production) Stage 1: Builder - Com…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

“Docker is not a virtualization technology. It is an application delivery technology.” - Mike Coleman (Docker)







Table of Contents




  1. Introduction

  2. Overview: Multi-Stage Build Strategy (Builder + Production)

  3. Stage 1: Builder - Compile Extensions, Install Tools & Dependencies

  4. Stage 2: Production - Minimal Runtime Image

  5. The .dockerignore File

  6. Practical Setup (Step-by-step)

  7. Quotes

  8. FAQs

  9. Key Takeaways

  10. Conclusion






1. Introduction



This document explains how to build a production-ready PHP 8.4-FPM Docker image using a multi-stage Dockerfile. The image ships with all the PHP extensions, CLI tools (Composer, WP-CLI, Drush, Drupal Console), and Node.js needed for WordPress and Drupal projects - while keeping the final image lean and secure.



The approach uses a two-stage build:




  • Stage 1 (Builder): Compiles PHP extensions, installs development tools, and downloads CLI utilities.

  • Stage 2 (Production): Starts from a clean php:8.4-fpm base, copies only the compiled artifacts and runtime libraries, applies security hardening, and runs as a non-root user.



Why multi-stage?



A single-stage Dockerfile that compiles extensions and installs build tools leaves behind compilers, header files, and package caches that bloat the image and increase the attack surface. Multi-stage builds solve this by discarding everything that is not needed at runtime.






2. Overview: Multi-Stage Build Strategy (Builder + Production)



Stage 1 (Builder) starts from php:8.4-fpm and installs: - 20+ PHP extensions using install-php-extensions (handles build dependencies and cleanup internally). - Composer, WP-CLI, Drush, and Drupal Console. - Node.js 20.x for front-end tooling.



Stage 2 (Production) starts from a fresh php:8.4-fpm and: - Copies compiled extension .so files and their .ini configs from the builder. - Copies CLI binaries (Composer, WP-CLI, Drush, Drupal Console, Node.js). - Installs only the runtime shared libraries needed by the extensions. - Applies OPcache tuning and PHP security settings. - Creates a non-root user (appuser) for PHP-FPM. - Add a health check.



Why separate stages?




  • Smaller image: Build tools (gcc, make, autoconf, header files) are discarded. Only runtime libraries remain.

  • Faster pulls and deploys: Less data to transfer across registries and hosts.

  • Reduced attack surface: No compilers or dev packages in production.

  • Clearer troubleshooting: Build failures are isolated to Stage 1; runtime issues are isolated to Stage 2.






3. Stage 1: Builder - Compile Extensions, Install Tools & Dependencies



3.1 Base Image



FROM php:8.4-fpm AS builder

We use the official php:8.4-fpm image (based on Debian 13 Trixie) as the builder base. This gives us the PHP source, phpize, and docker-php-ext-install out of the box.



3.2 PHP Extension Installation



Single tool for all PHP extensions (handles build deps + cleanup internally)

COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/



Install all PHP extensions in one layer

RUN install-php-extensions \

bcmath \

bz2 \

calendar \

exif \

gd \

gmp \

imagick \

intl \

mailparse \

mongodb \

mysqli \

opcache \

pcntl \

pdo \

pdo_mysql \

pdo_pgsql \

soap \

sockets \

sodium \

xsl \

zip



Why install-php-extensions?




  • It automatically installs the OS-level build dependencies (e.g., libpng-dev, libicu-dev), compiles the extension, and removes the build dependencies - all in one step.

  • It replaces the manual apt-get install && docker-php-ext-configure && docker-php-ext-install && apt-get purge pattern.

  • It supports PECL extensions (like imagick, mongodb, mailparse) with the same syntax.

  • We use COPY --from=mlocati/php-extension-installer to pull the binary directly from the tool’s official image, without adding another FROM stage.



Extensions included and why:







3.3 CLI Tools Installation

Install Composer, WP-CLI, Drush, and Drupal Console in one layer

RUN apt-get update && apt-get install -y --no-install-recommends \

git \

unzip \

&& rm -rf /var/lib/apt/lists/* \

Composer

&& curl -sSL https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \

WP-CLI

&& curl -sSL https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -o /usr/local/bin/wp \

&& chmod +x /usr/local/bin/wp \

Drupal Console

&& curl -sSL https://drupalconsole.com/installer -o /usr/local/bin/drupal \

&& chmod +x /usr/local/bin/drupal




  • Composer: PHP dependency manager. Installed globally at /usr/local/bin/composer.

  • WP-CLI: WordPress command-line interface for managing WordPress installations.

  • Drupal Console: CLI tool for generating boilerplate code and interacting with Drupal.

  • git and unzip are needed by Composer to fetch packages; they are installed here but not carried over to the production stage.



3.4 Drush Installation



ENV COMPOSER_HOME=/usr/local/share/composer

RUN composer global require drush/drush:8.* --no-interaction --prefer-dist

Drush (Drupal Shell) is installed globally via Composer. COMPOSER_HOME is set explicitly so the entire vendor directory can be copied to the production stage.



3.5 Node.js Installation



RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \

&& apt-get install -y --no-install-recommends nodejs \

&& rm -rf /var/lib/apt/lists/*

Node.js 20.x (LTS) is installed for front-end build tools (npm scripts, asset compilation). NVM is intentionally avoided - a single global Node.js installation is simpler and more predictable in a container.




“Build once, run anywhere - but make sure what you build is only what you need.” - Container best practice principle







4. Stage 2: Production - Minimal Runtime Image



4.1 Fresh Base and Labels



FROM php:8.4-fpm AS production



LABEL maintainer="AddWeb Solutions" \

description="PHP 8.4-FPM production image with WordPress/Drupal tooling"

A fresh php:8.4-fpm base - none of the builder’s build tools, caches, or temp files exist here.



4.2 Copying Artifacts from Builder



Copy PHP extensions and their configs from builder

COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/

COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/



Copy CLI tools from builder

COPY --from=builder /usr/local/bin/composer /usr/local/bin/composer

COPY --from=builder /usr/local/bin/wp /usr/local/bin/wp

COPY --from=builder /usr/local/bin/drupal /usr/local/bin/drupal

COPY --from=builder /usr/local/share/composer /usr/local/share/composer



Copy Node.js from builder

COPY --from=builder /usr/bin/node /usr/bin/node

COPY --from=builder /usr/bin/npm /usr/bin/npm

COPY --from=builder /usr/bin/npx /usr/bin/npx

COPY --from=builder /usr/lib/node_modules /usr/lib/node_modules

COPY --from=builder selectively pulls only the compiled .so files, .ini configs, and CLI binaries. Everything else (compilers, header files, build caches) is left behind.



4.3 Runtime Libraries



RUN apt-get update && apt-get install -y --no-install-recommends \

# Runtime libs required by PHP extensions

libbz2-1.0 \

libfreetype6 \

libgmp10 \

libicu76 \

libjpeg62-turbo \

libmagickwand-7.q16-10 \

libavif16 \

libpng16-16 \

libpq5 \

libxslt1.1 \

libzip5 \

# Essential runtime utilities only

cron \

curl \

default-mysql-client \

ffmpeg \

sendmail \

supervisor \

unzip \

sqlite3 \

&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \

&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*



Key points:




  • Runtime libs only: We install the shared libraries (.so files) that the compiled PHP extensions link against. These are the -dev-less counterparts (e.g., libpng16-16 not libpng-dev).

  • Debian Trixie package names: Since php:8.4-fpm is based on Debian 13 (Trixie), some package names differ from Bookworm (e.g., libicu76 instead of libicu72, libmagickwand-7.q16-10 instead of libmagickwand-6.q16-6, libzip5 instead of libzip4).

  • Essential utilities: cron (scheduled tasks), curl (health checks, API calls), default-mysql-client (database operations), ffmpeg (media processing), sendmail (email delivery), supervisor (process management), unzip, sqlite3.

  • Cleanup: apt-get purge --auto-remove removes packages that were pulled in only as install dependencies, and rm -rf /var/lib/apt/lists/* clears the package cache.



Runtime libraries mapped to extensions:





4.4 Drush Symlink



ENV COMPOSER_HOME=/usr/local/share/composer

RUN ln -s /usr/local/share/composer/vendor/bin/drush /usr/local/bin/drush

Makes drush available in $PATH without modifying the PATH variable.



4.5 OPcache Tuning



RUN { \

echo 'opcache.memory_consumption=128'; \

echo 'opcache.interned_strings_buffer=8'; \

echo 'opcache.max_accelerated_files=4000'; \

echo 'opcache.revalidate_freq=60'; \

echo 'opcache.fast_shutdown=1'; \

echo 'opcache.enable_cli=1'; \

echo 'opcache.validate_timestamps=0'; \

} > /usr/local/etc/php/conf.d/opcache-recommended.ini





validate_timestamps=0 is a production optimization - PHP never starts the filesystem to check if files have changed. When you deploy new code, restart PHP-FPM to pick up the changes.



4.6 Security Hardening



RUN { \

echo 'expose_php=Off'; \

echo 'display_errors=Off'; \

echo 'log_errors=On'; \

echo 'error_log=/dev/stderr'; \

echo 'allow_url_fopen=Off'; \

} > /usr/local/etc/php/conf.d/security.ini





4.7 Non-Root User



RUN groupadd -g 1000 appuser && useradd -u 1000 -g appuser -m appuser



WORKDIR /var/www/html

RUN chown -R appuser:appuser /var/www/html

Running PHP-FPM as a non-root user reduces the blast radius if the application is compromised. The appuser (UID 1000) owns the web root.



4.8 Health Check



HEALTHCHECK --interval=30s --timeout=5s --retries=3 \

CMD php-fpm-healthcheck || kill -0 $(cat /var/run/php-fpm.pid 2>/dev/null) || exit 1

Docker checks every 30 seconds whether PHP-FPM is responding. After 3 consecutive failures, the container is marked unhealthy, and orchestrators (Docker Compose, Swarm, Kubernetes) can restart it.



4.9 Expose and CMD



EXPOSE 9000



CMD ["php-fpm"]

PHP-FPM listens on port 9000 (FastCGI). A reverse proxy (Nginx, Apache, Caddy) forwards requests to this port.






5. The .dockerignore File



.git

.github

.gitignore

.md

LICENSE

docker-compose
.yml

.env*

.vscode

.idea

node_modules

vendor

.docker



The .dockerignore prevents unnecessary files from being sent to the Docker build context. This speeds up builds and avoids leaking secrets (.env files) or bloating the image with node_modules or vendor directories.






6. Practical Setup (Step-by-step)



6.1 Build the Image



docker build -t php8.4-multistage --target production .

The --target production flag tells Docker to stop at the production stage and discard the builder. If --target is omitted, Docker builds up to the last stage (which is already in production in this Dockerfile).



6.2 Run the Container



docker run -d \

--name php-app \

-p 9000:9000 \

-v ./src:/var/www/html \

php8.4-multistage

Mount your application source code at /var/www/html. PHP-FPM will serve it on port 9000.



6.3 Pair with Nginx (docker-compose example)



version: '3.8'



services:

php:

build:

context: .

target: production

volumes:

- ./src:/var/www/html

networks:

- app-network



nginx:

image: nginx:alpine

ports:

- "80:80"

volumes:

- ./src:/var/www/html

- ./nginx.conf:/etc/nginx/conf.d/default.conf

depends_on:

- php

networks:

- app-network



networks:

app-network:

driver: bridge



6.4 Verify Extensions



docker exec php-app php -m

This lists all loaded PHP modules. Confirm that gd, imagick, intl, opcache, pdo_mysql, etc., are all present.



6.5 Verify CLI Tools



docker exec php-app composer --version

docker exec php-app wp --version

docker exec php-app drush --version

docker exec php-app node --version

docker exec php-app npm --version



6.6 Check phpinfo



Create a phpinfo file

docker exec php-app bash -c "echo '<?php phpinfo();' > /var/www/html/info.php"



Quick test with built-in server

docker exec -d php-app php -S 0.0.0.0:8085 -t /var/www/html

Open http://localhost:8085/info.php in your browser to see the full PHP configuration. Remove info.php after testing - never leave it exposed in production.





6.7 Common Improvements (optional)




  • Use BuildKit cache mounts to speed up repeated builds: RUN --mount=type=cache,target=/var/cache/apt apt-get update && ...

  • Pin the PHP version (e.g., php:8.4.18-fpm) for reproducible builds instead of using the floating 8.4-fpm tag.

  • Add a .env-based PHP config layer for settings that vary between environments (memory_limit, upload_max_filesize).

  • Use Docker Compose profiles to add Xdebug only in development (avoid Xdebug in production).

  • Scan the image with Trivy or Docker Scout for vulnerability reporting before pushing to a registry.




“The art of simplicity is a puzzle of complexity.” - Douglas Horton







8. FAQs



Q1. Why use multi-stage builds instead of a single Dockerfile?

A. A single-stage build carries compilers, dev headers, and package caches into the final image. Multi-stage builds discard all of that, resulting in a smaller, more secure image.



Q2. Why install-php-extensions instead of docker-php-ext-install? A. install-php-extensions handles OS dependency installation, extension compilation, and cleanup automatically. It also supports PECL extensions (imagick, mongodb, mailparse) with the same syntax, eliminating the need for separate pecl install commands.



Q3. Why is the base image Debian Trixie? Can I use Bookworm or Alpine?

A. The official php:8.4-fpm tag is built on Debian 13 (Trixie) as of 2025. If you need Bookworm, use php:8.4-fpm-bookworm. Alpine (php:8.4-fpm-alpine) is smaller but uses musl libc, which can cause compatibility issues with some extensions (notably ImageMagick).



Q4. Why are the runtime library package names different from older guides?

A. Debian Trixie renamed several packages as part of the 64-bit time_t transition and library version bumps. For example: libicu72 became libicu76, libmagickwand-6.q16-6 became libmagickwand-7.q16-10, and libzip4 became libzip5. Always verify package names against the base image’s Debian version.



Q5. How do I find the correct runtime library names if I change the base image?

A. Run a temporary container and use ldd to check for missing libraries:

docker run --rm your-image bash -c \

"for so in /usr/local/lib/php/extensions//.so; do \

ldd \$so 2>/dev/null | grep 'not found'; \

done"

Then search for the correct package with apt-cache search .



Q6. What does validate_timestamps=0 mean for deployments?

A. OPcache will never check if PHP files on disk have changed. This avoids filesystem stat calls on every request (faster). The tradeoff: after deploying new code, you must restart PHP-FPM (docker restart ) to pick up changes.



Q7. Why create a non-root user?

A. Running as root inside a container means a compromised application has full control over the container filesystem. A non-root user limits the damage.



Q8. Can I add Xdebug for local development?

A. Yes, but do not include it in the production image. Use a separate development stage or a Docker Compose override file that installs Xdebug on top of the production image.






9. Key Takeaways




  • Multi-stage builds separate build-time complexity from runtime simplicity.

  • install-php-extensions eliminates the manual dance of installing dev packages, compiling, and cleaning up.

  • Always match runtime library package names to the base image’s Debian version (Trixie uses libicu76, libzip5, libmagickwand-7.q16-10, libavif16).

  • OPcache with validate_timestamps=0 is a significant performance win for production.

  • Security hardening (expose_php=Off, display_errors=Off, allow_url_fopen=Off) should be the default, not an afterthought.

  • Non-root user + health check + .dockerignore round out a production-ready setup.

  • Use ldd to diagnose missing shared libraries when extensions fail to load.






10. Conclusion



This multi-stage Dockerfile provides a clean, repeatable way to build a PHP 8.4-FPM production image with everything needed for WordPress and Drupal projects. The builder stage handles all the heavy lifting - compiling extensions, installing Composer, WP-CLI, Drush, Drupal Console, and Node.js - while the production stage starts fresh and copies only what is needed at runtime. Combined with OPcache tuning, security hardening, a non-root user, and a health check, the result is an image that is smaller, faster, and more secure than a traditional single-stage build. When paired with Nginx via Docker Compose, it forms a solid foundation for deploying PHP applications in any environment.



About the Author:Rajan is a DevOps Engineer at AddWebSolution, specializing in automation infrastructure, Optimize the CI/CD Pipelines and ensuring seamless deployments.

CTI Threat Relationship Graph4 Knoten / 3 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
SOC Incident Playbook: Remote Code Execution (RCE) Defense
title: Detect Exploitation - Docker Image with Multi-Stage Builds & Docker Images Optimization
id: fd3a5125-8d41-40ba-90d1-dbefc0030aae
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Docker Image with Multi-Stage " ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Docker Image with Multi-Stage Builds &amp; D.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Docker Image with Multi-Stage Builds & Docker Images Optimization

Thematisch verwandte Begriffe: Docker, Image, with, MultiStage · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97152 | Nanomsg versions 0.5-beta through 1.x before 1.2.3 has a remotely exploi…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick