I’m staring a new PHP project and I wanted a clean docker image to work from.
I inherited one on my last project and wanted to improve image size, security and production alignment.
Especially in my CI environment I’d like builds to be as quick as possible and most of the time is spent loading the image.
To get a smaller image I start with Alpine Linux: a security-oriented, lightweight Linux distribution based on musl libc and busybox.
The only things I notice missing from are that it uses /bin/sh instead of /bin/bash and vi instead of vim or nano.
I saved more space by multi-stage build
What this lets me do is
- Install build tools
- Build php modules like xdebug and postgresql
- Discard all the build tools and keep just the output
Some people seem to install a lot of stuff on the docker image - like: git, curl, image libraries etc. I install only what I need. This isn’t a VM - it has one job as a web server.
Finally I don’t install composer here. I use a separate image here (the official composer image) which only runs when I do an update via
I went from over 600Mb to 124Mb
Some people might argue that security in dev is unimportant
- Security is a habit:
By always practising good security you are less likely to forget it later and more likely to notice if something is amiss
- If you get used to ignoring security warnings in dev you may not notice them in prod
- Block whatever you don’t want to use
That way you are sure it isn’t being used and don’t get caught out by third party libraries
In my php-fpm.cong I have
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_flag[allow_url_fopen] = off
The closer I can make my dev system to production the easier it is to catch problems early and the less effort will be taken to fix them.
I won’t run composer updates in production
I wont have a full build environment in production (see the NIST Guide to General Server Security)
Code changes happen on my host computer whether that is got or editing.
Composer and npm updates are also something I see as primarily the responsibility of the host not the dev server - I use helper containers for these so that the only tools developers need to work on the project are git, docker, and an editor. Any project dependencies are within docker - ensuring everyone has teh same versions.
FROM php:8.2.0-fpm-alpine3.16 AS builder
RUN apk add --update postgresql-dev icu-data-full autoconf build-base linux-headers
RUN docker-php-ext-install pdo_pgsql
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
# copy what was built - but go back to the original image without all the build tools
# this reduces final image size from 648MB to 124MB
COPY --from=builder /usr/local /usr/local
# the php pgsql module needs the postgres libs (not all of postgresql-dev though)
RUN apk update && \
apk add postgresql-libs && \
rm -rf /var/cache/apk/*
# setup user/group to a cross-image consistent 1000:1000
RUN addgroup --gid=1000 www
RUN adduser -h /app -s /bin/sh -u 1000 -G www -D www
COPY www.conf /usr/local/etc/php-fpm.d/www.conf
#COPY php-fpm.conf /usr/local/etc/php-fpm.conf
# makes no difference to php-fpm - but sets the default on exec
UPDATE : 07/01/2023
While working on this process I struggled to find an up-to-date set of recommendations for securing PHP
I have since found the OWASP PHP Configuration Cheat Sheet which is great and I will review the above in consideration of this authoritative document. If anything I have written above contradicts with the OWASP document - you should probably follow their advice not mine.