Getting Started

These images vs others

The official PHP images are a clean starting point. Before one can serve a web app in production, you add a non-root user, a web server, Composer, and the extensions your app needs. serversideup/php starts from the same source and ships with those parts in place.

Same PHP, production defaults

We compile PHP from the same docker-library/php Dockerfiles as the official images. Your code runs the same way. The defaults are what change.

Official phpserversideup/php
Default userrootwww-data (UID 33 on Debian, 82 on Alpine)
Web server ports808080 and 8443 (fpm: 9000)
PHP settings from environment variables❌✅
Composer and install-php-extensions❌✅
pcntl, pdo_mysql, pdo_pgsql, redis, zip❌✅
NGINX + PHP-FPM image❌✅
Apache imagemod_phpPHP-FPM
FrankenPHP image❌✅
Built-in health check❌✅ (all but cli)
Real client IPs behind Cloudflare❌✅
Laravel automations at startup❌✅ (opt-in)
Debian, Alpine, amd64, arm64✅✅
RegistriesDocker HubDocker Hub, GitHub Packages

New PHP patches without waiting for a base image

We build PHP from php.net ourselves instead of waiting for a new base image on Docker Hub. We check php.net every hour, and a new patch release starts a build right away. The images publish once that build passes its tests.

The floating tags, such as 8.5-fpm-nginx, also rebuild each Tuesday to pick up operating system security fixes. How images are built has the details.

Images you can verify

Every image is signed and carries an SBOM and build provenance. Check them with cosign before you deploy. Verifying images shows the commands.

Requests for .env get a 403

NGINX, Apache, and Caddy refuse requests for dotfiles such as .env and for files ending in .log, .sql, or .bak. Your app's responses get X-Frame-Options, X-Content-Type-Options, and Referrer-Policy headers.

FrankenPHP with the same defaults

Our frankenphp variation builds FrankenPHP on our PHP base. It shares every default above. It runs as www-data on 8080 and 8443 and serves /var/www/html/public.

Caddy's automatic HTTPS and admin API start off (CADDY_AUTO_HTTPS=off, CADDY_ADMIN=off). That fits a container behind a reverse proxy. Turn HTTPS on when you need it; the FrankenPHP variation page explains how.

Switch an existing project

Moving from the official image takes a few changes: the port, the document root, and how you install extensions. The migration guide lists each one.

Follow the migration guide

Default configurations lists every package, extension, and startup script we add.