PHP Docker image for production: slim images and multi-stage builds
Our PHP Docker image was 1.2 GB. After optimization it is 180 MB. Notes on what actually helped.
Multi-stage build removes dev tools from the final image:
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-scripts --optimize-autoloader
FROM php:8.2-fpm-alpine AS runtime
RUN apk add --no-cache \
libpq-dev \
&& docker-php-ext-install pdo_pgsql opcache
COPY --from=vendor /app/vendor /app/vendor
COPY . /app
Key points:
- Alpine base saves 200+ MB vs Debian
--no-devremoves dev dependencies from vendor- Multi-stage keeps Composer out of the final image
- Only install extensions you actually use
Alpine has some gotchas with glibc-dependent extensions. If you use extensions that link against glibc (some PECL extensions do), you need musl-compatible builds or switch to debian-slim which is still much smaller than the full image.
Add OPcache configuration in the Dockerfile: COPY opcache.ini /usr/local/etc/php/conf.d/. Do not rely on the .env file for OPcache settings because they need to be set before PHP starts.
The vendor copy order matters for layer caching. Copy composer files and run install BEFORE copying application code. That way code changes do not invalidate the vendor layer.
We use php:8.2-fpm-alpine as base and add a custom entrypoint script that runs php artisan migrate --force on start. Useful for deploy-on-start patterns. Just make sure the command is idempotent.
docker scout or dive tool helps inspect layer sizes and find what is taking space. Often a single RUN step that leaves temp files in the layer is responsible for unexpected size.
One thing that bit us: composer dump-autoload --optimize needs to run AFTER all files are copied, not in the vendor stage. The classmap needs to know all app classes, not just vendor.
For development, use a separate Dockerfile.dev that extends the runtime image and adds Xdebug, pcov, and dev dependencies. Never ship dev tooling in the production image.