Sign inSign up

shinsenter/php

Sponsored OSS

By SHIN Company

•Updated 6 minutes ago

šŸ“¦ Simplified PHP Docker images for effortless customization and extension setup.

Image
Languages & frameworks
Developer tools
Web servers
34

1M+

shinsenter/php repository overview

⁠PHP Docker Images

šŸ“¦ Lightweight PHP Docker images designed for easy customization and simple extension management.

Lightweight images for PHP 5.6 to 8.6 (RC) on Debian and Alpine.

Build all Watch updates

⁠Table of Contents

⁠Introduction

shinsenter/php

These images are based on the official PHP Docker images⁠ and make it easy to change PHP and PHP-FPM settings using environment variables. No image rebuild required.

They include the latest Composer and common web servers - Apache2⁠, Nginx⁠, RoadRunner⁠, and FrankenPHP⁠ - so you don't need to install them separately.

Note

We slim these images down with the [docker-squash](https://code.shin.company/docker-squash) project, so pulls are faster and they use less disk and memory than the official images they're based on.

⁠Getting Started

⁠Docker image variants

Tags cover PHP versions from 5.6 to 8.6-rc and come in cli, zts, fpm, fpm-nginx, fpm-apache, roadrunner(1), and frankenphp(2) variants. Both Debian and Alpine builds are available.

Examples:

  • shinsenter/php:8.3-cli
  • shinsenter/php:8.4-zts
  • shinsenter/php:8.5-fpm
  • shinsenter/php:8.1-fpm-apache
  • shinsenter/php:8.2-fpm-nginx
  • shinsenter/php:8.3-roadrunner (1)
  • shinsenter/php:8.4-frankenphp (2)

(1) RoadRunner variant - requires PHP >= 8.0.
(2) FrankenPHP variant is BETA - requires PHP >= 8.2.

See all tags on our Docker Hub⁠.

⁠Examples

Run a container using one of these commands:

⁠CLI
# non-interactive
docker run --rm shinsenter/php:8.5-cli php -m

# interactive
docker run -it -v ./myproject:/var/www/html shinsenter/php:8.5-cli
⁠PHP-FPM
docker run -v ./myproject:/var/www/html -p 9000:9000 shinsenter/php:8.5-fpm
⁠PHP-FPM + Nginx (or Apache, RoadRunner, FrankenPHP)
# with Nginx
docker run -v ./myproject:/var/www/html -p 80:80 -p 443:443 shinsenter/php:8.5-fpm-nginx

# with Apache
docker run -v ./myproject:/var/www/html -p 80:80 -p 443:443 shinsenter/php:8.5-fpm-apache

# with RoadRunner
docker run -v ./myproject:/var/www/html -p 80:80 -p 443:443 shinsenter/php:8.4-roadrunner

# with FrankenPHP
docker run -v ./myproject:/var/www/html -p 80:80 -p 443:443 shinsenter/php:8.4-frankenphp

⁠Configuration

⁠Environment variables

You can configure PHP and PHP-FPM using environment variables instead of rebuilding images.

Naming rules:

This convention lets you map any environment variable straight back to its ini/fpm directive.

Note

By default, `$PHP_*` variables are applied only if set before the container starts. To allow changing PHP settings from an interactive shell inside a running container, start the container with `ALLOW_RUNTIME_PHP_ENVVARS=1`.

Tip

Run `php-envvars` inside the container to list the default `$PHP_*` variables.
⁠Examples
⁠Command Line
docker run \
    -v ./myproject:/var/www/html \
    -e PHP_DISPLAY_ERRORS='1' \
    -e PHP_POST_MAX_SIZE='100M' \
    -e PHP_UPLOAD_MAX_FILESIZE='100M' \
    -e PHP_SESSION_COOKIE_HTTPONLY='1' \
    shinsenter/php:8.5 php -i
⁠With docker-compose.yml
services:
  web:
    image: shinsenter/php:8.5-fpm-nginx
    environment:
      PHP_DISPLAY_ERRORS: "1"
      PHP_POST_MAX_SIZE: "100M"
      PHP_UPLOAD_MAX_FILESIZE: "100M"
      PHP_SESSION_COOKIE_HTTPONLY: "1"
⁠Explanation
Environment VariableWhat it doesEquivalent php.ini / fpm setting
PHP_DISPLAY_ERRORS=1Show errors during developmentdisplay_errors = 1
PHP_POST_MAX_SIZE=100MSet max POST size (default 8M)post_max_size = 100M
PHP_UPLOAD_MAX_FILESIZE=100MSet max upload file size (default 2M)upload_max_filesize = 100M
PHP_SESSION_COOKIE_HTTPONLY=1Enable HttpOnly on session cookiessession.cookie_httponly = 1
⁠Customizing PHP-FPM

The images start PHP-FPM with one generated file, /usr/local/etc/php-fpm.d/zz-docker-shinsenter-php.conf. They do not load php-fpm.conf or the other files in /usr/local/etc/php-fpm.d/, so a mounted www.conf has no effect. These sources apply, and the first one wins:

  1. custom-*.conf files in /usr/local/etc/php-fpm.d/.
  2. PHP_* variables you pass to the container.
  3. ENABLE_TUNING_FPM=1, which sets the pm.* values from the CPU limit of the container. A PHP_PM* variable you pass replaces the matching tuned value, and the other tuned values follow PHP_PM_MAX_CHILDREN.
  4. The defaults of the image.

PHP_* variables are the main way to configure PHP-FPM. If you prefer a file, mount it with a name that matches custom-*.conf. The container looks for these files when it starts, so restart it after you add one. A pool with the same name as the generated one ([www]) is merged, and your values win:

docker run -v ./custom-www.conf:/usr/local/etc/php-fpm.d/custom-www.conf:ro \
    shinsenter/php:8.3-fpm
[www]
pm = static
pm.max_children = 10
slowlog = /var/log/php-slow.log
⁠Application Directory

Default application directory: /var/www/html. Change it with $APP_PATH:

docker run -p 80:80 -p 443:443 -p 443:443/udp \
    -v "$PWD":/app \
    -e APP_PATH=/app \
    shinsenter/php:8.5-fpm-nginx

To change the document root (a path inside $APP_PATH that contains index.php), set $DOCUMENT_ROOT:

docker run -p 80:80 -p 443:443 -p 443:443/udp \
    -v "$PWD":/app \
    -e APP_PATH=/app \
    -e DOCUMENT_ROOT=public \
    shinsenter/php:8.5-fpm-nginx

This example sets the document root to /app/public.

⁠User and group

Override the default user and group with environment variables:

Environment VariableDescriptionDefault
APP_USERUsername inside the containerwww-data
APP_GROUPGroup name inside the containerwww-data
APP_UIDNumeric UID for the userUID in container
APP_GIDNumeric GID for the groupGID in container

Example (run as user myapp with UID 5000):

docker run -p 80:80 -p 443:443 -p 443:443/udp \
    -e APP_USER=myapp \
    -e APP_UID=5000 \
    shinsenter/php:8.5-fpm-nginx

⁠Extending the Images

⁠Pre-installed PHP Extensions

Common PHP extensions are pre-installed so projects can start quickly.

apcu
bcmath
calendar
exif
gd
gettext
igbinary
intl
msgpack
mysqli
opcache
pcntl
pdo_mysql
pdo_pgsql
pgsql
redis
sodium
tidy
uuid
yaml
zip

Note

Extensions that are already included in the official PHP images are not listed here.

Tip

Run `docker run --rm shinsenter/php:8.5-cli php -m` (replace `8.5` as needed) to see installed extensions.
⁠Adding PHP Extensions

Use the phpaddmod helper to install and configure extensions in one step - no need for docker-php-ext-install or manual php.ini edits.

Example Dockerfile:

FROM shinsenter/php:8.5-fpm-nginx

# Install imagick, swoole and xdebug
RUN phpaddmod imagick swoole xdebug

# Add your instructions here
# For example:
# ADD --chown=$APP_USER:$APP_GROUP ./myproject/ /var/www/html/

Note

`phpaddmod` is a wrapper around the [`mlocati/docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer), which handles compiling and enabling extensions.

Tip

See the supported extensions in their documentation: https://github.com/mlocati/docker-php-extension-installer/blob/master/README.md#supported-php-extensions
⁠Hooks

Hooks let you customize runtime behavior. Create a hooks folder inside $APP_PATH and place executable files named after each hook (or use subfolders).

Supported hooks:

Hook nameWhen it runsExample use
onbootAt container start or restartSend startup notification
first-runOnly the first time the container startsInitialize database
rebootedEvery container restartCheck crash logs
migrationRun database migrationsApply DB migrations
onreadyAfter migration, when the app is almost readyWarm up caches
onliveAfter the web server starts (if included)Trigger a webhook

Example: To install PHP modules on first run, add hooks/first-run or hooks/first-run/install-modules.

Enable DEBUG=1 to see which hooks run.

⁠Autorun Scripts

Place shell scripts in /startup/ to run automatically when the container starts. Scripts run in alphabetical order by filename.

Example Dockerfile to add an autorun script:

Important

Ensure the script is executable.
FROM shinsenter/php:8.5-cli

ADD ./autorun/00-migration /startup/00-migration
RUN chmod +x /startup/00-migration

# Add your instructions here
# For example:
# ADD --chown=$APP_USER:$APP_GROUP ./myproject/ /var/www/html/

Note

The startup directory includes `99-greeting`, which prints a welcome message at startup.
⁠Disable Autorun Scripts

To disable autorun scripts, set DISABLE_AUTORUN_SCRIPTS=1:

docker run -e DISABLE_AUTORUN_SCRIPTS=1 shinsenter/php:8.5-cli bash
⁠Using Cron Jobs

This project supports simple cron jobs; build a custom image for advanced cron features.

Enable cron with ENABLE_CRONTAB=1. The service loads jobs from $CRONTAB_DIR (default: /etc/crontab.d) and runs them as $APP_USER:$APP_GROUP (default www-data:www-data), with $CRONTAB_HOME (default: /var/www/html) as the home directory.

Example Dockerfile to add a crontab:

FROM shinsenter/php:latest

ENV ENABLE_CRONTAB=1

# create crontab entry via RUN instruction
RUN echo '* * * * * echo "This line will run every minute!" | tee /tmp/cron-every-minute.txt' >> /etc/crontab.d/sample1;

# or copy crontab entries via ADD instruction
ADD ./sample2 /etc/crontab.d/

Crontab entry format:

# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,...
# |  |  |  |  .---- day of week (0 - 6) OR sun,mon,...
# |  |  |  |  |
# *  *  *  *  *  command to be executed

You can set cron jobs directly via the CRONTAB_SETTINGS environment variable in docker-compose:

services:
  web:
    image: shinsenter/php:8.5-fpm-nginx
    environment:
      ENABLE_CRONTAB: "1"
      CRONTAB_SETTINGS: "* * * * * echo 'This line will run every minute!' | tee /tmp/cron-every-minute.txt"

For more cron-related variables, see System settings⁠.

⁠Supervisor command

Set SUPERVISOR_PHP_COMMAND to override the default command used by the supervisor process. This lets you run a different command to serve your app.

docker run \
    -e SUPERVISOR_PHP_COMMAND='php -S localhost:80 index.php' \
    shinsenter/php:8.5

⁠Operations

⁠Sending Emails

We use msmtp as a lightweight sendmail replacement, so libraries like PHPMailer⁠ work out of the box.

To use PHP's mail() function, configure SMTP via these environment variables:

Environment VariableDescriptionExample Value
SMTP_HOSTSMTP server hostname or IPsmtp.gmail.com
SMTP_PORTSMTP port587
SMTP_LOGPath to SMTP log file/path/to/email.log
SMTP_FROMSender email address[email protected]
SMTP_USERSMTP usernameYour smtp username
SMTP_PASSWORDSMTP passwordYour smtp password
SMTP_AUTHEnable SMTP authentication (on/off)on
SMTP_TLSUse TLS for the connection (on/off). On by default when SMTP_USER or SMTP_PASSWORD is seton

Tip

If you don't have an SMTP server, try using a local SMTP container like [Mailpit](https://hub.docker.com/r/axllent/mailpit). With Mailpit, set `SMTP_HOST=mailpit` and `SMTP_PORT=1025` in your container.
⁠Debug mode

Set DEBUG=1 to turn on verbose logging and to see which hooks run:

docker run -e DEBUG=1 shinsenter/php:8.5-fpm-nginx
⁠System settings

Environment variables for fine-tuning container behavior.

⁠General
Setting NameDefault ValueDescriptionExample
DEFAULT_LOG_PATH/proc/1/fd/2Where logs are written. By default, logs are sent to the container's stdout./var/log/container.txt
DEBUG or DEBUG_MODENot setEnable verbose logging when set to 1.1
TZUTCSet the container's timezone. See the list of timezones.Asia/Tokyo
ALLOW_RUNTIME_PHP_ENVVARSNot setAllow $PHP_* variables to override PHP configuration at runtime.1
DISABLE_GREETINGNot setSuppress the startup greeting with 1.1
ENABLE_SSHDNot setEnable SSH server inside the container when 1.1
SSHD_AUTHORIZED_KEYSNot setPublic SSH keys (one per line). When not empty, they replace the content of ~/.ssh/authorized_keys at every start.ssh-rsa AAAA...
SUPERVISOR_PHP_COMMANDNot setOverride the PHP command used by supervisor. Quotes group arguments that contain spaces.php -S localhost:80 index.php
⁠Project startup and autorun
Setting NameDefault ValueDescriptionExample
INITIAL_PROJECTNot setUsed only when the app directory is empty. A URL starting with http and ending in .zip or .tar.gz is downloaded and extracted. A URL ending in .git is cloned. A value like vendor/package is created with composer create-project. Any other value runs the create-project hook.https://example.com/project.zip
INITIAL_PROJECT_GIT_OPTIONSNot setOptions passed to git clone when INITIAL_PROJECT is a http*.git URL.-b develop
DISABLE_AUTORUN_SCRIPTSNot setSet to 1 to disable autorun scripts.1
DISABLE_AUTORUN_CREATING_PROJECTNot setDo not create a project from INITIAL_PROJECT when 1. It does not stop copying /source/.1
DISABLE_AUTORUN_COPYING_SOURCENot setDo not copy the files of /source/ into the app directory when 1. The copy runs only when the app directory is empty.1
DISABLE_AUTORUN_COMPOSER_INSTALLNot setSkip running composer install during startup when set to 1. It does not stop COMPOSER_OPTIMIZE_AUTOLOADER.1
DISABLE_AUTORUN_GENERATING_INDEXNot setDo not generate index.php when set to 1.1
DISABLE_AUTORUN_FIX_OWNER_GROUPNot setWhen 1, no command changes the owner of files in APP_PATH or DOCUMENT_ROOT: no ownership fix, no owner on created directories, and no UID/GID migration there. Files and directories the container creates there, such as a missing DOCUMENT_ROOT or index.php, belong to root, so set the owner of paths that APP_USER must write to yourself.1
COMPOSER_OPTIMIZE_AUTOLOADERNot setWhen 1, run composer dumpautoload --optimize --no-cache at startup if composer.lock and the vendor directory exist.1
⁠Cron
Setting NameDefault ValueDescriptionExample
ENABLE_CRONTABNot setEnable Crontab service when 1. Jobs are loaded from $CRONTAB_DIR.1
ENABLE_CRONTAB_DEBUGNot setAdds a debug cron job that runs every minute when 1. When set, it takes priority over DEBUG, so 0 removes the job even if DEBUG=1.1
CRONTAB_DIR/etc/crontab.dDirectory for crontab definitions. Jobs run as $APP_USER./path/for/crontab/schedules
CRONTAB_HOME$APP_PATHHome directory for cron jobs./path/for/crontab
CRONTAB_MAILTONot setEmail address to receive cron output.[email protected]
CRONTAB_PATH$PATHExecutable search path used by cron jobs./path/for/crontab/bin
CRONTAB_SETTINGSNot setDefine cron jobs directly via environment variable.0 0 * * * echo "..."
CRONTAB_SHELL/bin/bashDefault shell for cron jobs./bin/bash
CRONTAB_TZ$TZTimezone used for cron jobs.Asia/Tokyo
⁠Tuning and Apache
Setting NameDefault ValueDescriptionExample
ENABLE_TUNING_FPMNot enabledEnable automatic tuning of PHP-FPM settings when set to 1. Uses the CPU limit of the container, or the host CPU count if there is none. A PHP_PM* variable you pass replaces the tuned value. See Customizing PHP-FPM⁠.1
ENABLE_TUNING_MPMNot enabledEnable automatic tuning of Apache MPM settings when set to 1. Uses the CPU and memory limits of the container, or the host values if there are none.1
APACHE_ALLOW_OVERRIDEallValue of AllowOverride for the document root. none skips reading .htaccess files on every request, but rules in .htaccess stop working.none
APACHE_DEFLATE_LEVEL5Compression level of mod_deflate, from 1 (fast) to 9 (smallest).6
APACHE_DISABLE_MODULESNot setApache modules to turn off at start, separated by spaces.cache cgid mime_magic negotiation
APACHE_EXPIRESaccess plus 1 monthCache lifetime that mod_expires sets for static file types such as CSS, JS, images and fonts.access plus 1 week
APACHE_KEEPALIVE_TIMEOUT5Seconds that Apache keeps an idle connection open.2
APACHE_MPM_PROFILEbalancedProfile used by ENABLE_TUNING_MPM: low, balanced or high.high
APACHE_REFERRER_POLICYstrict-origin-when-cross-originValue of the Referrer-Policy response header.no-referrer
APACHE_REQUEST_READ_TIMEOUTheader=20-40,MinRate=500 body=20,MinRate=500Value of RequestReadTimeout. It closes connections that send the request too slowly.header=10-20,MinRate=500 body=20,MinRate=500

⁠About This Project

⁠Supported Platforms

See the Docker Hub tags⁠ for the platforms of each image.

Note

From 2026-10-25, the rarely used platforms `linux/386`, `linux/arm/v7`, `linux/ppc64le` and `linux/s390x` are no longer built. Tags published before that date keep them until they are rebuilt.
⁠Stable Image Tags

For stable, production-ready images we maintain dated tags in a separate repository.

Note

View stable tags: https://hub.docker.com/r/shinsenter/php-archives/tags
⁠How Images Are Updated

Images are no longer rebuilt on a daily schedule. The Watch updates⁠ workflow runs every 30 minutes and compares each published image with its current inputs:

  • the official php image of that version (new PHP release or security fix)
  • Composer, docker-php-extension-installer, s6-overlay, FrankenPHP, RoadRunner
  • the base OS images and the files in this repository

When an input changes, only the affected PHP versions are built again. Images that are still up to date are skipped without being pulled or built. A Composer, extension installer or s6-overlay update rebuilds the maintained versions only. Outdated versions are rebuilt when their official PHP image changes.

Each published image carries the fingerprint of its inputs in the org.shinsenter.build.fingerprint annotation of the image index:

docker buildx imagetools inspect --raw shinsenter/php:8.5-fpm | jq '.annotations'

These images are built from the official php image, so they can only be rebuilt after the official image for the new PHP release is published on Docker Hub. To check the PHP version in a container, run php -v.

Maintainers can start a build by hand from the Build all⁠ workflow, with php_versions (for example 8.5 8.4) and force_build.

⁠Contributing

If these images are useful to you, donate via PayPal⁠ or open an issue on GitHub⁠.

⁠License

This project is licensed under the GNU General Public License v3.0⁠.

Please respect the work that went into these images. If you reuse ideas from this project, credit is appreciated.


From Vietnam šŸ‡»šŸ‡³ with love.

Tag summary

Content type

Image

Digest

sha256:66e14bbca…

Size

39.7 MB

Last updated

6 minutes ago

docker pull shinsenter/php:dev-7.3-fpm-alpine

This week's pulls

Pulls:

49,824

Last week