From 9caf66bc8df2f2319934acad36fcd36b7547b124 Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:17:20 -0500 Subject: [PATCH 1/7] fix(dockerfile): add retry logic for s6-overlay download with exponential backoff Adds 3-attempt retry loop with 5s/10s/20s exponential backoff for s6-overlay wget downloads. This addresses transient network timeouts (HTTP 28 timeout) that occur during Alpine builds, which is a common issue with external repository downloads during Docker builds. The retry logic: - Attempts download up to 3 times - Uses 5 * 2^(ATTEMPT-1) backoff (5s, 10s, 20s) - Cleans up partial downloads before retry - Provides clear logging of retry attempts - Fails fast with descriptive error if all attempts fail --- Dockerfile.v2 | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Dockerfile.v2 b/Dockerfile.v2 index b1e40ff..c743763 100644 --- a/Dockerfile.v2 +++ b/Dockerfile.v2 @@ -157,8 +157,22 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ *) S6_ARCH="x86_64" ;; \ esac && \ echo "Downloading s6-overlay ${S6_OVERLAY_VERSION} for ${S6_ARCH}" && \ - wget -O /tmp/s6-overlay-noarch.tar.xz https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz && \ - wget -O /tmp/s6-overlay-${S6_ARCH}.tar.xz https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz && \ + for ATTEMPT in 1 2 3; do \ + if wget -O /tmp/s6-overlay-noarch.tar.xz https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz && \ + wget -O /tmp/s6-overlay-${S6_ARCH}.tar.xz https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz; then \ + break; \ + else \ + if [ $ATTEMPT -lt 3 ]; then \ + SLEEP_TIME=$((5 * 2 ** (ATTEMPT - 1))); \ + echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ + sleep $SLEEP_TIME; \ + rm -f /tmp/s6-overlay-*.tar.xz; \ + else \ + echo "Failed to download s6-overlay after 3 attempts"; \ + exit 1; \ + fi; \ + fi; \ + done && \ tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz && \ tar -C / -Jxpf /tmp/s6-overlay-${S6_ARCH}.tar.xz && \ rm /tmp/s6-overlay-noarch.tar.xz /tmp/s6-overlay-${S6_ARCH}.tar.xz && \ From b6938e4cd751edc7ca80dcabbdf9c66ed290ede2 Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:23:42 -0500 Subject: [PATCH 2/7] fix(dockerfile): add retry logic for install-php-extensions download in v1 Adds 3-attempt retry loop with 5s/10s/20s exponential backoff for: 1. install-php-extensions script download from GitHub 2. PHP extension compilation and installation This addresses transient network timeouts that occur on both Alpine and Debian builds. The retry logic handles: - Network timeouts during curl download - Compilation/installation failures due to transient issues - Clear logging of retry attempts and failures Each layer has independent retry loops for robustness. --- Dockerfile.v1 | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/Dockerfile.v1 b/Dockerfile.v1 index 682fbca..e10c6bb 100644 --- a/Dockerfile.v1 +++ b/Dockerfile.v1 @@ -24,12 +24,39 @@ RUN if [ "$BASEOS" = "bullseye" ]; then \ apk add --no-cache curl git zip unzip ghostscript imagemagick optipng gifsicle pngcrush jpegoptim libjpeg-turbo libjpeg-turbo-utils pngquant libwebp-tools; \ fi -# Add all needed PHP extensions -RUN curl -sSLf -o /usr/local/bin/install-php-extensions \ - https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \ - chmod +x /usr/local/bin/install-php-extensions && \ - install-php-extensions amqp bcmath bz2 calendar ctype exif intl imagick imap json mbstring ldap mcrypt memcached mongodb \ - mysqli opcache pdo_mysql pdo_pgsql pgsql redis snmp soap sockets tidy timezonedb uuid vips xsl yaml zip zstd @composer +# Add all needed PHP extensions with retry logic for transient network failures +RUN for ATTEMPT in 1 2 3; do \ + if curl -sSLf -o /usr/local/bin/install-php-extensions \ + https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions; then \ + break; \ + else \ + if [ $ATTEMPT -lt 3 ]; then \ + SLEEP_TIME=$((5 * 2 ** (ATTEMPT - 1))); \ + echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ + sleep $SLEEP_TIME; \ + rm -f /usr/local/bin/install-php-extensions; \ + else \ + echo "Failed to download install-php-extensions after 3 attempts"; \ + exit 1; \ + fi; \ + fi; \ + done && \ + chmod +x /usr/local/bin/install-php-extensions && \ + for ATTEMPT in 1 2 3; do \ + if install-php-extensions amqp bcmath bz2 calendar ctype exif intl imagick imap json mbstring ldap mcrypt memcached mongodb \ + mysqli opcache pdo_mysql pdo_pgsql pgsql redis snmp soap sockets tidy timezonedb uuid vips xsl yaml zip zstd @composer; then \ + break; \ + else \ + if [ $ATTEMPT -lt 3 ]; then \ + SLEEP_TIME=$((5 * 2 ** (ATTEMPT - 1))); \ + echo "Extension installation attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ + sleep $SLEEP_TIME; \ + else \ + echo "Failed to install PHP extensions after 3 attempts"; \ + exit 1; \ + fi; \ + fi; \ + done # Enable Apache rewrite mod, if applicable RUN if command -v a2enmod; then a2enmod rewrite; fi From 179f4989b7ca9044afa7bfd4174f665300d29d1c Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:36:45 -0500 Subject: [PATCH 3/7] fix(dockerfile): use POSIX-compliant arithmetic for retry backoff Replace bash-specific exponentiation (5 * 2 ** (ATTEMPT - 1)) with POSIX sh-compatible case statement for hardcoded backoff values: - ATTEMPT 1: 5 seconds - ATTEMPT 2: 10 seconds - ATTEMPT 3: 20 seconds This fixes 'arithmetic expression: expecting primary' errors in Alpine and Debian builds which use /bin/sh instead of bash. Applies to both: - Dockerfile.v1: install-php-extensions download and installation - Dockerfile.v2: s6-overlay download --- Dockerfile.v1 | 12 ++++++++++-- Dockerfile.v2 | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Dockerfile.v1 b/Dockerfile.v1 index e10c6bb..e6ba17e 100644 --- a/Dockerfile.v1 +++ b/Dockerfile.v1 @@ -31,7 +31,11 @@ RUN for ATTEMPT in 1 2 3; do \ break; \ else \ if [ $ATTEMPT -lt 3 ]; then \ - SLEEP_TIME=$((5 * 2 ** (ATTEMPT - 1))); \ + case $ATTEMPT in \ + 1) SLEEP_TIME=5 ;; \ + 2) SLEEP_TIME=10 ;; \ + 3) SLEEP_TIME=20 ;; \ + esac; \ echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ rm -f /usr/local/bin/install-php-extensions; \ @@ -48,7 +52,11 @@ RUN for ATTEMPT in 1 2 3; do \ break; \ else \ if [ $ATTEMPT -lt 3 ]; then \ - SLEEP_TIME=$((5 * 2 ** (ATTEMPT - 1))); \ + case $ATTEMPT in \ + 1) SLEEP_TIME=5 ;; \ + 2) SLEEP_TIME=10 ;; \ + 3) SLEEP_TIME=20 ;; \ + esac; \ echo "Extension installation attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ else \ diff --git a/Dockerfile.v2 b/Dockerfile.v2 index c743763..dcf65ba 100644 --- a/Dockerfile.v2 +++ b/Dockerfile.v2 @@ -163,7 +163,11 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ break; \ else \ if [ $ATTEMPT -lt 3 ]; then \ - SLEEP_TIME=$((5 * 2 ** (ATTEMPT - 1))); \ + case $ATTEMPT in \ + 1) SLEEP_TIME=5 ;; \ + 2) SLEEP_TIME=10 ;; \ + 3) SLEEP_TIME=20 ;; \ + esac; \ echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ rm -f /tmp/s6-overlay-*.tar.xz; \ From 45c28fba97bb4c5cee5b7a7c0cee92ed9ea7fc5b Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:40:27 -0500 Subject: [PATCH 4/7] fix(dockerfile): switch v1 to ECR to avoid Docker Hub rate limits Change base image from docker.io/library/php to public.ecr.aws/docker/library/php to avoid Docker Hub's unauthenticated pull rate limits (100 pulls per 6 hours). AWS ECR Public Gallery has no rate limits for public images and is a reliable mirror for Docker Hub's official images. This matches the approach already used in Dockerfile.v2. Fixes: 'toomanyrequests: You have reached your unauthenticated pull rate limit' --- Dockerfile.v1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.v1 b/Dockerfile.v1 index e6ba17e..20551f2 100644 --- a/Dockerfile.v1 +++ b/Dockerfile.v1 @@ -1,5 +1,5 @@ ARG VERSION=8.3-cli-alpine -FROM php:${VERSION} +FROM public.ecr.aws/docker/library/php:${VERSION} ARG PHPVERSION ARG BASEOS From 55257f672b36b95b88dadc6b8e950f652d8008b7 Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Wed, 25 Mar 2026 23:58:42 -0400 Subject: [PATCH 5/7] refactor: clean up Dockerfiles, CI workflow, and s6-overlay scripts - Delete legacy docker-image.v1.yml, docker-image.v2.yml workflows and unused docker-setup composite action - Dockerfile.v1: drop dead bullseye support, remove commented-out mcrypt/imagick block - Dockerfile.v2: remove duplicate imagemagick in alpine, add retry for install-php-extensions download, add dev package cleanup after builds (trixie/bookworm/alpine) to reduce image size - s6-overlay: simplify finish script for v3 (use exit code instead of manual s6-svscanctl), remove stale comments - CI: replace arc-s2-runner with ubuntu-latest, add apache to build-and-test matrix with smoke test --- .github/docker-setup/action.yml | 18 -- .github/workflows/docker-ci.yml | 34 +- .github/workflows/docker-image.v1.yml | 112 ------- .github/workflows/docker-image.v2.yml | 437 -------------------------- Dockerfile.v1 | 20 +- Dockerfile.v2 | 79 ++++- s6-overlay/cont-init.d/10-php-config | 1 - s6-overlay/services.d/php/finish | 10 +- s6-overlay/services.d/php/run | 1 - 9 files changed, 105 insertions(+), 607 deletions(-) delete mode 100644 .github/docker-setup/action.yml delete mode 100644 .github/workflows/docker-image.v1.yml delete mode 100644 .github/workflows/docker-image.v2.yml diff --git a/.github/docker-setup/action.yml b/.github/docker-setup/action.yml deleted file mode 100644 index 4b57014..0000000 --- a/.github/docker-setup/action.yml +++ /dev/null @@ -1,18 +0,0 @@ -# .github/actions/docker-setup/action.yml -name: 'Docker Setup' -description: 'Sets up Docker for multi-platform builds' - -runs: - using: "composite" - steps: - - name: Docker Setup QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: amd64,arm64,arm - - - name: Docker Setup Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - network=host - image=moby/buildkit:latest \ No newline at end of file diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index 82a3ee9..5124ebf 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -31,23 +31,20 @@ concurrency: jobs: build-and-test: - # runs-on: ubuntu-latest - runs-on: arc-s2-runner + runs-on: ubuntu-latest strategy: fail-fast: false matrix: variant: [v1, v2] php-version: ['8.4', '8.3', '8.2'] - php-type: [fpm, cli] + php-type: [fpm, cli, apache] php-base: [alpine, bookworm] exclude: - php-type: apache php-base: alpine - # v2 uses trixie as the Debian base; bookworm retained for v1 - variant: v2 php-base: bookworm include: - # v2 builds on trixie for Debian images - variant: v2 php-version: '8.4' php-type: fpm @@ -56,6 +53,10 @@ jobs: php-version: '8.4' php-type: cli php-base: trixie + - variant: v2 + php-version: '8.4' + php-type: apache + php-base: trixie - variant: v2 php-version: '8.3' php-type: fpm @@ -64,6 +65,10 @@ jobs: php-version: '8.3' php-type: cli php-base: trixie + - variant: v2 + php-version: '8.3' + php-type: apache + php-base: trixie - variant: v2 php-version: '8.2' php-type: fpm @@ -72,6 +77,10 @@ jobs: php-version: '8.2' php-type: cli php-base: trixie + - variant: v2 + php-version: '8.2' + php-type: apache + php-base: trixie name: ${{ matrix.variant }}-${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base }} @@ -269,6 +278,18 @@ jobs: echo "✅ PHP-FPM version check passed" echo "::endgroup::" + - name: Smoke tests - Apache specific + if: matrix.php-type == 'apache' + run: | + echo "::group::Testing Apache" + if ! docker run --rm test-${{ steps.vars.outputs.TAG }} apache2 -v 2>&1 | tee apache-version.txt; then + echo "::error::Failed to run apache2 -v" + cat apache-version.txt || true + exit 1 + fi + echo "✅ Apache version check passed" + echo "::endgroup::" + - name: Summary run: | echo "::notice::✅ Build and tests passed for ${{ matrix.variant }} - ${{ steps.vars.outputs.TAG }}" @@ -276,8 +297,7 @@ jobs: publish: needs: build-and-test if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'schedule') - # runs-on: ubuntu-latest - runs-on: arc-s2-runner + runs-on: ubuntu-latest strategy: fail-fast: false matrix: diff --git a/.github/workflows/docker-image.v1.yml b/.github/workflows/docker-image.v1.yml deleted file mode 100644 index 1d6c62a..0000000 --- a/.github/workflows/docker-image.v1.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: Docker Image CI - -on: - workflow_dispatch: - # schedule: - # - cron: '31 16 * * 3' - -jobs: - build: - name: ${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php-type: [cli, fpm, apache] - php-version: ['8.4', '8.3', '8.2'] - php-base-os: [bookworm, alpine] - exclude: - - php-type: apache - php-base-os: alpine - - steps: - - name: Checkout source - uses: actions/checkout@v4 - - - name: Docker Setup QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: amd64,arm64,arm - - - name: Docker Setup Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub registry - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Log into ghcr.io registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to Quay.io registry - uses: docker/login-action@v3 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} - - # - name: Build Docker image for testing - # uses: docker/build-push-action@v6 - # with: - # context: . - # load: true - # cache-from: type=gha - # cache-to: type=gha,mode=max - # build-args: | - # VERSION=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - # PHPVERSION=${{ matrix.php-version }} - # BASEOS=${{ matrix.php-base-os }} - # tags: | - # ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - - # - name: Test PHP - # if: matrix.php-type != 'apache' - # run: | - # docker run --rm ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} -v - # docker run --rm ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} -m - - # - name: Test Apache - # if: matrix.php-type == 'apache' - # run: | - # docker run --rm ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} -v - # docker run --rm ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} -l - - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64,linux/arm/v7 - push: true - cache-from: type=gha,scope=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - cache-to: type=gha,mode=max,scope=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - # build-contexts: | - # ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}=docker-image://${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - build-args: | - VERSION=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - PHPVERSION=${{ matrix.php-version }} - BASEOS=${{ matrix.php-base-os }} - tags: | - docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - ghcr.io/kingpin/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - quay.io/kingpinx1/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@master - with: - scan-type: image - image-ref: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - format: 'sarif' - severity: 'CRITICAL,HIGH' - hide-progress: false - output: 'trivy-results-${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}.sarif' - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: 'trivy-results-${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}.sarif' \ No newline at end of file diff --git a/.github/workflows/docker-image.v2.yml b/.github/workflows/docker-image.v2.yml deleted file mode 100644 index 3296fc7..0000000 --- a/.github/workflows/docker-image.v2.yml +++ /dev/null @@ -1,437 +0,0 @@ -name: PHP Docker Build Pipeline - -on: - workflow_dispatch: - # schedule: - # - cron: '31 16 * * 3' - # push: - # branches: - # - main - # paths: - # - 'Dockerfile' - # - '.github/workflows/docker-image.yml' - # - 's6-overlay/**' - # - '!README.md' - # - '!*.md' - # - '!docs/**' - # pull_request: - # paths: - # - 'Dockerfile' - # - '.github/workflows/docker-image.yml' - # - 's6-overlay/**' - # - '!README.md' - # - '!*.md' - # - '!docs/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - matrix-prep: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - name: Generate build matrix - id: set-matrix - run: | - # Using the heredoc delimiter syntax for multiline GitHub Actions outputs - echo "matrix<> $GITHUB_OUTPUT - echo '{ - "php-version": ["8.1", "8.2", "8.3", "8.4"], - "php-type": ["cli", "fpm", "apache"], - "php-base-os": ["trixie", "alpine"], - "exclude": [ - {"php-type": "apache", "php-base-os": "alpine"} - ] - }' >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - build-test: - needs: matrix-prep - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: ${{fromJson(needs.matrix-prep.outputs.matrix)}} - outputs: - image-tags: ${{ steps.save-tags.outputs.tags }} - - steps: - - name: Record start time - id: start_time - run: | - echo "time=$(date +%s)" >> $GITHUB_OUTPUT - - - name: Checkout source - uses: actions/checkout@v4 - - - name: Setup Docker environment - uses: docker/setup-qemu-action@v3 - with: - platforms: amd64,arm64,arm - - - name: Setup Docker Buildx with Resource Limits - uses: docker/setup-buildx-action@v3 - with: - buildkitd-flags: | - --debug - --allow-insecure-entitlement security.insecure - --allow-insecure-entitlement network.host - buildkitd-config-inline: | - [worker.oci] - max-parallelism = 4 - - [worker.containerd] - platforms = ["linux/amd64", "linux/arm64", "linux/arm/v7"] - - # [worker.oci.gcpolicy] - # keepBytes = 10000000000 - # keepDuration = 604800 - - - name: Login to DockerHub registry - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Log into ghcr.io registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to Quay.io registry - uses: docker/login-action@v3 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} - - - name: Set image details - id: image-details - run: | - # Generate consistent image tags/names across the workflow - echo "IMAGE_NAME=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}" >> $GITHUB_ENV - echo "DOCKERHUB_TAG=docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}" >> $GITHUB_ENV - echo "GHCR_TAG=ghcr.io/kingpin/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}" >> $GITHUB_ENV - echo "QUAY_TAG=quay.io/kingpinx1/${{ github.event.repository.name }}:${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}" >> $GITHUB_ENV - - - name: Set smart tags - id: smart-tags - run: | - echo "::group::Setting smart version tags" - # Add major.minor tags - MAJOR_VERSION=$(echo "${{ matrix.php-version }}" | cut -d. -f1) - MINOR_VERSION=$(echo "${{ matrix.php-version }}" | cut -d. -f1,2) - - # Set tags for each registry - if [[ "${{ matrix.php-version }}" == "8.4" && "${{ matrix.php-type }}" == "cli" && "${{ matrix.php-base-os }}" == "alpine" ]]; then - echo "DOCKERHUB_ADDITIONAL_TAGS=docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest,docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${MAJOR_VERSION},docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${MINOR_VERSION}" >> $GITHUB_ENV - echo "GHCR_ADDITIONAL_TAGS=ghcr.io/kingpin/${{ github.event.repository.name }}:latest,ghcr.io/kingpin/${{ github.event.repository.name }}:${MAJOR_VERSION},ghcr.io/kingpin/${{ github.event.repository.name }}:${MINOR_VERSION}" >> $GITHUB_ENV - echo "QUAY_ADDITIONAL_TAGS=quay.io/kingpinx1/${{ github.event.repository.name }}:latest,quay.io/kingpinx1/${{ github.event.repository.name }}:${MAJOR_VERSION},quay.io/kingpinx1/${{ github.event.repository.name }}:${MINOR_VERSION}" >> $GITHUB_ENV - else - # Regular version tags - ADDITIONAL_TAGS="${MAJOR_VERSION}-${{ matrix.php-type }}-${{ matrix.php-base-os }},${MINOR_VERSION}-${{ matrix.php-type }}-${{ matrix.php-base-os }}" - - # Add bookworm compatibility tags for trixie images (backward compatibility) - # Also add bullseye tags for transition compatibility - if [[ "${{ matrix.php-base-os }}" == "trixie" ]]; then - ADDITIONAL_TAGS="${ADDITIONAL_TAGS},${{ matrix.php-version }}-${{ matrix.php-type }}-bookworm,${MAJOR_VERSION}-${{ matrix.php-type }}-bookworm,${MINOR_VERSION}-${{ matrix.php-type }}-bookworm" - ADDITIONAL_TAGS="${ADDITIONAL_TAGS},${{ matrix.php-version }}-${{ matrix.php-type }}-bullseye,${MAJOR_VERSION}-${{ matrix.php-type }}-bullseye,${MINOR_VERSION}-${{ matrix.php-type }}-bullseye" - fi - - echo "DOCKERHUB_ADDITIONAL_TAGS=docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${ADDITIONAL_TAGS//,/,docker.io\/${{ secrets.DOCKERHUB_USERNAME }}\/${{ github.event.repository.name }}:}" >> $GITHUB_ENV - echo "GHCR_ADDITIONAL_TAGS=ghcr.io/kingpin/${{ github.event.repository.name }}:${ADDITIONAL_TAGS//,/,ghcr.io\/kingpin\/${{ github.event.repository.name }}:}" >> $GITHUB_ENV - echo "QUAY_ADDITIONAL_TAGS=quay.io/kingpinx1/${{ github.event.repository.name }}:${ADDITIONAL_TAGS//,/,quay.io\/kingpinx1\/${{ github.event.repository.name }}:}" >> $GITHUB_ENV - fi - echo "::endgroup::" - - - name: Set lowercase owner name - run: | - echo "OWNER_LC=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV - - - name: Build test image - uses: docker/build-push-action@v6 - with: - context: . - load: true - platforms: linux/amd64 - cache-from: | - type=gha,scope=test-${{ env.IMAGE_NAME }} - type=registry,ref=ttl.sh/${{ env.OWNER_LC }}-test-cache:${{ env.IMAGE_NAME }} - cache-to: | - type=gha,mode=max,scope=test-${{ env.IMAGE_NAME }} - build-args: | - VERSION=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - PHPVERSION=${{ matrix.php-version }} - BASEOS=${{ matrix.php-base-os }} - tags: | - test-${{ env.IMAGE_NAME }} - - - name: Run validation tests - run: | - echo "::group::Testing image ${{ env.IMAGE_NAME }}" - - # Version validation - echo "Verifying PHP version..." - docker run --rm test-${{ env.IMAGE_NAME }} php -v | grep -q "${{ matrix.php-version }}" || { echo "::error::PHP version mismatch"; exit 1; } - - # Extension validation - echo "Checking extensions..." - docker run --rm test-${{ env.IMAGE_NAME }} php -m > extensions.txt - - # Check for critical extensions - for ext in imagick gd json mysqli zip; do - grep -q "$ext" extensions.txt || { echo "::error::Missing extension: $ext"; exit 1; } - done - - # Additional health checks - echo "Running health checks..." - docker run --rm test-${{ env.IMAGE_NAME }} php -r "echo 'PHP is working correctly\n';" - - echo "::endgroup::" - - - name: Run comprehensive tests - run: | - echo "::group::Advanced Testing Suite" - - # Test PHP modules functionality - docker run --rm test-${{ env.IMAGE_NAME }} php -r " - // Test GD functionality - \$img = imagecreatetruecolor(100, 100); - echo imagecolorallocate(\$img, 255, 0, 0) !== false ? 'GD working ✅\n' : 'GD failed ❌\n'; - imagedestroy(\$img); - - // Test MySQL connectivity - if (function_exists('mysqli_connect')) { - echo 'MySQLi extension loaded ✅\n'; - } - - // Test Zip functionality - if (class_exists('ZipArchive')) { - echo 'Zip extension loaded ✅\n'; - } - " - - echo "::endgroup::" - - - name: Check for changes - id: check-changes - run: | - echo "::group::Checking for image changes" - - # Try to pull the previous image - if docker pull ${{ env.DOCKERHUB_TAG }} &>/dev/null; then - # Compare image digest with current Dockerfile hash - PREVIOUS_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ${{ env.DOCKERHUB_TAG }} | cut -d'@' -f2) - CURRENT_HASH=$(sha256sum Dockerfile | cut -d' ' -f1) - - echo "Previous digest: $PREVIOUS_DIGEST" - echo "Current hash: $CURRENT_HASH" - - # Skip if unchanged and not manually triggered - if [[ "$PREVIOUS_DIGEST" == "$CURRENT_HASH" && "${{ github.event_name }}" != "workflow_dispatch" ]]; then - echo "No changes detected, skipping build" - echo "skip=true" >> $GITHUB_OUTPUT - else - echo "Changes detected, will build" - echo "skip=false" >> $GITHUB_OUTPUT - fi - else - echo "No previous image found, will build" - echo "skip=false" >> $GITHUB_OUTPUT - fi - - echo "::endgroup::" - - - name: Build and push Docker image - if: steps.check-changes.outputs.skip != 'true' - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64,linux/arm/v7 - push: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/main' }} - cache-from: | - type=gha,scope=${{ env.IMAGE_NAME }} - type=registry,ref=ttl.sh/${{ env.OWNER_LC }}-cache:${{ env.IMAGE_NAME }} - cache-to: | - type=gha,mode=max,scope=${{ env.IMAGE_NAME }} - type=registry,ref=ttl.sh/${{ env.OWNER_LC }}-cache:${{ env.IMAGE_NAME }},mode=max - build-args: | - VERSION=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - PHPVERSION=${{ matrix.php-version }} - BASEOS=${{ matrix.php-base-os }} - tags: | - ${{ env.DOCKERHUB_TAG }} - ${{ env.GHCR_TAG }} - ${{ env.QUAY_TAG }} - ${{ env.DOCKERHUB_ADDITIONAL_TAGS }} - ${{ env.GHCR_ADDITIONAL_TAGS }} - ${{ env.QUAY_ADDITIONAL_TAGS }} - - # PR preview image build disabled because workflow now runs only via workflow_dispatch - # - name: Build PR preview image - # if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository - # uses: docker/build-push-action@v6 - # with: - # context: . - # push: true - # platforms: linux/amd64 - # build-args: | - # VERSION=${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }} - # PHPVERSION=${{ matrix.php-version }} - # BASEOS=${{ matrix.php-base-os }} - # tags: | - # ghcr.io/kingpin/${{ github.event.repository.name }}:pr-${{ github.event.pull_request.number }}-${{ env.IMAGE_NAME }} - - - name: Create and push manifest - if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && steps.check-changes.outputs.skip != 'true' }} - run: | - echo "::group::Creating multi-architecture manifests" - # Multi-architecture images are automatically created by docker/build-push-action - # This step is only needed for the additional tag manifests - - if [[ -n "$DOCKERHUB_ADDITIONAL_TAGS" ]]; then - for TAG in $(echo $DOCKERHUB_ADDITIONAL_TAGS | tr ',' ' '); do - docker buildx imagetools create -t $TAG $DOCKERHUB_TAG - done - fi - - if [[ -n "$GHCR_ADDITIONAL_TAGS" ]]; then - for TAG in $(echo $GHCR_ADDITIONAL_TAGS | tr ',' ' '); do - docker buildx imagetools create -t $TAG $GHCR_TAG - done - fi - - if [[ -n "$QUAY_ADDITIONAL_TAGS" ]]; then - for TAG in $(echo $QUAY_ADDITIONAL_TAGS | tr ',' ' '); do - docker buildx imagetools create -t $TAG $QUAY_TAG - done - fi - echo "::endgroup::" - - - name: Save image tags for scan job - id: save-tags - if: steps.check-changes.outputs.skip != 'true' && github.ref == 'refs/heads/main' - run: | - # Save image tags for scan job in JSON format - echo "tags={\"dockerhub\":\"${{ env.DOCKERHUB_TAG }}\",\"ghcr\":\"${{ env.GHCR_TAG }}\",\"quay\":\"${{ env.QUAY_TAG }}\",\"image_name\":\"${{ env.IMAGE_NAME }}\"}" >> $GITHUB_OUTPUT - - - name: Timing Report - if: always() - run: | - echo "::group::Build Performance Metrics" - # Calculate and store build times - END_TIME=$(date +%s) - BUILD_TIME=$((END_TIME - ${{ steps.start_time.outputs.time }})) - echo "Total build time: $(($BUILD_TIME / 60)) minutes and $(($BUILD_TIME % 60)) seconds" - - # Store metrics for tracking - echo "${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }},$BUILD_TIME" >> build-metrics.csv - echo "::endgroup::" - - - name: Upload build metrics - if: always() - uses: actions/upload-artifact@v4 - with: - name: build-metrics-${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base-os }}-${{ github.run_id }} - path: build-metrics.csv - retention-days: 90 - - scan: - needs: build-test - if: ${{ needs.build-test.outputs.image-tags != '' && github.ref == 'refs/heads/main' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - registry: dockerhub - name: DockerHub - - registry: ghcr - name: GitHub Container Registry - - steps: - - name: Parse image details - id: image-details - run: | - # Extract the image tag from the JSON output - IMAGE_TAGS='${{ needs.build-test.outputs.image-tags }}' - REGISTRY="${{ matrix.registry }}" - - # Parse JSON to get the specific registry tag - IMAGE_TAG=$(echo $IMAGE_TAGS | jq -r ".$REGISTRY") - IMAGE_NAME=$(echo $IMAGE_TAGS | jq -r ".image_name") - - echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV - - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@master - with: - scan-type: image - image-ref: ${{ env.IMAGE_TAG }} - format: 'sarif' - severity: 'CRITICAL,HIGH' - hide-progress: false - output: 'trivy-results-${{ matrix.registry }}-${{ env.IMAGE_NAME }}.sarif' - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: 'trivy-results-${{ matrix.registry }}-${{ env.IMAGE_NAME }}.sarif' - category: ${{ matrix.name }} - - publish: - needs: [build-test, scan] - if: ${{ needs.build-test.outputs.image-tags != '' }} - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Update README - run: | - echo "::group::Updating image metadata" - - # Update project documentation with latest build info - DATE=$(date '+%Y-%m-%d') - echo "Last updated: $DATE" >> README.md - echo "Successfully built and published images for PHP Docker project" >> README.md - - echo "::endgroup::" - - - name: Notify on completion - if: always() - run: | - echo "::group::Build status notification" - - # Send notification about build status - if [ "${{ job.status }}" == "success" ]; then - echo "Build completed successfully" - else - echo "Build failed" - fi - - echo "::endgroup::" - - combine-metrics: - needs: build-test - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@v4 - with: - pattern: build-metrics-* - merge-multiple: true - path: all-metrics - - - name: Combine metrics files - run: | - cat all-metrics/*.csv > combined-metrics.csv - - - uses: actions/upload-artifact@v4 - with: - name: combined-build-metrics - path: combined-metrics.csv diff --git a/Dockerfile.v1 b/Dockerfile.v1 index 20551f2..228a820 100644 --- a/Dockerfile.v1 +++ b/Dockerfile.v1 @@ -7,13 +7,7 @@ ARG BASEOS ENV DEBIAN_FRONTEND=noninteractive # Install dependencies based on the base OS -RUN if [ "$BASEOS" = "bullseye" ]; then \ - echo 'deb http://deb.debian.org/debian bullseye-backports main' > /etc/apt/sources.list.d/bullseye-backports.list && \ - apt-get update && \ - apt-get -y upgrade && \ - apt-get install -y --no-install-recommends curl git zip unzip ghostscript imagemagick optipng gifsicle pngcrush jpegoptim libjpeg-turbo-progs pngquant webp && \ - rm -rf /var/lib/apt/lists/*; \ - elif [ "$BASEOS" = "bookworm" ]; then \ +RUN if [ "$BASEOS" = "bookworm" ]; then \ echo 'deb http://deb.debian.org/debian bookworm main' > /etc/apt/sources.list && \ apt-get update && \ apt-get -y upgrade && \ @@ -69,18 +63,6 @@ RUN for ATTEMPT in 1 2 3; do \ # Enable Apache rewrite mod, if applicable RUN if command -v a2enmod; then a2enmod rewrite; fi -# # Add mcrypt and imagick based on PHP version -# RUN case "$PHPVERSION" in \ -# 7|8.0|8.1|8.2) \ -# install-php-extensions imagick; \ -# ;; \ -# 8.3) \ -# install-php-extensions imagick/imagick@master; \ -# ;; \ -# *) \ -# ;; \ -# esac - # Disable AV1 only in armv7 RUN case $(uname -m) in \ x86_64|aarch64) \ diff --git a/Dockerfile.v2 b/Dockerfile.v2 index dcf65ba..4d3e498 100644 --- a/Dockerfile.v2 +++ b/Dockerfile.v2 @@ -111,7 +111,6 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ libjpeg-turbo libjpeg-turbo-utils pngquant libwebp-tools \ rabbitmq-c rabbitmq-c-dev \ libpng libpng-dev \ - imagemagick \ imagemagick-dev \ c-client \ snappy snappy-dev \ @@ -129,9 +128,27 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ libuuid util-linux-dev \ libxpm libxpm-dev; \ fi && \ - # Download and install PHP extension installer - curl -sSLf -o /usr/local/bin/install-php-extensions \ - https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \ + # Download PHP extension installer with retry + for ATTEMPT in 1 2 3; do \ + if curl -sSLf -o /usr/local/bin/install-php-extensions \ + https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions; then \ + break; \ + else \ + if [ $ATTEMPT -lt 3 ]; then \ + case $ATTEMPT in \ + 1) SLEEP_TIME=5 ;; \ + 2) SLEEP_TIME=10 ;; \ + 3) SLEEP_TIME=20 ;; \ + esac; \ + echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ + sleep $SLEEP_TIME; \ + rm -f /usr/local/bin/install-php-extensions; \ + else \ + echo "Failed to download install-php-extensions after 3 attempts"; \ + exit 1; \ + fi; \ + fi; \ + done && \ chmod +x /usr/local/bin/install-php-extensions && \ # Install PHP extensions install-php-extensions \ @@ -180,6 +197,60 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz && \ tar -C / -Jxpf /tmp/s6-overlay-${S6_ARCH}.tar.xz && \ rm /tmp/s6-overlay-noarch.tar.xz /tmp/s6-overlay-${S6_ARCH}.tar.xz && \ + # Clean up build dependencies to reduce image size + if [ "$BASEOS" = "trixie" ]; then \ + apt-mark manual \ + curl git zip unzip wget ca-certificates xz-utils \ + ghostscript imagemagick optipng gifsicle pngcrush jpegoptim \ + libjpeg-turbo-progs pngquant webp \ + libmemcachedutil2t64 libmemcached11t64 \ + libmagickcore-7.q16-10 libmagickwand-7.q16-10 \ + libvips42t64 libpng16-16t64 libavif16 \ + libsnmp40t64 librabbitmq4 libpq5 \ + libssl3t64 libzip5 libicu76 \ + libldap2 libtidy58 libxslt1.1 \ + libyaml-0-2 libuuid1 libsnappy1v5 \ + libwebp7 libxpm4 && \ + apt-get purge -y --auto-remove \ + build-essential equivs \ + libmemcached-dev libmagickwand-dev libvips-dev \ + libpng-dev libavif-dev libsnmp-dev \ + librabbitmq-dev libpq-dev libssl-dev \ + libzip-dev libbz2-dev libicu-dev \ + libldap2-dev libtidy-dev libxslt1-dev \ + libyaml-dev uuid-dev libzstd-dev libxpm-dev && \ + rm -rf /var/lib/apt/lists/*; \ + elif [ "$BASEOS" = "bookworm" ]; then \ + apt-mark manual \ + curl git zip unzip wget ca-certificates xz-utils \ + ghostscript imagemagick optipng gifsicle pngcrush jpegoptim \ + libjpeg-turbo-progs pngquant webp \ + libmemcachedutil2 libmemcached11 \ + libmagickcore-6.q16-6 libmagickwand-6.q16-6 \ + libvips42 libpng16-16 libavif15 \ + libsnmp40 librabbitmq4 libpq5 \ + libssl3 libzip4 \ + libldap-2.5-0 libtidy5deb1 libxslt1.1 \ + libyaml-0-2 libuuid1 libxpm4 && \ + apt-get purge -y --auto-remove \ + build-essential \ + libmemcached-dev libmagickwand-dev libvips-dev \ + libpng-dev libavif-dev libsnmp-dev \ + librabbitmq-dev libpq-dev libssl-dev \ + libzip-dev libbz2-dev libicu-dev \ + libldap2-dev libtidy-dev libxslt1-dev \ + libyaml-dev uuid-dev libzstd-dev libxpm-dev && \ + rm -rf /var/lib/apt/lists/*; \ + elif [ "$BASEOS" = "alpine" ]; then \ + apk del \ + build-base \ + rabbitmq-c-dev libpng-dev imagemagick-dev \ + snappy-dev postgresql-dev net-snmp-dev \ + tidyhtml-dev vips-dev libxslt-dev \ + yaml-dev libzip-dev libmemcached-dev \ + libavif-dev icu-dev openldap-dev \ + util-linux-dev libxpm-dev; \ + fi && \ test -f /init || (echo "ERROR: /init not found after s6-overlay installation" && exit 1) # Set useful PHP environment variables with defaults diff --git a/s6-overlay/cont-init.d/10-php-config b/s6-overlay/cont-init.d/10-php-config index 6b602de..afddb6f 100644 --- a/s6-overlay/cont-init.d/10-php-config +++ b/s6-overlay/cont-init.d/10-php-config @@ -1,5 +1,4 @@ #!/bin/sh -# Note: changed from bash to sh as it's more universally available in container images echo "Configuring PHP settings from environment variables..." diff --git a/s6-overlay/services.d/php/finish b/s6-overlay/services.d/php/finish index 9ec9eed..2de6b09 100644 --- a/s6-overlay/services.d/php/finish +++ b/s6-overlay/services.d/php/finish @@ -1,10 +1,4 @@ -#!/usr/bin/with-contenv bash +#!/bin/sh -# Handle service termination echo "PHP service terminated with exit code $1" - -# We only want to shut down the container if this service exits unexpectedly -if [ "$1" -ne 0 ]; then - echo "PHP service failed, shutting down container" - s6-svscanctl -t /var/run/s6/services -fi +exit "$1" diff --git a/s6-overlay/services.d/php/run b/s6-overlay/services.d/php/run index 4e08605..9538656 100644 --- a/s6-overlay/services.d/php/run +++ b/s6-overlay/services.d/php/run @@ -1,5 +1,4 @@ #!/bin/sh -# Note: Changed from bash to sh for better compatibility # This script determines how to start the PHP service based on the image type From b54e1ada3f99ca6ac5d006e6bc7564b476d582cf Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Thu, 26 Mar 2026 00:03:26 -0400 Subject: [PATCH 6/7] docs(readme): fix inaccuracies and remove stale content - Clarify env vars are v2 only (s6 init applies them) - Fix security section: appuser is v2 only, v1 runs as root - Update architecture diagram: remove bullseye, show v1/v2 split - Fix stale bullseye redirect note with accurate v1/v2 base OS info - Remove BuildKit as a pro/con (it's now the Docker default) - Fix docker-compose to Docker Compose - Note dev dependency cleanup in image sizes - Replace inline license with reference to LICENSE file --- README.md | 134 ++++++++++++++++++++++-------------------------------- 1 file changed, 55 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 293dd83..3b2b9b1 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ See [v1 vs v2 comparison](#v1-vs-v2-comparison) below for details. > **ℹ️ Base OS Update (v2 only)**: v2 Debian images now use **Debian Trixie** as the base OS (following upstream PHP official images). For backward compatibility, `:bookworm` tags continue to work and point to the same Trixie-built images. v1 images remain on Bookworm. [See migration notes](docs/migration.md#debian-trixie-migration-v2-only) for details. -## Environment Variables +## Environment Variables (v2 only) -The following environment variables can be overridden when running containers: +The following environment variables are applied at container startup by the s6-overlay init script in **v2 images only**. v1 images do not process these variables. ### Memory Settings @@ -75,7 +75,7 @@ The following environment variables can be overridden when running containers: ### Example usage ```bash -docker run -e PHP_MEMORY_LIMIT=512M -e PHP_MAX_EXECUTION_TIME=600 kingpin/php-docker:8.3-fpm-alpine +docker run -e PHP_MEMORY_LIMIT=512M -e PHP_MAX_EXECUTION_TIME=600 kingpin/php-docker:8.3-fpm-alpine-v2 ``` ## 🚀 Quick Start @@ -118,15 +118,13 @@ We maintain **two image variants** to support both existing users and modern use - Simpler Dockerfile with fewer runtime layers - No s6-overlay or external init system -- Builds with standard `docker build` (no BuildKit required) -- Smaller image footprint in some configurations +- Smaller image footprint **Pros:** -✅ Drop-in replacement for existing deployments -✅ Simpler container runtime behavior -✅ Smaller learning curve -✅ No BuildKit dependency for local builds +✅ Drop-in replacement for existing deployments +✅ Simpler container runtime behavior +✅ Smaller learning curve **Cons:** @@ -151,22 +149,21 @@ We maintain **two image variants** to support both existing users and modern use - Uses [s6-overlay](https://github.com/just-containers/s6-overlay) as PID 1 init - Proper signal handling and zombie process reaping - Service supervision and restart policies -- BuildKit-enabled for better build performance and caching - Built on Debian Trixie (`:trixie` tags) with `:bookworm` compatibility aliases **Pros:** -✅ Proper PID 1 and process supervision (s6) -✅ Safe for running FPM + sidecar processes (e.g., cron, queue workers) -✅ Better build performance with BuildKit cache mounts -✅ Easier to add background services and health checks +✅ Proper PID 1 and process supervision (s6) +✅ Safe for running FPM + sidecar processes (e.g., cron, queue workers) +✅ Environment-based PHP config at runtime +✅ Non-root user (appuser, UID 1000) by default +✅ Easier to add background services and health checks ✅ Handles container signals properly **Cons:** -❌ Requires Docker BuildKit/buildx for advanced features -❌ Slightly larger image due to s6-overlay (~2-3MB) -❌ Different runtime behavior may require minor adjustments +❌ Slightly larger image due to s6-overlay (~2-3MB) +❌ Different runtime behavior may require minor adjustments ❌ More complex init system to understand **Use v2 when:** @@ -211,9 +208,9 @@ For more details, see [docs/ci.md](docs/ci.md). These images are designed with security in mind: -- **Non-root User**: Containers run as a non-root `appuser` (UID 1000) by default -- **Limited Permissions**: `/var/www/html` directory has appropriate permissions -- **Security Updates**: Images are regularly scanned for vulnerabilities +- **Non-root User (v2)**: v2 containers include a non-root `appuser` (UID 1000). v1 images run as the base PHP image default (root) +- **Limited Permissions (v2)**: `/var/www/html` directory has appropriate ownership and permissions +- **Security Updates**: Images are regularly scanned for vulnerabilities via Trivy ### Security Best Practices @@ -335,7 +332,7 @@ Both v1 and v2 variants are available for all combinations below: | 8.2 | FPM | Bookworm | `8.2-fpm-bookworm` | `8.2-fpm-bookworm-v2` | | 8.2 | Apache | Bookworm | `8.2-apache-bookworm` | `8.2-apache-bookworm-v2` | -> **Note:** PHP 8.1+ images are built on Bookworm (Debian 12). Bullseye tags redirect to Bookworm for PHP 8.1+. +> **Note:** v1 Debian images use Bookworm. v2 Debian images use Trixie, with `:bookworm-v2` tags as compatibility aliases pointing to the same Trixie-built images. ### Deprecated Tags (v1 only) @@ -360,17 +357,17 @@ The following tags are deprecated and will not be built going forward, but remai ## 📊 Image Sizes -Approximate compressed sizes (v1 / v2): +Approximate compressed sizes (vary by PHP version and platform): -| Type | OS | v1 Size | v2 Size | Delta | -|--------|-----------|---------|----------|----------| -| CLI | Alpine | ~80MB | ~83MB | +3MB | -| CLI | Bookworm | ~140MB | ~143MB | +3MB | -| FPM | Alpine | ~85MB | ~88MB | +3MB | -| FPM | Bookworm | ~150MB | ~153MB | +3MB | -| Apache | Bookworm | ~180MB | ~183MB | +3MB | +| Type | OS | v1 Size | v2 Size | +|--------|-----------|---------|----------| +| CLI | Alpine | ~80MB | ~83MB | +| CLI | Bookworm | ~140MB | ~143MB | +| FPM | Alpine | ~85MB | ~88MB | +| FPM | Bookworm | ~150MB | ~153MB | +| Apache | Bookworm | ~180MB | ~183MB | -> v2 overhead is primarily the s6-overlay binaries (~2-3MB per image). +> v2 overhead is primarily the s6-overlay binaries (~2-3MB). Build dependencies are cleaned up to minimize image size. ## Pre-installed PHP Extensions @@ -425,7 +422,7 @@ Approximate compressed sizes (v1 / v2): docker run -d --name php-app kingpin/php-docker:8.3-cli-alpine php -v ``` -### With docker-compose +### With Docker Compose ```yaml services: @@ -487,29 +484,32 @@ The following PHP versions are **no longer actively built** but remain available ## 🏗️ Architecture Diagram ``` - ┌───────────────┐ - │ Base Image │ - │ php:X.Y │ - └───────┬───────┘ - │ - ┌─────────────┼─────────────┐ - │ │ │ -┌─────────▼────┐ ┌──────▼─────┐ ┌─────▼──────┐ -│ Alpine OS │ │ Bullseye OS│ │ Bookworm OS│ -└─────────┬────┘ └──────┬─────┘ └─────┬──────┘ - │ │ │ -┌─────────▼────┐ ┌──────▼─────┐ ┌─────▼──────┐ -│ Extensions & │ │ Extensions │ │Extensions &│ -│ Libraries │ │ Libraries │ │ Libraries │ -└─────────┬────┘ └──────┬─────┘ └─────┬──────┘ - │ │ │ - └─────────────┼─────────────┘ - │ - ┌──────────┼──────────┐ - │ │ │ - ┌──────▼───┐ ┌────▼────┐ ┌───▼─────┐ - │ CLI │ │ FPM │ │ Apache │ - └──────────┘ └─────────┘ └─────────┘ + ┌───────────────┐ + │ Base Image │ + │ php:X.Y-... │ + └───────┬───────┘ + │ + ┌─────────────┴─────────────┐ + │ │ + ┌──────▼──────┐ ┌───────▼───────┐ + │ v1 │ │ v2 │ + │ (simple) │ │ (s6-overlay) │ + └──────┬──────┘ └───────┬───────┘ + │ │ + ┌───────┼───────┐ ┌────────┼────────┐ + │ │ │ │ + Alpine Bookworm Alpine Trixie + │ │ │ (bookworm + │ │ │ compat) + └───────┬───────┘ └────────┬────────┘ + │ │ + └─────────┬─────────────────┘ + │ + ┌──────────┼──────────┐ + │ │ │ + ┌──────▼───┐ ┌────▼────┐ ┌───▼─────┐ + │ CLI │ │ FPM │ │ Apache │ + └──────────┘ └─────────┘ └─────────┘ ``` ## Contributing @@ -536,31 +536,7 @@ Our CI/CD pipeline will automatically test your changes when you submit a PR. ## License -This project is licensed under the MIT License - see below for details: - -``` -MIT License - -Copyright (c) 2023 Kingpin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -``` +This project is licensed under the MIT License - see [LICENSE](LICENSE) for details. --- From 00708866b29eb2a3a27b75e3a9c4e7fc62165f6b Mon Sep 17 00:00:00 2001 From: KingPin <{ID}+{username}@users.noreply.github.com> Date: Thu, 26 Mar 2026 00:30:27 -0400 Subject: [PATCH 7/7] fix: address PR review feedback - Remove unreachable case 3) SLEEP_TIME=20 branches in retry loops (guarded by ATTEMPT -lt 3, so ATTEMPT=3 never reaches the case) - Clarify README: appuser is pre-created in v2 but entrypoint runs as root for s6-overlay; users should use --user 1000:1000 --- Dockerfile.v1 | 2 -- Dockerfile.v2 | 2 -- README.md | 6 ++++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Dockerfile.v1 b/Dockerfile.v1 index 228a820..d980359 100644 --- a/Dockerfile.v1 +++ b/Dockerfile.v1 @@ -28,7 +28,6 @@ RUN for ATTEMPT in 1 2 3; do \ case $ATTEMPT in \ 1) SLEEP_TIME=5 ;; \ 2) SLEEP_TIME=10 ;; \ - 3) SLEEP_TIME=20 ;; \ esac; \ echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ @@ -49,7 +48,6 @@ RUN for ATTEMPT in 1 2 3; do \ case $ATTEMPT in \ 1) SLEEP_TIME=5 ;; \ 2) SLEEP_TIME=10 ;; \ - 3) SLEEP_TIME=20 ;; \ esac; \ echo "Extension installation attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ diff --git a/Dockerfile.v2 b/Dockerfile.v2 index 4d3e498..6c03b23 100644 --- a/Dockerfile.v2 +++ b/Dockerfile.v2 @@ -138,7 +138,6 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ case $ATTEMPT in \ 1) SLEEP_TIME=5 ;; \ 2) SLEEP_TIME=10 ;; \ - 3) SLEEP_TIME=20 ;; \ esac; \ echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ @@ -183,7 +182,6 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \ case $ATTEMPT in \ 1) SLEEP_TIME=5 ;; \ 2) SLEEP_TIME=10 ;; \ - 3) SLEEP_TIME=20 ;; \ esac; \ echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \ sleep $SLEEP_TIME; \ diff --git a/README.md b/README.md index 3b2b9b1..96fcfdf 100644 --- a/README.md +++ b/README.md @@ -156,10 +156,12 @@ We maintain **two image variants** to support both existing users and modern use ✅ Proper PID 1 and process supervision (s6) ✅ Safe for running FPM + sidecar processes (e.g., cron, queue workers) ✅ Environment-based PHP config at runtime -✅ Non-root user (appuser, UID 1000) by default +✅ Includes non-root user (`appuser`, UID 1000) for running your application ✅ Easier to add background services and health checks ✅ Handles container signals properly +> The container entrypoint runs as root (required for s6-overlay as PID 1), but a non-root `appuser` is pre-created. To run your app as this user, use `--user 1000:1000` or configure your orchestrator's security context. + **Cons:** ❌ Slightly larger image due to s6-overlay (~2-3MB) @@ -208,7 +210,7 @@ For more details, see [docs/ci.md](docs/ci.md). These images are designed with security in mind: -- **Non-root User (v2)**: v2 containers include a non-root `appuser` (UID 1000). v1 images run as the base PHP image default (root) +- **Non-root User (v2)**: v2 images include a pre-created `appuser` (UID 1000) for running your application. The entrypoint runs as root for s6-overlay, but your app can run as appuser via `--user 1000:1000`. v1 images run as the base PHP image default (root) - **Limited Permissions (v2)**: `/var/www/html` directory has appropriate ownership and permissions - **Security Updates**: Images are regularly scanned for vulnerabilities via Trivy