Blog
August 20, 2026
PHP Docker Strategies: How to Make the Most of Your Deployment
PHP Development,
Modernization
Docker makes it easier to package and deploy PHP applications, but building an image is only the beginning. Teams often struggle with choosing the right base images, managing extensions and dependencies, securing containers, and creating deployment workflows that are maintainable as applications and infrastructure grow.
In this guide, I'll walk through practical PHP Docker strategies, from understanding Dockerfiles and images to building secure, customizable containers for development and production. You'll learn how to streamline PHP containerization, apply best practices, and lay the groundwork for deploying and managing containerized PHP applications more effectively.
Back to topPHP Docker: Overview
If you're looking at Docker for PHP applications, it's worth understanding the pieces that make it work before diving into implementation. In this section, I'll cover the fundamentals of PHP containerization, including defining Dockerfiles vs. Docker images, outlining how PHP can run on Docker, and why containers have become a popular way to improve consistency, simplify deployments, and support modern development practices.
PHP Docker Image vs. Dockerfile
A Dockerfile is a text file that describes how to build the initial state of a container or image. Most consumers will extend an existing Docker image and add additional instructions to it to provide settings specific to their own application. Base images can be an operating system (e.g., Ubuntu 20.04, Alpine Linux, or CentOS 8), or another image built on these.
A Docker image is the finished result of these instructions. It is a packaged environment that can be deployed consistently across development, testing, and production.
One way to think about this is to look at the Dockerfile as a recipe, and the Docker image as the finished meal.
Can PHP Run on Docker?
Yes, PHP runs on Docker.
Often, a base image will provide a language runtime on top of a base operating system image, ensuring that you can run applications that target that language within that operating system. PHP is no different, and there are a number of different PHP images you can target in your application Dockerfile.
Zend supplies our own base images for ZendPHP and provides extensive tooling to make configuring your PHP runtime easier, reducing the amount of knowledge you need to be successful with PHP Docker deployments.
Secure PHP Containers
How to Simplify PHP Containerization
Zend offers a full suite of PHP container images, including CIS hardened Docker images, and deep expertise in guiding PHP containerization projects. Learn more below.
Why Choose Docker for PHP Developers?
In general, containerizing PHP applications will help to improve the reliability and scalability of managing your applications, all while reducing the overhead from maintaining traditional servers. This means that whether you’re building a new application or modernizing an existing one, containers deliver a flexible and repeatable way to deploy software faster.
Building PHP Docker images can be an effective first step toward that containerization goal. Docker images make it easy to package applications, dependencies, and runtime into a single portable unit. This reduces configuration issues and simplifies deployments. Docker is also a highly accessible technology and makes it simpler to onboard new team members, standardize workflows, and support modern CI/CD pipelines.
Back to topExploring the PHP Dockerfile
An uncommented version of the Dockerfile looks like the following:
ARG OS=alpine
ARG OS_VERSION=3.22
ARG ZENDPHP_VERSION=8.4
ARG BASE_IMAGE=fpm
FROM cr.zend.com/zendphp/${ZENDPHP_VERSION}:${OS}-${OS_VERSION}-${BASE_IMAGE}
ARG TIMEZONE=UTC
ARG ZENDPHP_REPO_USERNAME
ARG ZENDPHP_REPO_PASSWORD
ARG INSTALL_COMPOSER=
ARG SYSTEM_PACKAGES
ARG ZEND_EXTENSIONS_LIST
ARG POST_BUILD_BASH
# Prepare tzdata
ENV TZ=$TIMEZONE \
YUM_y='-y'
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# ADD or COPY any files or directories needed in your image here.
# Customize PHP runtime according to the given building arguments.
# Generally, this should be the last statement of your custom image.
RUN ZendPHPCustomizeWithBuildArgs.shThe above Dockerfile will build a PHP-FPM 8.4 image using Alpine 3.22, using the base extension list.
Dockerfile Build Arguments
You'll notice that you can customize the Dockerfile using a variety of build arguments:
| Build Argument | Explanation |
|---|---|
| OS | The operating system. We have Alma, Alpine, Debian, Oracle, RedHat, Rocky, and Ubuntu builds available currently in our container registry. |
| OS_VERSION | The version of the operating system. We generally build the current and previous 2 LTS versions for a given Linux distribution within our container registry. |
| ZENDPHP_VERSION | Which ZendPHP version you want to install; all LTS and community versions are available. LTS versions (all prior to 8.2 at the time of publication) require some additional setup in order to work, as they require ZendPHP credentials. |
| BASE_IMAGE | One of “apache”, "cli", or "fpm". CLI versions provide a build with only the PHP CLI binary, while Apache versions provide the CLI and mod_php, and FPM versions provide both the CLI and PHP-FPM binaries. The CLI versions are often useful in CI/CD pipelines for things such as running PHPUnit, PHPCS, or static analysis. PHP-FPM binaries can be used behind any web server that supports FastCGI. Apache versions include the Apache HTTPd web server. |
| TIMEZONE | The system timezone to use. We recommend using UTC, but if you need to use a local timezone, it can be set via a build argument. |
| INSTALL_COMPOSER | If a "truthy" value, the image will install Composer during build, before runing the POST_BUILD_BASH command, if present. |
| SYSTEM_PACKAGES | A comma- or space-separated list of additional system packages to install, if any. |
| ZEND_EXTENSIONS_LIST | A comma- or space-separated list of additional PHP extensions available in the ZendPHP repository to install; these can be specified using just the extension name (e.g. "mysqli") instead of the full package name. Additionally, you can install any PECL extension available on pecl.php.net; by default, the latest version will be installed, but you can append the version string to the extension name to choose a specific version (e.g. “inotify-0.1.6”). In all cases, you can provide an initialization priority as a prefix to the extension name to control the order in which it is initialized (e.g., “30-swoole”); the default priority is 20, with lower priorities initializing earlier. |
| POST_BUILD_BASH | A shell script or other executable to execute as the last build step. This can be useful for doing things such as moving files around, setting permissions, or other tasks required to ensure the container is ready to run, such as running a composer install. |
These are a lot of PHP Docker build arguments, and you likely won't want to specify them on the command line regularly:
$ docker build \
> --build-arg OS=debian \
> --build-arg OS_VERSION=10 \
> --build-arg ZENDPHP_VERSION=8.1 \
> --build-arg ZEND_EXTENSIONS_LIST=mysqli,gd,intl \
> --build-arg POST_BUILD_BASH=setup.sh \
> -f Dockerfile.custom \
> -t mybiz/php-fpm-8.1Because you will have no application files present, just an empty PHP install, let's figure out ways to customize the image.
Back to topCustomizing PHP Docker Images
When serving PHP with Docker, you will generally use another technology such as Compose or Kubernetes.
These technologies each allow you to describe services and (generally speaking) these descriptions allow you to specify either an image to consume and customize with volumes, networks, and environment variables, or a Dockerfile and a build context (which can include things such as build arguments). It's this latter that I want to look at now.
We’ll start by using the base Dockerfile, and building out our application image in development, so we can determine what needs to be done to create our final production image. Here’s an example of how that configuration will look:
services:
php:
build:
context: .
dockerfile: Dockerfile
args:
OS: debian
OS_VERSION: 10
ZENDPHP_VERSION: 8.1
INSTALL_COMPOSER: 'true'
ZEND_EXTENSIONS_LIST: 'bz2 curl intl mbstring opcache xsl zip'
POST_BUILD_BASH: '/var/www/scripts/setup.sh'
volumes:
- .:/var/www/Be aware that when installing custom extensions, non-standard extensions, or specific extension versions via ZEND_EXTENSIONS_LIST, you may need to also specify development packages in the SYSTEM_PACKAGES build argument required to build the extension. When performing PECL installs, the ZendPHP development package (e.g., php-7.4-zend-dev or php74-zend-php-devel) and/or PEAR package will also be installed during build time.
The beauty of each of these is that I can use the same Dockerfile over and over, without changes. By using build arguments, environment variables, and mapping local directories into the image via volumes, I am able to customize the image I build.
Building PHP Docker Images for Production
The previous examples were a good start. However, in production, we generally want to ensure that application files are copied into the image and not mounted via a volume. This is particularly true when we consider multi-container and/or multi-server orchestrations.
As such, we'll need to customize the Dockerfile to do so.
If you pay close attention to the basic Dockerfile, you'll note that it has very few directives; the bulk of it is defining build arguments. In most cases, you can provide your customizations in one of two locations:
- Immediately before the call to
ZendPHPCustomizeWithBuildArgs.sh— this is generally where you will copy files into the container image. - Immediately following the call to
ZendPHPCustomizeWithBuildArgs.sh— this is where you will run any build tasks not in your providedPOST_BUILD_BASHscript.
As an example, I can copy the application files in:
ARG OS=ubuntu
ARG OS_VERSION=20.04
ARG ZENDPHP_VERSION=7.4
ARG BASE_IMAGE=fpm
FROM cr.zend.com/zendphp/${ZENDPHP_VERSION}:${OS}-${OS_VERSION}-${BASE_IMAGE}
ARG TIMEZONE=UTC
ARG INSTALL_COMPOSER=
ARG SYSTEM_PACKAGES
ARG ZEND_EXTENSIONS_LIST
ARG PECL_EXTENSIONS_LIST
ARG POST_BUILD_BASH
ENV TZ=$TIMEZONE \
YUM_y='-y'
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# ADD or COPY any files or directories needed in your image here.
COPY . /var/www/
RUN ZendPHPCustomizeWithBuildArgs.shA huge benefit to this is that we can likely still re-use the Dockerfile with multiple applications. We can assume that at both build and runtime, the application is installed to /var/www/, allowing us to specify post-build scripts or configure our FastCGI-enabled webserver to pass requests to that location. In development, we can use Compose, where we can map a volume so that as we make changes, they're reflected in the running application.
PHP Docker Images: Multi-Stage Builds
In a previous example, I noted that when specifying PECL_EXTENSIONS_LIST, I may need to specify system development packages in order to build the extension. In production, I likely don't want those lingering.
One way to deal with those is to add another build step that removes the development packages:
RUN apt-get remove {list of development packages here}Another way is to ensure they're never present in the first place, which can be done with a multi-stage build. As an example, let’s say you wanted to build the scalar objects extension for a ZendPHP container. Create one stage that performs the build, which requires additional packages. The production image then copies files from that stage into itself before running the standard customization script.
# DOCKER-VERSION 1.3
# Build scalar objects; only supports through PHP 8.1
FROM cr.zend.com/zendphp/8.1:ubuntu-20.04-cli as scalar_objects
## Prepare image
ARG TIMEZONE=UTC
ENV TZ=$TIMEZONE
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN set –e ; \
apt-get update ; \
apt-get install -y git curl php8.1-zend-dev ; \
mkdir /workdir ; \
cd /workdir ; \
git clone https://github.com/nickic/scalar_objects.git ; \
cd scalar_objects ; \
phpize ; \
./configure ; \
make ; \
make install
# Build the PHP container
FROM cr.zend.com/zendphp/8.1:ubuntu-20.04-cli
## Build time customizations per usual
ARG TIMEZONE=UTC
ARG INSTALL_COMPOSER=false
ARG SYSTEM_PACKAGES
ARG ZEND_EXTENSIONS_LIST
ARG PECL_EXTENSIONS_LIST
ARG POST_BUILD_BASH
ARG ZENDPHP_REPO_USERNAME
ARG ZENDPHP_REPO_PASSWORD
## Prepare tzdata
ENV TZ=$TIMEZONE
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
## Install scalar_objects
COPY --from=scalar_objects /usr/lib/php/8.1-zend/scalar_objects.so /usr/lib/php/8.1-zend/scalar_objects.so
COPY --from=scalar_objects /usr/include/php/8.1-zend/ext/scalar_objects /usr/include/php/8.1-zend/ext/scalar_objects
RUN set -e; \
echo "extension=scalar_objects.so" > /etc/zendphp/cli/conf.d/60-scalar_objects.ini
## Copy application files into image
COPY . /var/www/
## Customize PHP runtime according
## to the given building arguments
RUN ZendPHPCustomizeWithBuildArgs.sh
## Set working directory
WORKDIR /var/wwwThis approach ensures that no build tools reach the final PHP Docker image, only the results of building (the extension file and its libraries). The Dockerfile can still be re-used for any images I want to expose that will use the scalar_objects extension, and we now receive the benefit of a Docker build caching whenever creating such images, as they will cache the "scalar_objects" stage.
What Comes After Building PHP Docker Images?
Building a secure, customizable Docker PHP image is only the first step. Once your image is complete, you still need to deploy, manage, monitor, and update the containers that run your application.
Following a few best practices can help improve security and reliability:
- Treat containers as disposable and rebuild images instead of modifying running containers
- Keep environment-specific configuration separate from the image
- Regularly update and rebuild images to incorporate security fixes
- Monitor application health, logs, and performance
- Automate deployments to reduce manual effort and configuration drift
As applications grow, managing containers often becomes more challenging than building them. As a result, you’ll likely find yourself maintaining Docker Compose files, configuring services, troubleshooting unforeseen issues, and handling ongoing updates across environments — all of which will quickly consume valuable development time.
Back to topIntroducing the Zend Core Web Platform
The Zend Core Web Platform helps organizations move beyond basic PHP Docker deployments by providing a streamlined platform for running containerized PHP applications.
Built on Docker Compose, the Core Web Platform simplifies deployment and management with secure and supported PHP runtimes, integrated services, built-in observability, and security-focused configurations. Instead of building and maintaining a complete container stack from scratch, teams can deploy applications on a platform designed specifically for PHP workloads.
For organizations modernizing existing applications, the Zend Core Web Platform provides an easier path to production. It combines the flexibility of containers with simplified operations, allowing teams to focus less on infrastructure and more on delivering applications.
For many organizations, Docker for PHP developers starts with building images. The next step is adopting a platform that simplifies deployment, management, and long-term maintenance at scale.
Free Demo
Improved PHP Management — No Rewrites Required
The Zend Web Platform provides a unified command center for managing, modernizing, securing, and monitoring mission-critical PHP. Watch the video below to learn more, and schedule a demo to see how it can improve your PHP operations.
Additional Resources
- Video Tour - Getting Started With the Zend Web Platform
- Guide - How to Develop Web Applications with PHP
- On-Demand Webinar - The Peaks and Valleys of PHP Containerization
- Report - 2026 PHP Landscape Report
- Blog - Docker Compose vs. Kubernetes: What's the Best PHP Orchestration Tool?
- Blog - Using a GitOps Model for Web Application Development