Перейти до вмісту
PHP Ecosystem
Explore

Docker

Цей контент ще не доступний вашою мовою.

Debian Trixie images with Apache 2.4, PHP-FPM and HTTP/2 for Yii2 applications

These are container images, not Composer extensions. Use them to run an existing Yii application; they do not create an application or install the Yii framework by themselves.

Each image runs Apache (event MPM, HTTP/2, HTTPS) in front of PHP-FPM under supervisord, serves the application from /var/www/app/web, and prepares the application at startup (directories, Composer, migrations) through an entrypoint configured with environment variables.

Images are published to ghcr.io/yii2-extensions/apache for linux/amd64 and linux/arm64.

TagExampleLifecycle
<php>-debian-<variant>8.5-debian-prodRolling: rebuilt weekly from the latest release to pick up PHP and Debian security patches.
<php>-debian-<variant>-v<version>8.5-debian-prod-v2.0.0Frozen at release time.
latestlatestRolling: the prod variant of the newest stable PHP version (currently 8.5).
  • PHP versions: 8.3, 8.4, 8.5. PHP 8.6 is built in CI from its release candidate as an experimental job and is not published until it is stable.
  • Variants: prod, dev, full.

Rolling tags move when the release workflow rebuilds them; an existing container does not update itself. Pull the selected tag and recreate your container to use a newer image. Use a frozen release tag, or pin the registry digest, when you need a fixed image. Verify availability before changing versions:

Terminal window
docker buildx imagetools inspect ghcr.io/yii2-extensions/apache:8.5-debian-dev-v2.0.0

The release workflow publishes stable versions only. Locally generated Bake tags, particularly PHP 8.6 tags, are not evidence that those images exist in the registry.

First install Docker Engine or Docker Desktop and confirm docker info succeeds. Docker Compose v2 or newer is needed only for Compose examples; Buildx is needed to build these images. Start in an existing application root containing composer.json and a Web entry point. The default document root is web/; the Yii 22.0 basic template on branch 22 uses public/ and needs the setting described in the installation guide. Match the application’s own framework and PHP version constraints. See installation for the complete setup.

Run the dev variant against that application in the current directory. On Linux, pass your own user so the files the container creates (vendor/, runtime/, web/assets/) belong to you:

Terminal window
docker run -d --name app --user "$(id -u):$(id -g)" -p 127.0.0.1:8080:80 -p 127.0.0.1:8443:443 -v "$PWD":/var/www/app \
ghcr.io/yii2-extensions/apache:8.5-debian-dev-v2.0.0

The application answers on http://localhost:8080 and, with a generated self-signed certificate, on https://localhost:8443. Composer installs the dependencies at startup when vendor/ is missing, as the container user, so the project directory must be writable by it. Without --user the stack runs as www-data (UID 33), which can suit Docker Desktop. A named volume must also have suitable ownership; mounting one does not grant UID 33 write access automatically. On runtimes that keep ports below 1024 privileged (Kubernetes, --network host), set APACHE_HTTP_PORT=8080 and APACHE_HTTPS_PORT=8443 and map -p 8080:8080 -p 8443:8443 instead.

With Docker Compose, save this as compose.yaml in the application root:

services:
app:
image: ghcr.io/yii2-extensions/apache:8.5-debian-dev-v2.0.0
user: "${APP_UID:?Set APP_UID}:${APP_GID:?Set APP_GID}"
ports:
- "127.0.0.1:8080:80"
- "127.0.0.1:8443:443"
volumes:
- .:/var/www/app
environment:
YII_ENV: dev
YII_DEBUG: "1"
FAIL_ON_COMPOSER_ERROR: "true"
Terminal window
APP_UID="$(id -u)" APP_GID="$(id -g)" docker compose up -d
docker compose logs app
curl --fail http://127.0.0.1:8080/health

YII_ENV and YII_DEBUG are available to PHP, but your application entry scripts must read them; setting container variables does not override constants already defined by the application. The health endpoint checks the stack, so also request an application route. Usage examples cover CLI commands and cleanup.

For production, build an application image on top of prod so the code and its dependencies are part of the image:

FROM ghcr.io/yii2-extensions/apache:8.5-debian-prod-v2.0.0
COPY --chown=www-data:www-data . /var/www/app
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress

Keep credentials, .env, .git, local vendor/, node_modules/, runtime files, and uploads out of the build context with an application .dockerignore. Include composer.lock; decide which application Composer scripts and plugins may run during the build. The image does not configure your database or persist uploaded files.

VariantPHP extensionsTools
prodapcu, bcmath, gd, imagick, intl, opcache, pcntl, pdo_mysql, pdo_pgsql, redis, zipComposer
devprod plus memcached, mongodb, soap, xdebug, yamlComposer, Node.js 24, npm
fulldev plus oci8, pdo_oci, pdo_sqlsrv, sqlsrv, tidyComposer, Node.js 24, npm, Oracle Instant Client, Microsoft ODBC Driver 18
  • prod: OPcache without timestamp validation and with the tracing JIT, session cookies restricted to HTTPS (session.cookie_secure) and SameSite=Strict, no Composer install at startup by default.
  • dev: OPcache revalidates files on every request, JIT disabled, Xdebug loaded.
  • full: dev plus the Oracle and SQL Server drivers and assertions enabled, JIT disabled as in dev (Xdebug is loaded); intended for test suites that cover every database driver.

Compressed size of the PHP 8.5 images:

Variantv1.1.0v2.0.0
prod232.9 MiB125.1 MiB
dev235.3 MiB175.4 MiB
full275.6 MiB215.4 MiB

The most common settings are listed below. See the configuration reference for every variable, its default and the startup sequence.

VariableDefaultDescription
APACHE_DOCUMENT_ROOT/var/www/app/webDocument root.
APACHE_HTTP_PORT80HTTP port inside the container.
APACHE_HTTPS_PORT443HTTPS port inside the container.
APACHE_SSL_ENABLEDtrueHTTPS with HTTP/2 on APACHE_HTTPS_PORT.
APACHE_SSL_REDIRECTfalseRedirect HTTP to HTTPS.
SSL_AUTO_GENERATEtrueGenerate a self-signed certificate when none is mounted.
SSL_CERT_FILE/etc/apache2/ssl/cert.pemMounted certificate, readable by the container user.
SSL_KEY_FILE/etc/apache2/ssl/key.pemMounted private key, readable by the container user.
SKIP_COMPOSER_INSTALLtrue for prod, false otherwiseSkip composer install at startup.
YII_RUN_MIGRATIONSfalseRun yii migrate at startup.
WAIT_FOR_SERVICESfalseWait for the DB_*_HOST services before starting.
ENABLE_HEALTH_ENDPOINTtrueServe the public health endpoint at HEALTHCHECK_PATH.
ENABLE_FPM_STATUSfalseServe the PHP-FPM status and ping pages to loopback only.
PHP_MEMORY_LIMIT256MPHP memory limit; every PHP_* setting is listed in the docs.

To use your own certificate, mount it and point the entrypoint at it:

Terminal window
docker run -d -p 80:80 -p 443:443 \
-v /path/to/certs:/run/certs:ro \
-e SSL_AUTO_GENERATE=false \
-e SSL_CERT_FILE=/run/certs/fullchain.pem \
-e SSL_KEY_FILE=/run/certs/privkey.pem \
-e APACHE_SSL_REDIRECT=true \
my-app

The whole stack (the entrypoint, supervisord, Apache and PHP-FPM) runs as the container user. The default and arbitrary-UID modes run without root; root mode is an explicit choice:

  • www-data (default, the image declares USER www-data).
  • Any UID, with --user <uid>:<gid> or Compose user:. The UID needs no account in the image, so files created in a bind-mounted project belong to the host user. The entrypoint cannot change ownership in this mode; when the application directory is not writable it says so and names the remedy.
  • Root, as an explicit opt-in with --user root: the entrypoint initializes as root (directories, FIX_PERMS before a Composer install), then Apache and PHP-FPM drop their workers to www-data, as in v1. Nothing root sources, executes or loads may be written by www-data, and the generated private key is readable by root only.

Runtime state (pid files, sockets, the Apache runtime environment, generated certificates, PHP sessions and temporary files) lives under /run/yii2 and is created at every start for the running user. In the two non-root modes no process can gain root, so the class of privilege escalation that the root mode must guard against does not exist.

Ports 80 and 443 work as a non-root user under docker run, because Docker allows unprivileged processes to bind every port. Kubernetes, --network host and other runtimes that keep ports below 1024 privileged need APACHE_HTTP_PORT and APACHE_HTTPS_PORT (for example 8080 and 8443); the entrypoint stops with that remedy when the configured port cannot be bound. The container side of the port mappings changes with them: -p 8080:80 becomes -p 8080:8080. See the configuration reference for the read-only root filesystem flags.

  • GET /health returns a JSON status document produced by PHP-FPM. Change the path with HEALTHCHECK_PATH or disable it with ENABLE_HEALTH_ENDPOINT=false.
  • The Docker HEALTHCHECK runs the healthcheck command, which requests the reserved, loopback-only /__health path through Apache and PHP-FPM. The container becomes unhealthy when either of them stops answering.

The images contain neither the compiler toolchain nor the PHP source tarball. The included install-extensions command compiles extensions in a derived image in one step:

FROM ghcr.io/yii2-extensions/apache:8.5-debian-prod
USER root
RUN install-extensions amqp sockets
USER www-data

The image runs as www-data, so every step that installs packages or extensions needs USER root first, and the image should switch back to USER www-data afterwards; install-extensions stops with that hint when it is not root.

It accepts the extension names and version syntax of install-php-extensions (PECL and bundled extensions). It downloads the exact PHP source the image was built from and verifies its SHA-256 checksum, installs the toolchain, builds the extensions, then removes the toolchain, the source and the build-only packages while keeping the shared libraries the new extensions load. The build fails when a library is left unresolved or PHP reports a startup warning. The added size depends on the extensions and their runtime libraries.

Ghostscript is not included, so Imagick reads and writes raster formats (JPEG, PNG, WebP, GIF and others) but not PDF, PS or EPS. Add it in a derived image when you need them:

USER root
RUN apt-get update && apt-get install -y --no-install-recommends ghostscript && rm -rf /var/lib/apt/lists/*
USER www-data

Version 2 contains breaking changes. Review each item before switching tags.

  • No toolchain in the image. Images ship without compilers and PHP sources. Derived images that compile extensions use the included install-extensions command (see Extending the image).
  • Runs as www-data. The image declares USER www-data and the whole stack runs as the container user; docker exec defaults to www-data. Use --user "$(id -u):$(id -g)" for bind mounts on Linux (v1 changed the owner of the project to www-data instead), or --user root for the v1 behavior (root initialization, FIX_PERMS, workers as www-data). Derived images need USER root before installing packages or extensions and USER www-data after; application files copied into a derived image must be readable by the runtime user, and directories used by Composer or the application must be writable (the quick start uses COPY --chown=www-data:www-data).
  • Runtime state moved to /run/yii2. Pid files, the PHP-FPM socket (/run/yii2/php/php-fpm.sock), the supervisor socket, PHP sessions (PHP_SESSION_PATH=/run/yii2/sessions), PHP temporary files and the generated self-signed certificate (/run/yii2/ssl) are created at every start. Generated certificates are no longer written to SSL_DIR and do not survive a restart; mounted certificates must be readable by the container user, otherwise the container serves HTTP only and logs why. /etc/apache2 and /etc/apache2/ssl belong to root.
  • No sudo, gosu, vim-tiny or brotli CLI. www-data no longer has passwordless sudo, and the entrypoint no longer re-executes itself as root.
  • Node.js is no longer downloaded at startup. dev and full include Node.js 24 and npm; prod does not. Build front-end assets in your application image or in a separate build stage.
  • prod skips composer install by default. Install dependencies in the application image, or set SKIP_COMPOSER_INSTALL=false to restore the v1 behavior.
  • FAIL_ON_* flags are honored. FAIL_ON_SERVICE_TIMEOUT and FAIL_ON_COMPOSER_ERROR default to false, so an unreachable service or a failed Composer install is logged and startup continues; v1 aborted the container. Set them to true to keep failing fast. FAIL_ON_MIGRATION_ERROR defaults to true.
  • Health endpoint served from the image. Nothing is written to web/ any more; remove the web/health/ directory that v1 created in the application. /__health is reserved. The container now turns unhealthy when PHP-FPM is down.
  • No User-Agent filter. Requests from clients whose User-Agent contains bot, curl, wget, python or scan are no longer rejected with 403. Block unwanted clients at your proxy or firewall if needed.
  • Single virtual host file. sites-available/vhost-ssl.conf and sites-available/vhost-ssl-full.conf are gone; sites-available/vhost.conf is driven by Apache defines and the entrypoint no longer rewrites configuration files. APACHE_HTTPS_PORT is not opened when APACHE_SSL_ENABLED=false. Images that replaced or patched these files must be updated.
  • Apache defaults. The ineffective <LimitExcept> block, the inert global rewrite rules and 07-rate-limiting.conf are removed. Debian’s default configuration snippets are disabled, so the Server header is Apache and nothing is logged to files inside the container.
  • Shorter PHP_DISABLE_FUNCTIONS default. Web requests now block only the shell functions (exec, passthru, shell_exec, system, proc_open, popen); parse_ini_file and show_source are available again. The list is applied to the PHP-FPM master instead of the pool, so pools you add inherit it and cannot re-enable its functions with php_admin_value[disable_functions]; set the variable instead.
  • PHP-FPM status is opt-in. Set ENABLE_FPM_STATUS=true to serve PHP_FPM_STATUS_PATH and PHP_FPM_PING_PATH to loopback only. /fpm-status-full is replaced by ?full on the status path.
  • SQL Server drivers. sqlsrv and pdo_sqlsrv 5.13 come from the extension installer instead of a patched 5.12.0 build.
  • Xdebug configuration. The unused xdebug.ini is removed; configure Xdebug with XDEBUG_MODE and XDEBUG_CONFIG (see the configuration reference).
  • No JIT in full. Xdebug made PHP disable the tracing JIT anyway and warn at every startup; full now turns it off like dev. prod keeps the tracing JIT.
  • Inert Apache variables removed. APACHE_RUN_USER, APACHE_RUN_GROUP, APACHE_RUN_DIR, APACHE_PID_FILE and APACHE_LOCK_DIR are no longer in the image environment. /etc/apache2/envvars always overrode them, so setting them never had an effect; drop them from your configuration.
  • Published assets are served. The Yii directory protection is anchored at /var/www/app, so files under web/assets/<hash>/widgets/ or web/assets/<hash>/vendor/ (jQuery UI, for example) no longer return 403.

APACHE_SSL_REDIRECT, SSL_AUTO_GENERATE, SSL_CERT_FILE, SSL_KEY_FILE, SSL_DIR, SSL_CHAIN_FILE, APACHE_DISABLE_OCSP_STAPLING, DEBUG_ENTRYPOINT, FORCE_COMPOSER_INSTALL, FIX_PERMS and every APACHE_* and PHP_* tuning variable keep their names, defaults and behavior.

See testing for isolated runtime validation and the distinction between the fixture and a real application test. No build command below publishes images; never use the digest-* targets for local validation.

docker-bake.hcl is the single source of truth for PHP versions, variants and tags. Adding a PHP version is one entry in its PHP variable; an entry whose base differs from version (a release candidate such as 8.6-rc) is experimental, excluded from the default group and never published.

Terminal window
# Build every stable image (PHP 8.3 to 8.5, all variants)
docker buildx bake
# Build one image and load it into the local image store
docker buildx bake image-8-5-dev --load
# Build the experimental PHP 8.6 image
docker buildx bake image-8-6-prod --load
# Print every image target as JSON
docker buildx bake --print image

Continuous integration:

WorkflowTriggerPurpose
build.ymlPull requests, pushes to mainBuilds every PHP version on both architectures.
release.ymlTags vX.Y.Z, weekly schedule, manual runPublishes a release; the weekly run rebuilds the latest release and moves the rolling tags.
quality.ymlPull requests, pushesOrganization quality checks, Hadolint and ShellCheck.
security.ymlPull requests, pushesOrganization security checks.

Practical guides for selecting images, running Yii applications, and validating containers.

AI agents: use the repository guides and source for the installed revision. For a documentation deployment with llms.txt, read that index first. When Markdown export is enabled, append index.md to a page URL.

The skill source covers HTTP/HTTPS, PHP-FPM, and filesystem behavior.

If the hosted sources are unavailable, use the repository guides and locally available skills.

Follow on X

License