How to Optimize Docker Images for Laravel Apps: A Simple Guide
If you already used Docker with a Laravel app, you know the final image can get big and that affects build time, deployment speed, and even storage cost.
In this post, I’ll show you how to make you Docker image smaller and faster using multi-stage builds, lightweight images, and a few best practices.
Basic Structure
Here is a real example using PHP 8.3 + Laravel + Composer + Node for front assets:
# Stage 1: Build the app dependencies
FROM composer:2.7 as vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --optimize-autoloader
COPY . .
# Stage 2: Build frontend assets (optional)
FROM node:20-alpine as frontend
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 3: Final production image (clean and light)
FROM php:8.3-fpm-alpine
RUN apk add --no-cache
bash curl libpng libjpeg-turbo-dev libzip-dev oniguruma-dev
&& docker-php-ext-install pdo pdo_mysql zip mbstring
WORKDIR /var/www
COPY --from=vendor /app /var/www
COPY --from=frontend /app/public/build /var/www/public/build
RUN chown -R www-data:www-data /var/www
USER www-data
Best Practices
- Use Alpine Images they are much smaller and good for prod.
- Split the build into stages This keeps dev tools out of the final image.
- Do not install Composer or Node in the final Image this reduces the size and security risks.
- Don’t use
COPY . .
at the end copy only what you really need (no .env, .git, tests, etc).
Before And After
A non optimize image can be 1.2GB or more.
With multi-stage builds and Alpine, it can be under 200MB!
Extra Security Tips
- Use
USER www-data
at the end - Add a
.dockerignore
file
node_modules
vendor
.env
.git
tests
Final Thoughts
Optimized Docker images are faster, safer, and cheaper.
Have youy optimized you Laravel Setup ? Do you have other tips ?