initial commit

This commit is contained in:
Elijah Duffy
2025-12-06 22:25:03 -08:00
commit 377e13c972
9 changed files with 596 additions and 0 deletions

135
docker/7.4/Dockerfile Normal file
View 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
View 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
View 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

View 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
View 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