initial commit
This commit is contained in:
153
.github/workflows/build.yml
vendored
Normal file
153
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
REGISTRY_USER:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
REGISTRY_TOKEN:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
plan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.compute.outputs.matrix }}
|
||||||
|
should_build: ${{ steps.compute.outputs.should_build }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- id: compute
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Discover lanes: immediate subdirs under docker/
|
||||||
|
mapfile -t ALL_LANES < <(find docker -mindepth 1 -maxdepth 1 -type d | sort)
|
||||||
|
if [[ ${#ALL_LANES[@]} -eq 0 ]]; then
|
||||||
|
echo "No lanes found under docker/. Nothing to do."
|
||||||
|
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||||
|
echo 'matrix={"dir":[]}' >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine changed files (PR vs push)
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
BASE_SHA="${{ github.event.pull_request.base.sha }}"
|
||||||
|
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||||||
|
else
|
||||||
|
BASE_SHA="$(git rev-parse HEAD~1 || echo '')"
|
||||||
|
HEAD_SHA="$(git rev-parse HEAD)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$BASE_SHA" ]]; then
|
||||||
|
CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
|
||||||
|
else
|
||||||
|
# First commit or shallow: treat everything as changed
|
||||||
|
CHANGED=$(git ls-files)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Changed files:"
|
||||||
|
echo "$CHANGED"
|
||||||
|
|
||||||
|
# If workflow changed, rebuild all lanes
|
||||||
|
if grep -qx ".github/workflows/build.yml" <<< "$CHANGED"; then
|
||||||
|
echo "Workflow changed; rebuilding all lanes."
|
||||||
|
TARGET_DIRS=("${ALL_LANES[@]}")
|
||||||
|
else
|
||||||
|
# Build only lanes with changes under their directories
|
||||||
|
TARGET_DIRS=()
|
||||||
|
for lane in "${ALL_LANES[@]}"; do
|
||||||
|
# Any change directly under lane dir counts; include Dockerfile or subpaths
|
||||||
|
if grep -q "^${lane}/" <<< "$CHANGED"; then
|
||||||
|
TARGET_DIRS+=("$lane")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# De-duplicate
|
||||||
|
mapfile -t TARGET_DIRS < <(printf "%s\n" "${TARGET_DIRS[@]}" | awk 'NF && !x[$0]++')
|
||||||
|
|
||||||
|
if [[ ${#TARGET_DIRS[@]} -eq 0 ]]; then
|
||||||
|
echo "No lane directories changed. Skipping build."
|
||||||
|
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||||
|
echo 'matrix={"dir":[]}' >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Produce JSON matrix
|
||||||
|
JSON=$(jq -nc --argjson arr "$(printf '%s\n' "${TARGET_DIRS[@]}" | jq -R . | jq -s .)" '{dir: $arr}')
|
||||||
|
echo "Matrix: $JSON"
|
||||||
|
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "matrix=$JSON" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: plan
|
||||||
|
if: needs.plan.outputs.should_build == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.plan.outputs.matrix) }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: docker/setup-qemu-action@v3
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: gitea.auvem.com
|
||||||
|
username: ${{ inputs.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Derive image name and tags
|
||||||
|
id: meta
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
DIR="${{ matrix.dir }}"
|
||||||
|
NAME="${DIR#docker/}" # e.g. '7.4' or 'nginx'
|
||||||
|
SHA=${GITHUB_SHA::7}
|
||||||
|
|
||||||
|
# Decide repository and tag scheme:
|
||||||
|
# - nginx lane -> gitea.auvem.com/auvem/wordpress-nginx:stable
|
||||||
|
# - other lanes (assumed php variants) -> gitea.auvem.com/auvem/wordpress-php-fpm:<version>-stable
|
||||||
|
if [[ "${NAME}" == "nginx" ]]; then
|
||||||
|
IMAGE="gitea.auvem.com/auvem/wordpress-nginx"
|
||||||
|
TAG="stable"
|
||||||
|
else
|
||||||
|
IMAGE="gitea.auvem.com/auvem/wordpress-php-fpm"
|
||||||
|
# Extract version like 7.4 or 8.1 from the lane name; otherwise use lane name
|
||||||
|
if [[ "${NAME}" =~ ([0-9]+\.[0-9]+) ]]; then
|
||||||
|
VERSION="${BASH_REMATCH[1]}"
|
||||||
|
TAG="${VERSION}-stable"
|
||||||
|
elif [[ "${NAME}" =~ ([0-9]+) ]]; then
|
||||||
|
VERSION="${BASH_REMATCH[1]}"
|
||||||
|
TAG="${VERSION}-stable"
|
||||||
|
else
|
||||||
|
TAG="${NAME}-stable"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "image=$IMAGE" >> $GITHUB_OUTPUT
|
||||||
|
echo "tags=$IMAGE:${TAG},$IMAGE:git-${SHA}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ matrix.dir }}/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
platforms: linux/amd64
|
||||||
|
cache-from: type=registry,ref=${{ steps.meta.outputs.image }}:cache
|
||||||
|
cache-to: type=registry,ref=${{ steps.meta.outputs.image }}:cache,mode=max
|
||||||
124
README.md
Normal file
124
README.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# auvem/php-fpm-wordpress — multi-version PHP-FPM + nginx Docker images
|
||||||
|
|
||||||
|
This repository contains Dockerfiles and configuration for building PHP-FPM
|
||||||
|
images optimized for WordPress and a companion nginx image. It aims to make
|
||||||
|
adding and maintaining multiple PHP versions straightforward while keeping
|
||||||
|
builds reproducible and small.
|
||||||
|
|
||||||
|
## Repository Layout
|
||||||
|
|
||||||
|
- `docker/` — top-level directory containing per-image lanes
|
||||||
|
- `7.4/` — PHP 7.4 FPM lane (multi-stage, Alpine-based)
|
||||||
|
- `Dockerfile` — builds PHP + required extensions
|
||||||
|
- `nginx/` — nginx lane
|
||||||
|
- `Dockerfile` — nginx:alpine-slim image that ships `nginx.conf`
|
||||||
|
- `nginx.conf` — default server config that works with the php-fpm image
|
||||||
|
- `php-fpm/` — canonical shared files
|
||||||
|
- `www.conf` — canonical php-fpm pool config
|
||||||
|
- `entrypoint.sh` — optional guarded entrypoint to fix mounts at container start
|
||||||
|
|
||||||
|
### Note about shared files and builds
|
||||||
|
|
||||||
|
The CI workflow is configured to build with the repository root as the Docker
|
||||||
|
build context and to point Docker to lane Dockerfiles (for example,
|
||||||
|
`file: docker/7.4/Dockerfile`). That means Dockerfiles can safely `COPY`
|
||||||
|
shared files from `docker/php-fpm/` without requiring per-lane duplicates. This
|
||||||
|
reduces maintenance overhead — keep the canonical copy in
|
||||||
|
`docker/php-fpm/www.conf` and the CI will make it available to all lanes.
|
||||||
|
|
||||||
|
## Adding a new PHP version
|
||||||
|
|
||||||
|
1. Create `docker/<version>/` (e.g. `docker/8.1/`).
|
||||||
|
2. Copy `docker/7.4/Dockerfile` into the new directory and update `ARG BASE_TAG`
|
||||||
|
to the desired `php:<version>-fpm-alpine` tag.
|
||||||
|
3. Adjust `docker-php-ext-install`/build deps if needed.
|
||||||
|
4. Push — CI will detect the new lane and build it.
|
||||||
|
|
||||||
|
## Bind mounts and permissions
|
||||||
|
|
||||||
|
- Images use `/var/www/html` as the webroot. When you mount a host directory
|
||||||
|
over that path the mount replaces the image contents, including ownership.
|
||||||
|
- Recommended safe options:
|
||||||
|
- Pre-chown host files to UID/GID 1000 before starting containers:
|
||||||
|
```bash
|
||||||
|
sudo chown -R 1000:1000 ./wp_root
|
||||||
|
```
|
||||||
|
- Or enable the entrypoint-based fixup in php-fpm by setting
|
||||||
|
`CHOWN_ON_START=1` for the `php-fpm` service (the entrypoint is guarded — it
|
||||||
|
only runs when this env is explicitly enabled).
|
||||||
|
|
||||||
|
## Local Testing & Development
|
||||||
|
|
||||||
|
Use the provided `docker-compose.yml` in the repo root for local development —
|
||||||
|
it builds images from the repo (so shared files are available) and mounts
|
||||||
|
`./wp_root` for site content.
|
||||||
|
|
||||||
|
## Production Example
|
||||||
|
|
||||||
|
Below is an example `docker-compose.yml` for production deployments that pulls
|
||||||
|
images from your registry instead of building locally. Adjust image names and
|
||||||
|
secrets as appropriate.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mariadb:10.11
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_DATABASE: wordpress
|
||||||
|
MYSQL_USER: wordpress
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
|
php-fpm:
|
||||||
|
image: gitea.auvem.com/auvem/wordpress-php-fpm:7.4-stable
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
WORDPRESS_DB_HOST: db:3306
|
||||||
|
WORDPRESS_DB_USER: wordpress
|
||||||
|
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
WORDPRESS_DB_NAME: wordpress
|
||||||
|
volumes:
|
||||||
|
- ./wp_root:/var/www/html:rw
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: gitea.auvem.com/auvem/wordpress-nginx:stable
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
depends_on:
|
||||||
|
- php-fpm
|
||||||
|
volumes:
|
||||||
|
- ./wp_root:/var/www/html:ro
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI / build notes
|
||||||
|
|
||||||
|
- The GitHub Actions workflow at `.github/workflows/build.yml` discovers
|
||||||
|
immediate subdirectories of `docker/` and builds each as a lane. The workflow
|
||||||
|
has been updated to use the repository root as the Docker build context and
|
||||||
|
to set the `file` property to the lane Dockerfile (so shared files in
|
||||||
|
`docker/php-fpm/` are accessible during build).
|
||||||
|
- The workflow tags and pushes images using the pattern `gitea.auvem.com/auvem/wordpress-<component>:<tag>`.
|
||||||
|
- PHP lanes are pushed to `gitea.auvem.com/auvem/wordpress-php-fpm:<version>-stable` (for example `7.4-stable`).
|
||||||
|
- The nginx lane is pushed to `gitea.auvem.com/auvem/wordpress-nginx:stable`.
|
||||||
|
If you prefer a different naming convention, update the `meta` step in the workflow.
|
||||||
|
|
||||||
|
### Security & hardening
|
||||||
|
|
||||||
|
- Multi-stage builds keep final images minimal and reduce attack surface.
|
||||||
|
- PHP uses `php.ini-production` with opcache tuned. The php-fpm pool is
|
||||||
|
configured to drop workers to `app` (UID 1000) while the master runs as
|
||||||
|
`root` to avoid socket/permission surprises; workers remain unprivileged.
|
||||||
|
- The nginx config contains conservative security headers and blocking of
|
||||||
|
hidden files; review and extend headers (CSP, COEP, COOP) as needed per-site.
|
||||||
|
|
||||||
|
### Production deployment and archival
|
||||||
|
|
||||||
|
- For archival (quiesce + tar), stop services and `tar` the `./wp_root` and any
|
||||||
|
associated volumes (database dump + attachments). Ensure services are fully
|
||||||
|
stopped to avoid inconsistent state.
|
||||||
42
docker-compose.yml
Normal file
42
docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mariadb:10.11
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_DATABASE: wordpress
|
||||||
|
MYSQL_USER: wp
|
||||||
|
MYSQL_PASSWORD: example
|
||||||
|
MYSQL_ROOT_PASSWORD: example_root
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
|
php-fpm:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/7.4/Dockerfile
|
||||||
|
image: local/auvem-php:7.4
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./wp_root:/var/www/html:rw
|
||||||
|
environment:
|
||||||
|
WORDPRESS_DB_HOST: db:3306
|
||||||
|
WORDPRESS_DB_USER: wp
|
||||||
|
WORDPRESS_DB_PASSWORD: example
|
||||||
|
WORDPRESS_DB_NAME: wordpress
|
||||||
|
# Enable chown-on-start if you want the container to fix host mounts
|
||||||
|
# CHOWN_ON_START=1
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/nginx/Dockerfile
|
||||||
|
image: local/auvem-nginx:latest
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
depends_on:
|
||||||
|
- php-fpm
|
||||||
|
volumes:
|
||||||
|
- ./wp_root:/var/www/html:ro
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
135
docker/7.4/Dockerfile
Normal file
135
docker/7.4/Dockerfile
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Multi-stage Alpine-based PHP 7.4 FPM image optimized for WordPress
|
||||||
|
# - builds extensions in a builder stage (isolation)
|
||||||
|
# - copies only runtime bits into the final image
|
||||||
|
# - minimal runtime packages, production php.ini and opcache tuned
|
||||||
|
# - includes common WordPress extensions: pdo_mysql, mysqli, gd, zip, exif,
|
||||||
|
# intl, xml, xmlrpc (if compiled), bcmath, opcache, mbstring, curl, fileinfo
|
||||||
|
|
||||||
|
ARG BASE_TAG=7.4-fpm-alpine3.16
|
||||||
|
FROM php:${BASE_TAG} AS build
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
apk add --no-cache --virtual .build-deps \
|
||||||
|
$PHPIZE_DEPS \
|
||||||
|
autoconf \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
make \
|
||||||
|
pkgconfig \
|
||||||
|
bash \
|
||||||
|
freetype-dev \
|
||||||
|
libjpeg-turbo-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
zlib-dev \
|
||||||
|
icu-dev \
|
||||||
|
libzip-dev \
|
||||||
|
oniguruma-dev \
|
||||||
|
mariadb-dev \
|
||||||
|
; \
|
||||||
|
# Configure and build common extensions required by WordPress
|
||||||
|
docker-php-ext-configure gd --with-freetype --with-jpeg; \
|
||||||
|
docker-php-ext-install -j"$(nproc)" \
|
||||||
|
gd \
|
||||||
|
mysqli \
|
||||||
|
pdo \
|
||||||
|
pdo_mysql \
|
||||||
|
zip \
|
||||||
|
exif \
|
||||||
|
intl \
|
||||||
|
bcmath \
|
||||||
|
opcache \
|
||||||
|
xml \
|
||||||
|
mbstring \
|
||||||
|
xmlrpc \
|
||||||
|
soap \
|
||||||
|
pcntl \
|
||||||
|
; \
|
||||||
|
pecl channel-update pecl.php.net; \
|
||||||
|
pecl install redis && docker-php-ext-enable redis; \
|
||||||
|
# Use production php.ini as a base
|
||||||
|
cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"; \
|
||||||
|
# Clean build deps (we'll copy only runtime artifacts to final image)
|
||||||
|
apk del .build-deps; \
|
||||||
|
rm -rf /var/cache/apk/* /tmp/*
|
||||||
|
|
||||||
|
FROM php:${BASE_TAG} AS runtime
|
||||||
|
|
||||||
|
# Install only runtime library packages (no -dev)
|
||||||
|
RUN set -eux; \
|
||||||
|
apk add --no-cache \
|
||||||
|
freetype \
|
||||||
|
libjpeg-turbo \
|
||||||
|
libpng \
|
||||||
|
libxml2 \
|
||||||
|
zlib \
|
||||||
|
icu-libs \
|
||||||
|
libzip \
|
||||||
|
mariadb-client \
|
||||||
|
openssl \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
; \
|
||||||
|
update-ca-certificates || true
|
||||||
|
|
||||||
|
# Copy built PHP and extensions from the build stage
|
||||||
|
COPY --from=build /usr/local/lib/php /usr/local/lib/php
|
||||||
|
COPY --from=build /usr/local/etc/php /usr/local/etc/php
|
||||||
|
|
||||||
|
# Create a non-root application user and prepare webroot directory
|
||||||
|
RUN addgroup -g 1000 app || true; \
|
||||||
|
adduser -D -u 1000 -G app app || true; \
|
||||||
|
mkdir -p /var/www/html; \
|
||||||
|
chown -R app:app /var/www/html; \
|
||||||
|
# Ensure php-fpm runtime directories exist and are writable by the app user
|
||||||
|
mkdir -p /var/run/php /run/php /var/log/php; \
|
||||||
|
chown -R app:app /var/run/php /run/php /var/log/php
|
||||||
|
|
||||||
|
# Minimal security / production tuning for opcache and PHP
|
||||||
|
RUN set -eux; \
|
||||||
|
{ \
|
||||||
|
echo 'opcache.enable=1'; \
|
||||||
|
echo 'opcache.memory_consumption=128'; \
|
||||||
|
echo 'opcache.interned_strings_buffer=8'; \
|
||||||
|
echo 'opcache.max_accelerated_files=10000'; \
|
||||||
|
echo 'opcache.revalidate_freq=2'; \
|
||||||
|
echo 'opcache.fast_shutdown=1'; \
|
||||||
|
echo 'opcache.enable_file_override=0'; \
|
||||||
|
} > /usr/local/etc/php/conf.d/zz-opcache.ini; \
|
||||||
|
{ \
|
||||||
|
echo 'expose_php = Off'; \
|
||||||
|
echo 'display_errors = Off'; \
|
||||||
|
echo 'log_errors = On'; \
|
||||||
|
echo 'error_log = /proc/self/fd/2'; \
|
||||||
|
} > /usr/local/etc/php/conf.d/zz-hardening.ini
|
||||||
|
|
||||||
|
# Expose FPM socket port (if using TCP); keep default unix socket when preferred
|
||||||
|
EXPOSE 9000
|
||||||
|
|
||||||
|
# Healthcheck: ensure php-fpm master process exists
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD pgrep -f "php-fpm" || exit 1
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
# Copy pool configuration from build context (keep a copy in each lane to
|
||||||
|
# allow building with context set to the lane directory).
|
||||||
|
COPY --chown=root:root docker/php-fpm/www.conf /usr/local/etc/php-fpm.d/www.conf
|
||||||
|
# Copy a shared entrypoint that can optionally fix permissions on the mounted webroot
|
||||||
|
COPY --chown=root:root docker/php-fpm/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod 755 /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
|
# Use a small entrypoint to optionally chown mounted volumes at container start.
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
|
|
||||||
|
# Run php-fpm master as root and have workers drop to the non-root `app` user.
|
||||||
|
# This avoids permission surprises while keeping workers unprivileged.
|
||||||
|
USER root
|
||||||
|
|
||||||
|
CMD ["php-fpm"]
|
||||||
|
|
||||||
|
# Notes:
|
||||||
|
# - The build stage compiles extensions and leaves a production php.ini in place.
|
||||||
|
# - The final image copies only the runtime artifacts and installs only runtime libs
|
||||||
|
# to keep it small.
|
||||||
|
# - If you want to run PHP-FPM as a non-root user in container, you will need to
|
||||||
|
# adjust the php-fpm pool user/group configuration in /usr/local/etc/php-fpm.d/www.conf
|
||||||
11
docker/nginx/Dockerfile
Normal file
11
docker/nginx/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM nginx:alpine-slim
|
||||||
|
|
||||||
|
# Add a minimal, secure nginx config that works with the php-fpm service
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Create directories for logs and ensure permissions (nginx user is nginx)
|
||||||
|
RUN mkdir -p /var/www/html /var/log/nginx && chown -R nginx:nginx /var/www/html /var/log/nginx
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
69
docker/nginx/nginx.conf
Normal file
69
docker/nginx/nginx.conf
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
root /var/www/html;
|
||||||
|
index index.php index.html index.htm;
|
||||||
|
|
||||||
|
# Basic security headers (can be extended per-site)
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||||
|
|
||||||
|
# Deny access to hidden files and directories
|
||||||
|
location ~ (^|/)[.] {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static files: long cache, immutable where appropriate
|
||||||
|
location ~* \.(?:css|js|gif|jpe?g|png|ico|svg|woff2?|ttf|eot)$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
access_log off;
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, max-age=2592000, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; allow all; }
|
||||||
|
|
||||||
|
# Main front controller; fall back to index.php
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
# PHP-FPM handling; pass to php-fpm:9000 (docker service name)
|
||||||
|
location ~ [^/]
|
||||||
|
\.php(/|$) {
|
||||||
|
# Prevent direct access to PHP files in uploads or other writable dirs if necessary
|
||||||
|
try_files $document_root$fastcgi_script_name =404;
|
||||||
|
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
# Use TCP FPM backend service name. Matches the php-fpm image we built.
|
||||||
|
fastcgi_pass php-fpm:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param HTTPS $https if_not_empty;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block access to .ht* files
|
||||||
|
location ~* /\.(?:ht|git) {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optional: small buffer for large headers (WordPress with many cookies/plugins)
|
||||||
|
fastcgi_buffers 16 16k;
|
||||||
|
fastcgi_buffer_size 32k;
|
||||||
|
|
||||||
|
# Prevent clickjacking on all responses
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default server; allow override by mounting /etc/nginx/conf.d/default.conf
|
||||||
19
docker/php-fpm/entrypoint.sh
Normal file
19
docker/php-fpm/entrypoint.sh
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Entrypoint for php-fpm images.
|
||||||
|
# If CHOWN_ON_START is set to '1' or 'true', recursively chown the webroot
|
||||||
|
# to the 'app' user (UID 1000). This is optional and must be explicitly enabled
|
||||||
|
# via environment (safer for multi-tenant hosts).
|
||||||
|
|
||||||
|
: ${CHOWN_ON_START:=}
|
||||||
|
if [ "${CHOWN_ON_START}" = "1" ] || [ "${CHOWN_ON_START}" = "true" ]; then
|
||||||
|
echo "[entrypoint] CHOWN_ON_START enabled — fixing ownership of /var/www/html"
|
||||||
|
# Only run chown if the directory exists
|
||||||
|
if [ -d /var/www/html ]; then
|
||||||
|
chown -R 1000:1000 /var/www/html || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exec the given command (php-fpm by default)
|
||||||
|
exec "$@"
|
||||||
43
docker/php-fpm/www.conf
Normal file
43
docker/php-fpm/www.conf
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
; Shared php-fpm pool configuration for containers
|
||||||
|
; Designed to be reused across php-fpm versions in this repo
|
||||||
|
|
||||||
|
[www]
|
||||||
|
; Listen on TCP to be container-friendly
|
||||||
|
listen = 0.0.0.0:9000
|
||||||
|
|
||||||
|
; Run workers as the unprivileged 'app' user
|
||||||
|
user = app
|
||||||
|
group = app
|
||||||
|
|
||||||
|
; Ensure socket ownership/mode if a socket is used
|
||||||
|
listen.owner = app
|
||||||
|
listen.group = app
|
||||||
|
listen.mode = 0660
|
||||||
|
|
||||||
|
; Process management
|
||||||
|
pm = dynamic
|
||||||
|
pm.max_children = 10
|
||||||
|
pm.start_servers = 2
|
||||||
|
pm.min_spare_servers = 1
|
||||||
|
pm.max_spare_servers = 3
|
||||||
|
pm.max_requests = 500
|
||||||
|
|
||||||
|
; Timeouts and logging
|
||||||
|
request_terminate_timeout = 300s
|
||||||
|
request_slowlog_timeout = 5s
|
||||||
|
slowlog = /var/log/php/www-slow.log
|
||||||
|
|
||||||
|
; Helpful logging for debugging worker crashes
|
||||||
|
catch_workers_output = yes
|
||||||
|
access.log = /var/log/php/www-access.log
|
||||||
|
|
||||||
|
; Keep environment variables (useful if you pass DB credentials via env)
|
||||||
|
clear_env = no
|
||||||
|
|
||||||
|
; Security and restart behaviour
|
||||||
|
emergency_restart_threshold = 10
|
||||||
|
emergency_restart_interval = 1m
|
||||||
|
process_control_timeout = 10s
|
||||||
|
|
||||||
|
; Ensure stdout/stderr are visible in container logs
|
||||||
|
; php-fpm will already write logs to paths above; ensure the directory exists in image
|
||||||
0
version.json
Normal file
0
version.json
Normal file
Reference in New Issue
Block a user