# 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