Awesome
dirtsimple/php-server
Overview
This is a docker image for an alpine nginx + php-fpm combo container, with support for:
- Cloning a git repo at container start (and running
composer install
if applicable) - Running scheduled jobs (cron), on-file-change jobs (cortesi/modd), webhooks (adnanh/webhook), or other supervisord-controlled tasks
- Build arguments to allow adding extra packages and PHP extensions
- Environment-based templating of any configuration file in the container at startup
- Running any user-supplied startup scripts
- 100% automated HTTPS certificate management via certbot and Let's Encrypt
- Robust privilege separation and defense-in-depth for a variety of development and production use cases
Inspired by richarvey/nginx-php-fpm, this image supports most of that image's configuration flags, plus many, many enhancements and bug fixes like these:
- Configuration files are generated using dockerize templates instead of
sed
, and boolean environment variables can be set totrue
orfalse
, not just1
or0
- Your code can provide additional configuration files to be processed w/dockerize at container start time (or you can mount replacements for this image's configuration templates under
/tpl
) - Ready-to-use support for most PHP "front controllers" (as used by Wordpress, Laravel, Drupal, Symfony, etc.): just set
PHP_CONTROLLER
totrue
andPUBLIC_DIR
to the subdirectory that contains the relevantindex.php
(if any). (PATH_INFO
support is also available, for e.g. Moodle.) - HTTPS is as simple as setting a
DOMAIN
andLETS_ENCRYPT=my@email
: registration and renewals are immediate, painless, and 100% automatic. The certs are saved in a volume by default, and renewals happen on container restart, as well as monthly if you enable cron. - cron jobs are supported by setting
USE_CRON=true
and putting the job data in/etc/crontabs/nginx
, or an executable file in one of the/etc/periodic/
subdirectories (via volume mount, startup script, or template files) - You can add
.ini
files to/etc/supervisor.d/
to add additional processes to the base supervisor configuration, or to override the default supervisor configurations for nginx, php-fpm, etc. php-fpm
pool parameters can be set with environment vars (FPM_PM
,FPM_MAX_CHILDREN
,FPM_START_SERVERS
,FPM_MIN_SPARE_SERVERS
,FPM_MAX_SPARE_SERVERS
,FPM_MAX_REQUESTS
)- nginx's
set_real_ip_from
is recursive, and supports Cloudflare (viaREAL_IP_CLOUDFLARE=true
) as well as your own load balancers/proxies (viaREAL_IP_FROM
-- which can include multiple addresses, separated by spaces.) - Additional alpine APKs, PHP core extensions, and pecl extensions can be installed by setting
EXTRA_APKS
,EXTRA_EXTS
, andEXTRA_PECL
as environment variables or build-time arguments. sendfile
is turned on for optimal static file performance, unless you setVIRTUALBOX_DEV=true
- Configuration files don't grow on each container restart
- Developer and server priviliges are kept separate: git and composer are run as a
developer
user rather than as root, and files are owned by that user. To be written to by PHP and the web server, files or directories must be explicitly listed inNGINX_WRITABLE
. (The whole codebase isNGINX_READABLE
by default, but can be made more restrictive by listing specific directories.) - You can mount your code anywhere, not just
/var/www/html
(just setCODE_BASE
to whatever directory you like) - If any supervised process (nginx, php-fpm, cron, etc.) enters a
FATAL
state, the entire container is shut down, so that configuration or other errors don't produce a silently unresponsive container. - Command-line PHP scripts run with a file-based opcache under
/tmp
, speeding start times for large PHP command line tools such aswp-cli
,artisan
, etc. (For compatibility reasons, this cache is disabled whenENABLE_XDEBUG
is true.) Command-line scripts run without a memory limit, unless you setPHP_CLI_MEMORY
to a memory value like512M
.
Note: there are a few configuration options that must be specified in a different way than the richarvey image, or which have different defaults: see Backward-Compatibility Settings, below, for more info.
Contents
<!-- toc -->- Adding Your Code
- Configuration Templating
- Nginx Configuration
- Adding Extensions
- Supervised Tasks
- Version Info
Adding Your Code
This image assumes your primary application code will be found in the directory given by CODE_BASE
(which defaults to /var/www/html
). You can place it there via a volume mount, installation in a derived image, or by specifying a GIT_REPO
environment variable targeting your code.
If a GIT_REPO
is specified, the given repository will be cloned to the CODE_BASE
directory at container startup, unless a .git
subdirectory is already present (e.g. in the case of a restart, or a mounted checkout). If GIT_BRANCH
is set, the specified branch will be used. You can also supply a base64-encoded SSH_KEY
to access protected repositories (including any checkouts done by composer
).
(Important: do not both mount your code as a volume and provide a GIT_REPO
: your code will be erased unless it's already a git checkout, or you set REMOVE_FILES=false
in your environment.)
Whether you're using a GIT_REPO
or not, this image checks for the following things in the CODE_BASE
directory during startup:
- a
composer.lock
file (triggering an automaticcomposer install
run if found) - Any configuration template directories specified in
DOCKERIZE_TEMPLATES
(see "Configuration Templating" below for details) - A list of startup scripts (or globs thereof), specified by
RUN_SCRIPTS
that will be run as root in glob-sorted order during container startup, just beforesupervisord
is launched. (The search pattern defaults toscripts/*
ifRUN_SCRIPTS
is set to1
,true
,TRUE
,T
, ort
.) These scripts must not be writable by the nginx user; the container will refuse to start if any of them are. (RUN_SCRIPTS
can be a space-separated list of individual scripts in order to force them to be run in that order; if they are globs then each group will be glob-sorted but the order of groups will be as defined byRUN_SCRIPTS
.)
Note: if you are using a framework that exposes a subdirectory (like web
or public
) as the actual directory to be served by nginx, you must set the PUBLIC_DIR
environment variable to that subdirectory (e.g. public
). (Assuming you don't override the default web server configuration; see more below.)
Pulling Updates and Pushing Changes
You can pull updates from GIT_REPO
to CODE_BASE
by running the pull
command via docker exec
or docker-compose exec
, as appropriate. If the pull is successful, the container will immediately shutdown so that it will reflect any changed configuration upon restart. If you're using a docker-compose container with restart: always
, the container should automatically restart. Otherwise you will need to explicitly start the container again.
(Note that you must set GIT_NAME
to a commiter name, and GIT_EMAIL
to a committer email, in order for pull operations to work correctly.)
For compatibility with richarvey/nginx-php-fpm, there is also a push
command that adds all non-gitignored files, commits them with a generic message, and pushes them to the origin. (You're probably better off looking at the script as a guide to implementing your own, unless those are your exact requirements.)
Permissions and the developer
User
If you use any of the git
or composer
features of this image, they will be run using a special developer
user that's created on-demand. This user is created inside the container, but you can set DEVELOPER_UID
and/or DEVELOPER_GID
so that the created user will have the right permissions to access or update files mounted from outside the container. The developer user is also added to the nginx
group inside the container, so that it can read and write files created by the app. Once the user is created, ownership of the entire CODE_BASE
directory tree is changed to developer
.
If you need to run tasks inside the container as the developer user, you can use the as-developer
script, e.g. as-developer composer install
. (The push
and pull
commands and the container start script already use as-developer
internally to run git and composer.)
Composer Configuration, PATH
, and Tools
When running a command under as-developer
, the PATH
is expanded to include $COMPOSER_HOME/vendor/bin
and $CODE_BASE/vendor/bin
. This allows you to easily run project-specific tools, and also to override them with globally-installed tools using GLOBAL_REQUIRE
.
Setting the GLOBAL_REQUIRE
environment variable to a series of package specifiers causes them to be installed globally, just after templates are processed and before the project-level composer install. For example setting GLOBAL_REQUIRE
to "psy/psysh wp-cli/wp-cli"
would install both Psysh and the Wordpress command line tools as part of the container.
(You can also use GLOBAL_REQUIRE
as a build-time argument, to specify packages to be built into the container.)
The COMPOSER_OPTIONS
variable can also be set to change the command line options used by the default composer install
run. It defaults to --no-dev --optimize-autoloader
, but can be set to an empty string for a development environment. If you need finer control over the installation process, you can also disable automatic installation by setting SKIP_COMPOSER=true
, and then running your own installation scripts with RUN_SCRIPTS
(which are run right after the composer-install step.
Last but not least, you can set COMPOSER_CONFIG_JSON
to a string of JSON to be placed in $COMPOSER_HOME/config.json
. This can be useful for adding site-specific repository or authentication information to a project's composer.json
. (New in version 1.2)
Configuration Templating
This image uses dockerize templates to generate arbitrary configuration files from templates. Templates are loaded first from the image's bundled/tpl
directory, and then from the subdirectories of CODE_BASE
identified by the DOCKERIZE_TEMPLATES
environment variable.
If used, DOCKERIZE_TEMPLATES
should be a space-separated series of source:destination
directory name pairs. For example, if it were set to this:
.tpl:/ .nginx:/etc/nginx .supervisor:/etc/supervisor.d .periodic:/etc/periodic
it would mean that your project could contain a .tpl
directory, whose contents would be recursively expanded to the root directory, an .nginx
subdirectory expanded into /etc/nginx
, and so on. Source and destination can both be absolute or relative; relative paths are interpreted relative to CODE_BASE
.
The path of an output configuration file is derived from its relative path. So, for example, the default template for /etc/supervisord.conf
can be found in /tpl/etc/supervisord.conf
, and with the above DOCKERIZE_TEMPLATES
setting it could be overrridden by a template in $CODE_BASE/.tpl/etc/supervisord.conf
.
Templates found in the image-supplied /tpl
are applied at the very beginning of container startup, before code is cloned or startup scripts are run. Templates in DOCKERIZE_TEMPLATES
directories are applied just after the code checkout (if any), and just before composer install
(if applicable).
Template files are just plain text, except that they can contain Go template code like {{.Env.DOMAIN}}
to insert environment variables. Please see the dockerize documentation and Go Text Template language reference for more details, and this project's tpl
subdirectory for examples.
(Note: for improved security, template files are not processed if they are writable by the nginx
user. If even one template file is writable by the web server or php-fpm, the container will refuse to start.)
Nginx Configuration
Config Files
This image generates and uses the following configuration files in /etc/nginx
, any or all of which can be replaced using mounts or template files:
app.conf
-- the main app configuration for running PHP and serving files under the document root. In general, if you need to change your nginx configuration, this is the first place to look. Its contents are included inside of theserver {}
blocks for both the http and https servers, so they can both be configured from one file.*.app.conf
-- any files named with this pattern are loaded immediately afterapp.conf
; by adding files named this way, you can extend the base configuration without needing to copy the defaultapp.conf
.webhooks.app.conf
-- proxy configuration for the webhooks tool; you can override this to customize webhook routing or add extra authorization requirements for connections coming from outside the container.static.conf
-- configuration for static files. This is included inapp.conf
under anyEXCLUDE_PHP
locations. With the exception ofexpires
, any settings here should be wrapped in location sub-blocks. The default version of this file includes settings for nginx's mp4 and flv modules, linked to the appropriate file types.http.conf
-- extra configuration for thehttp {}
block, empty by default. (Use this to define maps, caches, additional servers, etc.)nginx.conf
-- the main server configuration, with anhttp
block that includeshttp.conf
and any server configs listed in thesites-enabled/
subdirectorysites-available/default.conf
-- theserver
block for the HTTP and HTTPS protocol; includesapp.conf
to specify locations and server-level settings other than the listening port/protocol/certs/etc. HTTPS is only enabled if$DOMAIN
is set and a private key is available in/etc/letsencrypt/live/$DOMAIN
. (This file is symlinked fromsites-enabled
by default.)cloudflare.conf
-- the settings needed for correct IP detection/logging when serving via cloudflare; this file is automatically included bynginx.conf
ifREAL_IP_CLOUDFLARE
is true.
For backwards compatibility with richarvey/nginx-php-fpm
, you can include conf/nginx/nginx.conf
, conf/nginx/nginx-site.conf
, and/or conf/nginx/nginx-site-ssl.conf
file(s) in your CODE_BASE
directory. Doing this will, however, disable any features of app.conf
that you don't copy into them. It's recommended that you use .nginx/app.conf
instead, going forward.
Environment
In addition, the following environment variables control how the above configuration files behave:
PUBLIC_DIR
-- the subdirectory ofCODE_BASE
that should be used as the server's default document root. If not specified,CODE_BASE
is used as the default document root.EXCLUDE_PHP
-- a space-separated list of absolute location prefixes where PHP code should not be executed (e.g./wp-content/uploads
for Wordpress's file upload directory). Within these locations, paths containing.php
will return 404 errors, and everything else will be processed according to the rules instatic.conf
FORCE_HTTPS
-- boolean: redirect all http requests to https; ifFORWARDED_SSL
is in effect, X-Forwarded-Proto is used to determine whether the request is httpsREAL_IP_CLOUDFLARE
-- boolean: if true, trust Cloudflare to provide the true client IP, using the addresses listed incloudflare.conf
REAL_IP_FROM
-- space-separated list of address/netmask values designating proxies to trust as to the identity of the real client's IPFORWARDED_SSL
-- boolean: if true, trust Cloudflare or other proxies to say whether the connection is HTTPS or not, and override theHTTPS
,SERVER_NAME
, andSERVER_PORT
fastcgi variables to matchPHP_ACCESS_LOG
andSTATIC_ACCESS_LOG
-- theaccess_log
settings to be used for PHP and static files, defaulting to/dev/stdout
andoff
, respectively.PHP_LOG_ERRORS
-- boolean: if true, turns on PHP'slog_errors
setting. True by default.APP_URL_PREFIX
-- a prefix to put in front of theSCRIPT_NAME
,REQUEST_URI
, andDOCUMENT_URI
passed to PHP. This should only be used when behind a proxy that is removing this prefix, e.g. when using a traefikPathPrefixStrip
rule. (Note that apps which assume a static relationship between URIs andDOCUMENT_ROOT
will not work properly with this setting and may require patching.)USE_FORWARDED_PREFIX
-- boolean: use traefik'sX-Forwarded-Prefix
header to determine theAPP_URL_PREFIX
. Only takes effect ifAPP_URL_PREFIX
isn't set, and you should not use this unless the app is behind a proxy that always sets this header (e.g. traefik 1.3+ with aPathPrefixStrip
rule).NGINX_IPV6
-- boolean: enables IPV6 in the http and/or https server blocks. (Otherwise, only IPV4 is used.)NGINX_WORKERS
-- number of nginx worker processes; defaults to 1STATIC_EXPIRES
-- expiration time to use for static files; if not set, use nginx defaultsVIRTUALBOX_DEV
-- boolean: disables thesendfile
option (use this when doing development with Docker Toolbox or boot2docker with a volume synced to OS X or Windows)
Backward-Compatibility Settings
If you want extreme backward compatibility with the default settings of richarvey/nginx-php-fpm
, you can use the following settings:
NGINX_IPV6=true
STATIC_EXPIRES=5d
VIRTUALBOX_DEV=true
(not really needed unless you're actually using virtualbox)NGINX_READABLE=.
andNGINX_WRITABLE=.
, to make the entire codebase readable and writable by nginx+php (which is much less secure)NGINX_WORKERS=auto
PHP_LOG_ERRORS=false
EXTRA_EXTS=pgsql pdo_pgsql redis xsl soap xdebug
(if you need any of these extensions)EXTRA_APKS=nginx-mod-http-geoip nginx-mod-stream-geoip
(if you need nginx geoip modules)
The following features of richarvey/nginx-php-fpm
are not directly supported by this image, and must be configured in a different way:
APPLICATION_ENV=development
-- setCOMPOSER_OPTIONS
to an empty string instead to disable the--no-dev
flagSKIP_CHOWN
-- this image doesn't chown the code tree except when doing a git checkout. But it does chgrp the code tree to the nginx user and set everything group readable by default, unless you explicitly set a differentNGINX_READABLE
value. So the equivalent toSKIP_CHOWN
would be to explicitly setNGINX_READABLE
to empty, and not set values for any of the other permission variables (described under File Permissions below).PHP_ERRORS_STDERR
-- this image always directs the PHP error log to stderr, and logs errors by default. To disable error output, setPHP_LOG_ERRORS=false
.- If you want a custom 404 page, you need to configure it via a configuration file
PHP Front Controllers and PATH_INFO
Many PHP frameworks use a central entry point like index.php
to process all dynamic paths in the application. If your app is like this, you can set PHP_CONTROLLER
to true
to get a default front controller of /index.php$is_args$args
-- a value that works for correctly a wide variety of PHP applications and frameworks. If your front controller isn't index.php
or needs different parameters, you can specify the exact URI to be used instead of true
. (If the document root isn't the root of your code, you need to set PUBLIC_DIR
as well.)
For example, if you are deploying a Laravel application, you need to set PUBLIC_DIR
to public
, and PHP_CONTROLLER
to true
. Then, any URLs that don't resolve to static files in public
will be routed through /index.php
instead of producing nginx 404 errors.
By default, PATH_INFO
is disabled, meaning that you cannot add trailing paths after .php files. If you need this (e.g. for Moodle), you can set USE_PATH_INFO
to true
, and then you can access urls like /some/file.php/other/stuff
. As long as /some/file.php
exists, then it will be run with $_SERVER['PATH_INFO']
set to /other/stuff
. If you also enable PHP_CONTROLLER
, then the default PHP_CONTROLLER
will be /index.php$uri?$args
, so that the front controller gets PATH_INFO
set as well. (You can override this by explicitly setting PHP_CONTROLLER
to the exact expression desired.)
File Permissions
For security, you must specifically make files readable or writable by nginx and php, using the NGINX_READABLE
and NGINX_WRITABLE
variables. Each is a space-separated lists of file or directory globs (with **
recursion supported) which will be recursively chgrp
'd to nginx and made group-readable or group-writable, respectively. Paths are interpreted relative to CODE_BASE
, and the default NGINX_READABLE
is .
, meaning the entire code base is readable by default. If you are using a web framework that writes to the code base, you must add the affected directories and/or files to NGINX_WRITABLE
. For frameworks like wordpress that require certain files to be owned by the web server, you can use NGINX_OWNED
.
In some cases, it may be easier to specify what should not be readable or writable, or to carve out specific exceptions in a larger grant of access. For this purpose, you can remove group+world read/write/execute access via NGINX_NO_RWX
, and group+world write access viaNGINX_NO_WRITE
. These settings are applied after the readable/writable/owned ones, and so can carve out exceptions within them.
Both variables can be used to list file or directory paths that will be recursively chown
'd to developer
(creating the developer
user if necessary), and then have the relevant permissions revoked. For simplicity's sake, existing file groups are not checked, so if you are sharing files outside the container this may not do what you want or expect.
Note: file permissions are applied prior to processing template files and running startup scripts, so if you make your entire codebase writable you will not be able to use configuration templates or startup scripts. To preserve separation of privileges within the container, you will need to explicitly list subdirectories of your code that do not include your templates or startup scripts, or else list those templates and startup scripts under NGINX_NO_WRITE
.
In addition, please note that these permission changes are applied only to files and directories that actually exist: files created by startup scripts or nginx/php later will not magically have these permissions applied. To get the results you want in such cases, you may need to apply permissions to a parent directory, or pre-create the necessary files or directories yourself.
HTTPS and Let's Encrypt Support
HTTPS is automatically enabled if you set a DOMAIN
and there's a private key in /etc/letsencrypt/live/$DOMAIN/
.
If you want the key and certificate to be automatically generated, just set LETS_ENCRYPT
to your desired registration email address, and certbot
will automatically run at container start, either to perform the initial registration or renew the existing certificate. (You may want to make /etc/letsencrpt
a named or local volume in order to ensure the certificate persists across container rebuilds.)
If your container isn't restarted often enough to ensure timely certificate renewals, you can set USE_CRON=true
, and an automatic renewal attempt will also happen on the first of each month at approximately 5am UTC.
(Note: certbot uses the "webroot" method of authentication, so the document root of DOMAIN
must be the server's default document root (i.e. $CODE_BASE/$PUBLIC_DIR
), or else certificate authentication will fail. Once a certificate has been requested, the default document root directory must remain the same for all future renewals. Also note that the .well-known
directory under the webroot should not be made inaccessible to the webserver; i.e., it needs to be NGINX_READABLE
, at least.)
Adding Extensions
Additional alpine APKs, PHP core extensions, and pecl extensions can be installed using the EXTRA_APKS
, EXTRA_EXTS
, and EXTRA_PECL
build-time arguments, respectively. For example, one might use this in a docker-compose.yml
to build a server for Moodle:
version: '2'
services:
moodle:
build:
context: https://github.com/dirtsimple/php-server.git
args:
- PHP_VER=7.2 # build from php:7.2-fpm-alpine3.10
- OS_VER=3.10
- EXTRA_APKS=ghostscript graphviz
- EXTRA_EXTS=xmlrpc pspell ldap memcached
environment:
- GIT_REPO=https://github.com/moodle/moodle.git
- GIT_BRANCH=MOODLE_33_STABLE
- NGINX_WRITABLE=/moodledata
- USE_PATH_INFO=true
For performance's sake, it's generally better to specify extras at build-time, but as a development convenience you can also pass them to the container as environment variables to be installed or built during container startup. (Which, of course, will be slower as a result.)
Specific versions of a PECL module can be forced by using a :
, e.g. EXTRA_PECL=mcrypt:1.0.2
. (A :
is used in place of a -
so that the version can be stripped from the extension name in generated PHP .ini file(s).)
As of the 2.x versions of this image, EXTRA_EXTS
are built using mlocati/docker-php-extension-installer, so you no longer need to specify EXTRA_APKS
for any of the extensions it supports, and you can build supported PECL extensions using EXTRA_EXTS
, without needing EXTRA_PECL
, unless you need to specify a particular module version, or need an extension that isn't supported by docker-php-extension-installer. (But in such cases, you must explicitly list any needed packages in EXTRA_APKS
, since the automatic installer won't be handling it for you.)
Supervised Tasks
Any files named /etc/supervisor.d/*.ini
are included as part of the supervisord configuration, so that you can add your own supervised tasks. (For example, if you wanted to add a mysql or openssh server.) This image's own tasks are there as well, and can be overridden by your own substitutions in /tpl/etc/supervisor.d
or a DOCKERIZE_TEMPLATES
directory:
nginx.ini
php-fpm.ini
certbot.ini
-- run registration or renewal ifLETS_ENCRYPT
andDOMAIN
are setcron.ini
-- run crond ifUSE_CRON=true
modd.ini
-- run modd (file watcher) ifMODD_CONF
is setwebhook.ini
- run webhooks ifWEBHOOK_CONF
is set
You can override any of these with an empty file to disable the relevant functionality.
Scheduled Jobs (cron)
If you want to add cron jobs, you have two options:
- Generate a
/etc/crontabs/nginx
crontab file - Add scripts to a subdirectory of
/etc/periodic
. Scripts must be in a subdirectory named15min
,hourly
,daily
,weekly
, ormonthly
(e.g. a script placed in/etc/periodic/daily
would be run daily)
Cron jobs listed in the /etc/crontabs/nginx
file will run as the nginx
user; scripts in /etc/periodic/
dirs run as root. You can preface commands in those scripts with as-nginx
to run them as the nginx user, or as-developer
to run them with developer privileges. (Note: the templates for scripts to be placed in /etc/periodic
must have their executable bit set in order to run!)
As always, these configuration files can be generated by mounting templates in /tpl
or via a DOCKERIZE_TEMPLATES
directory inside your codebase.
Changed-File Jobs (modd)
The bundled modd tool lets you watch for changes to files, then run other tasks automatically (such as stopping, starting, or restarting other supervised processes). To enable this, just set MODD_CONF
to the path of a modd config file, and a supervised modd process will be started automatically when the container starts. If the config file changes, modd will restart itself automatically to use the updated configuration.
You can also use MODD_OPTIONS
to supply extra global options to modd, and MODD_DIR
to set the directory where you want it to run, if it's not CODE_BASE
. (If MODD_CONF
is a relative path, it's relative to MODD_DIR
or CODE_BASE
.)
Please note that the modd process runs as root, which means that your config file must not be writable by nginx (or else the container will not start). This also means you should preface most commands in your modd config file with as-developer
or as-nginx
to set the appropriate user ID for the task in question. But if you need a modd job to stop or start other tasks, you can have it simply invoke supervisorctl
with the appropriate options.
Webhooks (adnanh/webhook)
The bundled webhook tool optionally lets you run commands in the container upon receiving webhooks. These variables control where, how, and whether the webhooks are served:
WEBHOOK_CONF
-- the name of a JSON or YAML file with the webhooks' configuration. If not set, webhooks are disabled. The path can be relative or absolute; a relative path is interpreted relative toWEBHOOK_DIR
WEBHOOK_USER
-- the name of the user the webhook tool will run as; defaults tonginx
WEBHOOK_DIR
-- the directory in which the webhook tool runs; defaults to$CODE_BASE
if not set.WEBHOOK_PATH
-- the path under your site's root URL at which webhooks will be accessed. Defaults tohooks
, meaning that a webhook with idfoo
will be reachable via nginx at/hooks/foo
.WEBHOOK_OPTS
-- additional startup arguments to pass to the webhook tool; defaults to-hotreload -verbose
.
The webhook tool is proxied by nginx, so its port does not need to be directly exposed, and so it listens only on 127.0.0.1:9000. If you have something else you need to run on port 9000 (and you also want to run webhooks), you can change the default port with WEBHOOK_PORT
.
Security Considerations
To prevent privilege escalation from web-served PHP, the webhook tool's configuration file must not be writable by nginx (or else the container won't start).
The default WEBHOOK_USER
is nginx
, meaning the webhooks can't do anything that nginx or web-served PHP can't. If your webhooks need greater privileges than that, you can configure the tool to run as developer
(if code needs to be updated), or root
(if process control via supervisorctl
is needed).
Note, however, that this tool is very powerful, and it is ridiculously easy to accidentally create vulnerabilities, especially if you use the developer
or root
user. For example, if your configuration file contains secrets (e.g. to verify a webhook is authorized), then you need to ensure it can't be read by the web server or php, not just written! (Otherwise, a PHP-level exploit can find out the secret, even if the file is not located in the container's PUBLIC_DIR
.) Likewise, if a webhook doesn't have some sort of authorization check as part of its configuration, then any program run inside the container can invoke it by talking directly to port 9000. (Again allowing access by an exploited PHP process.)
Therefore, if a command can have detrimental effects (or if it merely accepts any input whatsoever!), it should probably be protected with some form of authorization check (such as request signatures or an authorization key field). Alternately, the command being run can perform the check, again with the caveat that such scripts should not be readable or writable by nginx or php. You may also need some form of concurrency protection (if webhooks arrive at the same time) and/or rate-limiting (to avoid denial of service or similar issues).
(In addition, if you're using root
to run the webhook tool, you should also ensure webhooks that do not need root access downgrade their privileges with as-developer
or as-nginx
accordingly.)
In short, using this feature may require careful design consideration, as it can easily poke holes in the "defense in depth" architecture this container works so hard to create. (Especially with a non-default WEBHOOK_USER
.)
Version Info
Builds of this image are tagged with multiple aliases to make it easy to pin specific revisions or to float by PHP version. For example, a PHP 7.3.13 image with release 2.1.1 of this container could be accessed via any of the following tags (if 2.1.1 were the latest release of this image):
7.3
7.3-2.x
7.3-2.1.x
7.3-2.1.1
7.3.13
7.3.13-2.x
7.3.13-2.1.x
7.3.13-2.1.1
Note that there is no latest
tag for this image; you must explicitly select at least a PHP version such as 7.3
to get the latest version of this image for that PHP version.
Also note that although you can just specify a PHP version, major releases of this container may be incompatble with older releases due to e.g. changes in OS versions or other factors, so you should probably at least target a specific major release of this container, e.g. 7.3-2.x
or 7.4-3.x
.
Major Versions
- 3.1.x - Added 8.3 and newer versions of 7.4-8.2, added
-alpine3.x
tags so you can distinguish by OS version (e.g.8.0-alpine3.16
vs8.0-alpine3.18
). - 3.x - Alpine 3.10-3.16, Composer 2, PHP 7.1 through 8.2, dropped prestissimo from default
GLOBAL_REQUIRE
, added--optimize-autoloader
to defaultCOMPOSER_OPTIONS
- 2.x - Alpine 3.9, Composer 1, PHP 7.1 through 7.3, build extensions using mlocati/docker-php-extension-installer
- 1.4.x - Alpine 3.9, Composer 1, PHP 7.1 and 7.2, image based on Docker php-fpm-alpine, scripted extension builds
- 1.3.x and older - Alpine 3.6, PHP 7.1 only, implemented as an overlay on the nginx-php-fpm image
Version Details
Tags | PHP | nginx | mod lua | alpine | Notes |
---|---|---|---|---|---|
8.3-3.1.x | 8.3.1 | 1.24.0 | 0.10.24 | 3.18 | These versions are also tagged with an -alpine version suffix |
8.2-3.1.x | 8.2.14 | 1.24.0 | 0.10.24 | 3.18 | |
8.1-3.1.x | 8.1.27 | 1.24.0 | 0.10.24 | 3.18 | |
8.0-3.1.x | 8.0.30 | 1.22.1 | 0.10.21 | 3.16 | |
7.4-3.1.x | 7.4.33 | 1.22.1 | 0.10.21 | 3.16 | |
7.3-3.1.x | 7.3.33 | 1.20.1 | 0.10.19 | 3.14 | |
7.2-3.1.x | 7.2.34 | 1.18.0 | 0.10.15 | 3.12 | |
7.1-3.1.x | 7.1.33 | 1.16.1 | 0.10.15 | 3.10 | |
8.2-3.0.x | 8.2.3 | 1.22.1 | 0.10.21 | 3.16 | Composer 2 |
8.1-3.0.x | 8.1.16 | 1.22.1 | 0.10.21 | 3.16 | |
8.0-3.0.x | 8.0.19 | 1.20.1 | 0.10.19 | 3.14 | |
7.4-3.0.x | 7.4.22 | 1.20.1 | 0.10.19 | 3.14 | |
7.3-3.0.x | 7.3.29 | 1.20.1 | 0.10.19 | 3.14 | |
7.2-3.0.x | 7.2.34 | 1.18.0 | 0.10.15 | 3.12 | |
7.1-3.0.x | 7.1.33 | 1.16.1 | 0.10.15 | 3.10 | |
7.3-2.x | 7.3.13 | 1.14.2 | 0.10.15 | 3.9 | New extension build method for all 7.x-2.x versions |
7.2-2.x | 7.2.26 | 1.14.2 | 0.10.15 | 3.9 | |
7.1-2.x | 7.1.33 | 1.14.2 | 0.10.15 | 3.9 | |
7.2.26-1.4.x | 7.2.26 | 1.14.2 | 0.10.15 | 3.9 | Old extension build method used from here down |
7.1.33-1.4.x | 7.1.33 | 1.14.2 | 0.10.15 | 3.9 | |
1.4.x | 7.1.33 | 1.14.2 | 0.10.15 | 3.9 | |
1.4.0 | 7.1.32 | 1.14.2 | 0.10.15 | 3.9 | |
1.0.x - 1.3.x | 7.1.12 | 1.13.7 | 0.10.11 | 3.6 | Based on upstream 1.3.10 |