Images

php-nginx

Nginx and PHP-FPM in one container, with HTTPS, security headers, a health endpoint and tagged logs.

adamrollogi/php-nginx:{version} adds Nginx 1.30 and PHP-FPM on top of the php image. Mount your application at /var/www/html and it is served immediately.

Quick start

docker run --rm -d -p 8080:8080 \
  -e NGINX_PORT=8080 \
  -v $(pwd)/public:/var/www/html:ro \
  adamrollogi/php-nginx:8.4

Ports 80 and 443 are exposed but, because the container runs as UID 1000 by default, you bind to 8080 and 8443 unless you run with --user root. See Users and permissions.

What you get out of the box

  • Health endpoint. GET /health returns 200 OK with the security headers and is used by the image's own HEALTHCHECK.
  • Security headers. X-Frame-Options, X-Content-Type-Options, Referrer-Policy and Permissions-Policy on every response, plus HSTS when HTTPS is on.
  • Hidden files denied. Anything under a dot-prefixed path (/.env, /.git/...) returns 403, except /.well-known/, which is served for ACME challenges.
  • Safe PHP handling. cgi.fix_pathinfo=0 and security.limit_extensions=.php, so /image.png/evil.php style attacks fail.
  • Sensible limits. client_max_body_size follows PHP_POST_MAX_SIZE; 60-second timeouts; gzip for text assets.
  • No surprises on the default page. Without a volume you get a static one-line placeholder, never phpinfo().
  • IPv4 and IPv6 listeners, with NGINX_LISTEN_IPV6=false for hosts that disable IPv6.
  • Tagged logs for [nginx], [php-fpm], [entrypoint] and, in root mode, [cron].

Process model

ModeHow it runsUsers
Non-root (default)The entrypoint starts PHP-FPM and Nginx directly and waits on both. No cron.Everything as appuser (UID 1000).
Root (--user root)Supervisor manages cron, Nginx and PHP-FPM.Nginx workers as nginx, FPM workers as nobody, masters and cron as root.

Either way, every service is started through run-service, so its log lines carry the service name.

Configuration

Everything is an environment variable. The pages below cover each area in depth; this is the shape of it.

AreaKey variablesPage
PHPPHP_MEMORY_LIMIT, PHP_POST_MAX_SIZE, php.<setting>PHP settings
PHP-FPMPHP_FPM_MAX_CHILDREN, PHP_FPM_LISTEN, fpm.pool.<setting>PHP settings
NginxNGINX_PORT, NGINX_SERVER_NAME, NGINX_DOCUMENT_ROOTNginx
HTTPSNGINX_SSL_ENABLED, NGINX_SSL_SELF_SIGNED, NGINX_SSL_REDIRECT_HOSTSSL and HTTPS
LoggingLOG_MODE, LOG_FORMAT, NGINX_ACCESS_LOGLogging

Drop-in configuration

For anything the variables do not cover, mount or COPY files into these directories. They are read at startup and stay root-owned.

DirectoryScopeTypical use
/etc/nginx/http-conf.d/*.confhttp blockmap, upstream, log_format, rate-limit zones
/etc/nginx/after.d/*.confinside the app server blockextra location blocks, rewrites, headers
/etc/nginx/http.d/*.confadditional server blocksa second vhost on another port

Layout

PathPurpose
/var/www/htmlDocument root (NGINX_DOCUMENT_ROOT).
/usr/local/etc/nginx/*.templateNginx server block templates (HTTP, HTTPS, redirect). Root-owned.
/usr/local/etc/php/www.conf.templatePHP-FPM pool template.
/run/nginx/nginx.confRendered main config, derived from the stock /etc/nginx/nginx.conf.
/run/nginx/http.d/*.confRendered server blocks.
/run/nginx/logging.confjson_combined log format definition.
/run/php/php.ini, /run/php/php-fpm.conf, /run/php/php-fpm.d/www.confRendered PHP and FPM config.
/etc/nginx/ssl/Default certificate location, writable so self-signed certificates can be generated.
/usr/local/bin/php-nginx-entrypoint.shEntrypoint.
/usr/local/bin/healthcheck.shHEALTHCHECK command; always probes /health on the right port and scheme.

Nginx is started with -c /run/nginx/nginx.conf and PHP-FPM with -y /run/php/php-fpm.conf -c /run/php/php.ini, which is what keeps /etc read-only to the runtime user.