Contributing

Thanks for your interest in contributing to this project! Please use read this entire guide before submitting a pull request.

Improve the docs

All our docs are located under /docs. Contributing is very easy and is documented under /docs/README.md.

Learn how to contribute to the docs

Project dependencies

You must have these installed on your system.

How things work

  1. All files are stored in the /src folder
  2. GitHub Actions will automatically build and deploy the images

Running things locally

To run a build, simply run ./scripts/dev.sh (with Docker Desktop running). This will show you a help menu with all the available options.

Example: Building a Unit image running PHP 8.2.12 on Debian Bookworm

bash scripts/dev.sh --variation unit --version 8.2.12 --os bookworm

This will build serversideup/php:8.2.12-unit-bookworm locally on your machine for testing and inspection.

Published Beta Images

We also have beta images that are published from the release/v3.0 branch (or the "main" branch once v3.0 is released).

Debian Variations

⚙️ Variation🚀 Version
cliserversideup/php:beta-8.4-cliserversideup/php:beta-8.3-cliserversideup/php:beta-8.2-cliserversideup/php:beta-8.1-cliserversideup/php:beta-8.0-cliserversideup/php:beta-7.4-cli
fpmserversideup/php:beta-8.4-fpmserversideup/php:beta-8.3-fpmserversideup/php:beta-8.2-fpmserversideup/php:beta-8.1-fpmserversideup/php:beta-8.0-fpmserversideup/php:beta-7.4-fpm
fpm-apacheserversideup/php:beta-8.4-fpm-apacheserversideup/php:beta-8.3-fpm-apacheserversideup/php:beta-8.2-fpm-apacheserversideup/php:beta-8.1-fpm-apacheserversideup/php:beta-8.0-fpm-apacheserversideup/php:beta-7.4-fpm-apache
fpm-nginxserversideup/php:beta-8.4-fpm-nginxserversideup/php:beta-8.3-fpm-nginxserversideup/php:beta-8.2-fpm-nginxserversideup/php:beta-8.1-fpm-nginxserversideup/php:beta-8.0-fpm-nginxserversideup/php:beta-7.4-fpm-nginx
unitserversideup/php:beta-8.4-unitserversideup/php:beta-8.3-unitserversideup/php:beta-8.2-unitserversideup/php:beta-8.1-unitserversideup/php:beta-8.0-unitserversideup/php:beta-7.4-unit

Alpine Variations

To see the size difference between Debian and Alpine, here is a list of our Alpine versions:

⚙️ Variation🚀 Version
cliserversideup/php:beta-8.4-cli-alpineserversideup/php:beta-8.3-cli-alpineserversideup/php:beta-8.2-cli-alpineserversideup/php:beta-8.1-cli-alpineserversideup/php:beta-8.0-cli-alpineserversideup/php:beta-7.4-cli-alpine
fpmserversideup/php:beta-8.4-fpm-alpineserversideup/php:beta-8.3-fpm-alpineserversideup/php:beta-8.2-fpm-alpineserversideup/php:beta-8.1-fpm-alpineserversideup/php:beta-8.0-fpm-alpineserversideup/php:beta-7.4-fpm-alpine
fpm-apacheserversideup/php:beta-8.4-fpm-apache-alpineserversideup/php:beta-8.3-fpm-apache-alpineserversideup/php:beta-8.2-fpm-apache-alpineserversideup/php:beta-8.1-fpm-apache-alpineserversideup/php:beta-8.0-fpm-apache-alpineserversideup/php:beta-7.4-fpm-apache-alpine
fpm-nginxserversideup/php:beta-8.4-fpm-nginx-alpineserversideup/php:beta-8.3-fpm-nginx-alpineserversideup/php:beta-8.2-fpm-nginx-alpineserversideup/php:beta-8.1-fpm-nginx-alpineserversideup/php:beta-8.0-fpm-nginx-alpineserversideup/php:beta-7.4-fpm-nginx-alpine
unit⚠️ Unit does not have an Alpine version. See the known issue →

Running a test web server:

Sometimes you need to just run a test web server locally to see if your changes work. Below is a good example on how to quickly do this.

Example: Run a simple container for testing

docker run --rm -v $(pwd):/var/www/html -p 80:8080 -p 443:8443 serversideup/php:8.4-fpm-nginx

How PHP Versions are selected for distribution

We use the official PHP versions as our base image. To identify which versions should be built, we use a file called scripts/conf/php-versions-base-config.yml to explicitly select what versions should be built and any special rules/settings for each version (like base OS, default versions, etc).

We then use a scripts/get-php-versions.sh script to download the latest active releases from PHP and merge them into a final file called scripts/conf/php-versions.yml.

The php-versions.yml file will include all final versions for tagging and building.

We generate our tags with a file called scripts/assemble-docker-tags.sh which handles all the advanced logic of compiling our tags together.

All the scripts above are designed to run locally and in GitHub Actions. Feel free to execute these scripts to see the help menus and how they work.

GitHub Actions

We use GitHub Actions exclusively to publish all of our releases. If the image exists from DockerHub or GitHub Packages, it will never be published from a local machine.

See .github/workflows/action_publish-beta-images.yml for an example of how we publish our beta images.

NGINX Versions

We use the official NGINX repos to install the latest version of NGINX for each OS. The version to install is set by a build argument, which is loaded from the scripts/conf/php-versions-base-config.yml file.

To view the current NGINX versions, run the following command:

View NGINX versions

./scripts/get-nginx-versions.sh

This script will look at the official NGINX repos to find the latest version of NGINX for each OS. If you want to update the version, you can run the script with the --write flag.

Update NGINX versions

./scripts/get-nginx-versions.sh --write

NGINX repository key verification

  • Debian (APT): We import the official NGINX GPG key from https://nginx.org/keys/nginx_signing.key and verify it against a pinned fingerprint via the SIGNING_FINGERPRINT build arg.
  • Alpine (APK): APK uses a raw RSA public key (nginx_signing.rsa.pub). We verify this key by pinning the SHA‑256 of the DER‑encoded public key via the SIGNING_ALPINE_RSA_PUB_SHA256 build arg. You can provide multiple comma‑separated hashes to support key rotation.

Compute the Alpine key hash when updating:

curl -sS https://nginx.org/keys/nginx_signing.rsa.pub -o /tmp/nginx_signing.rsa.pub
# macOS
openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -outform DER 2>/dev/null | shasum -a 256 | awk '{print $1}'
# Linux
openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -outform DER 2>/dev/null | sha256sum | awk '{print $1}'

Then build with the new hash (optionally include the old hash during rotation):

docker build \
  --build-arg SIGNING_ALPINE_RSA_PUB_SHA256="<new-hash>,<old-hash>" \
  -f src/variations/fpm-nginx/Dockerfile .

Reference: Installing NGINX Open Source → Alpine packages.

Why allow multiple hashes? This is optional, but useful during a short rotation window:

  • Ensure CI builds across branches/runners succeed while the upstream key change propagates.
  • Avoid flakes from CDN/caching delays where some environments still see the old key.
  • Let you pre-stage the new value before the official switch, then remove the old afterwards.

If you control all builds centrally and can update quickly, pass a single hash.

Helping out

If you're really eager to help out, here are a few places to get started: