Getting Started

Quick start

Run a PHP script, serve an application over HTTP or HTTPS, and wire the image into Docker Compose.

Run a PHP script

The php image is a CLI image. Its entrypoint renders php.ini from your environment and then runs whatever command you pass.

docker run --rm adamrollogi/php:8.4 php -v
docker run --rm -v $(pwd):/app -w /app adamrollogi/php:8.4 php artisan migrate
docker run --rm -v $(pwd):/app -w /app adamrollogi/php:8.4 composer install

Serve an application over HTTP

php-nginx runs Nginx and PHP-FPM together. Containers run as an unprivileged user by default, so bind to a port above 1024.

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

/health returns 200 OK and is what the built-in Docker health check uses.

curl -sf http://localhost:8080/health
Without a volume the image serves a one-line placeholder page, never phpinfo(), so a misconfigured deployment cannot leak environment variables.

HTTPS with a self-signed certificate

Useful for local development or behind a load balancer that re-encrypts.

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

Plain HTTP on 8080 redirects to HTTPS on 8443, except for /health and ACME challenges under /.well-known/.

HTTPS with your own certificate

Mount the certificate and key read-only. Running as root here only so that ports 80 and 443 can be bound.

docker run --rm -d -p 80:80 -p 443:443 --user root \
  -e NGINX_SSL_ENABLED=true \
  -e NGINX_SERVER_NAME=app.example.com \
  -v /path/to/cert.pem:/etc/nginx/ssl/cert.pem:ro \
  -v /path/to/key.pem:/etc/nginx/ssl/key.pem:ro \
  -v $(pwd)/public:/var/www/html:ro \
  adamrollogi/php-nginx:8.4

Set NGINX_SERVER_NAME so the HTTP to HTTPS redirect targets your hostname rather than whatever Host header a client sends. See SSL and HTTPS.

Docker Compose

compose.yaml
services:
  app:
    image: adamrollogi/php-nginx:8.4
    ports:
      - "8080:8080"
    environment:
      NGINX_PORT: 8080
      PHP_MEMORY_LIMIT: 256M
      PHP_POST_MAX_SIZE: 32M
      PHP_UPLOAD_MAX_FILESIZE: 32M
      php.date.timezone: Europe/London
      fpm.pool.pm.max_children: 20
    volumes:
      - ./public:/var/www/html:ro
    healthcheck:
      test: ["CMD", "healthcheck.sh"]
      interval: 30s
      timeout: 3s
      retries: 3

  worker:
    image: adamrollogi/php:8.4
    command: php artisan queue:work
    working_dir: /app
    volumes:
      - ./:/app

The php.* and fpm.pool.* keys are the generic overrides: any php.ini or FPM pool setting can be set this way. See PHP settings.

Development with Xdebug

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

Xdebug connects back to host.docker.internal:9003 when triggered. See the php-nginx-dev page for the variables.

Next steps

Tags and versions

Pick between the minor tag and the exact-release tag, and learn how updates arrive.

Environment variables

The complete reference for every variable across all images.

Logging

Tagged streams, JSON output and per-service files.

Users and permissions

Non-root by default, root mode, and a read-only root filesystem recipe.