• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Beyond the Basics: Advanced Docker Multi-Stage Builds for Optimized Laravel Deployments and CI/CD Pipelines

Beyond the Basics: Advanced Docker Multi-Stage Builds for Optimized Laravel Deployments and CI/CD Pipelines

Optimizing Laravel Docker Images with Multi-Stage Builds

For modern PHP applications, particularly those built with frameworks like Laravel, Docker has become an indispensable tool for consistent development, testing, and deployment. However, a naive Dockerfile can lead to bloated images containing development dependencies, build tools, and unnecessary artifacts, increasing build times, image sizes, and potential security attack surfaces. Multi-stage builds offer a robust solution to this problem by allowing us to use multiple `FROM` instructions in a single Dockerfile, discarding intermediate build artifacts and keeping only the essential runtime components in the final image.

This approach is particularly effective for Laravel applications where we need to compile assets (JavaScript, CSS) using tools like npm/yarn, run PHP dependency installations with Composer, and then package only the compiled assets and production-ready PHP code. Let’s explore a practical, multi-stage Dockerfile for a typical Laravel deployment.

Stage 1: The Build Environment (Composer & Node.js)

Our first stage will focus on installing PHP dependencies via Composer and Node.js dependencies via npm or yarn, and crucially, compiling our front-end assets. We’ll use official PHP and Node.js images as our base, ensuring a clean and well-maintained starting point.

# Stage 1: Build Environment
FROM php:8.2-fpm AS builder

# Set working directory
WORKDIR /var/www/html

# Install system dependencies for Composer and Node.js
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libssl-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    curl \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install pdo pdo_mysql zip exif pcntl opcache \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer

# Install Node.js and npm/yarn (using a specific version for consistency)
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
    && apt-get install -y nodejs \
    && npm install -g yarn

# Copy application files
COPY . .

# Install Composer dependencies
RUN composer install --no-dev --optimize-autoloader --no-interaction

# Install Node.js dependencies and compile assets
RUN yarn install --frozen-lockfile
RUN yarn production

In this `builder` stage:

  • We start with a PHP 8.2 FPM image, suitable for production environments.
  • Essential system packages for PHP extensions, Composer, and Node.js are installed.
  • Composer is copied from its official image to ensure we’re using a consistent, up-to-date version.
  • Node.js and Yarn are installed. Using `setup_18.x` and `yarn install –frozen-lockfile` promotes reproducible builds.
  • Application files are copied into the container.
  • Composer dependencies are installed with production flags (`–no-dev`, `–optimize-autoloader`).
  • Node.js dependencies are installed, and front-end assets are compiled for production using `yarn production`.

Stage 2: The Production Environment (Nginx & PHP-FPM)

The second stage will create our lean production image. It will copy only the necessary artifacts from the `builder` stage: the installed Composer dependencies, the compiled assets, and the application code. We’ll also set up Nginx to serve the application.

# Stage 2: Production Environment
FROM php:8.2-fpm-alpine AS production

# Install Nginx and other runtime dependencies
RUN apk update && apk add --no-cache \
    nginx \
    supervisor \
    oniguruma-dev \
    libzip-dev \
    && docker-php-ext-install pdo pdo_mysql zip exif pcntl opcache \
    && apk del oniguruma-dev libzip-dev \
    && rm -rf /var/cache/apk/*

# Set working directory
WORKDIR /var/www/html

# Copy application code and dependencies from the builder stage
COPY --from=builder /var/www/html/vendor /var/www/html/vendor
COPY --from=builder /var/www/html/public /var/www/html/public
COPY --from=builder /var/www/html/bootstrap/cache /var/www/html/bootstrap/cache
COPY --from=builder /var/www/html/storage /var/www/html/storage
COPY --from=builder /var/www/html/routes /var/www/html/routes
COPY --from=builder /var/www/html/app /var/www/html/app
COPY --from=builder /var/www/html/config /var/www/html/config
COPY --from=builder /var/www/html/resources /var/www/html/resources

# Copy Nginx configuration
COPY docker/nginx.conf /etc/nginx/sites-available/default
RUN ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/

# Copy Supervisor configuration for PHP-FPM and Nginx
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Create storage symlink for Nginx
RUN ln -sfn /var/www/html/storage/app/public /var/www/html/public/storage

# Expose port 80
EXPOSE 80

# Start Supervisor to manage Nginx and PHP-FPM
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

In the `production` stage:

  • We use a lean `php:8.2-fpm-alpine` image for a smaller footprint.
  • Nginx and Supervisor are installed. Supervisor is crucial for managing multiple processes (Nginx and PHP-FPM) in a container.
  • PHP extensions required at runtime are installed.
  • Crucially, only the necessary directories (`vendor`, `public`, `bootstrap/cache`, `storage`, `routes`, `app`, `config`, `resources`) are copied from the `builder` stage. This excludes development dependencies, Node.js source files, and intermediate build artifacts.
  • Nginx configuration is copied and enabled.
  • Supervisor configuration is copied to manage Nginx and PHP-FPM.
  • A symbolic link is created for the public storage directory, essential for serving assets like images.
  • Port 80 is exposed.
  • The `CMD` uses `supervisord` to start and manage both Nginx and PHP-FPM processes.

Supporting Configuration Files

The Dockerfile relies on a few external configuration files. Here are examples for `docker/nginx.conf` and `docker/supervisord.conf`.

docker/nginx.conf

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;

    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        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;
    }

    location ~ /\.ht {
        deny all;
    }

    location /storage {
        alias /var/www/html/storage/app/public;
        expires 30d;
        add_header Cache-Control "public";
    }
}

docker/supervisord.conf

[supervisord]
nodaemon=true
user=root

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
priority=10
stdout_logfile=/var/log/supervisor/nginx_stdout.log
stderr_logfile=/var/log/supervisor/nginx_stderr.log

[program:php-fpm]
command=/usr/local/sbin/php-fpm --nodaemonize --fpm-config /usr/local/etc/php-fpm.conf
autostart=true
autorestart=true
priority=20
stdout_logfile=/var/log/supervisor/php-fpm_stdout.log
stderr_logfile=/var/log/supervisor/php-fpm_stderr.log

Note that in the Nginx configuration, `fastcgi_pass php-fpm:9000;` assumes you are using Docker Compose and have a service named `php-fpm` that exposes port 9000. If running a single container, you would typically use `unix:/var/run/php/php8.2-fpm.sock` or `127.0.0.1:9000` depending on your PHP-FPM setup.

Integrating into CI/CD Pipelines

These multi-stage builds are exceptionally well-suited for CI/CD pipelines. The build stage can be used to run tests, static analysis, and other checks without needing to install all production dependencies in the CI runner itself. The final production image is then lean and ready for deployment.

A typical CI pipeline step might look like this:

# Example CI pipeline step (e.g., GitLab CI, GitHub Actions)

# Build the builder stage for testing
docker build --target builder -t my-laravel-app:build .

# Run tests against the builder stage
docker run --rm my-laravel-app:build composer test
# or
docker run --rm my-laravel-app:build yarn test

# If tests pass, build the final production image
docker build -t my-laravel-app:latest .

# Push the production image to a registry
docker push my-laravel-app:latest

This strategy significantly reduces the time and resources required for your CI jobs. Tests are executed in an environment that closely mirrors production, and the final artifact is a highly optimized Docker image.

Further Optimizations and Considerations

  • Caching: Leverage Docker’s build cache effectively. Place commands that change frequently (like copying application code) later in the Dockerfile, and commands that change less often (like installing dependencies) earlier.
  • Security: Regularly update base images and dependencies. Use specific versions for reproducibility. Avoid running as root where possible (though for simplicity in this example, we use root for `supervisord`).
  • Environment Variables: Manage environment-specific configurations using Docker’s `–env` flag or Docker Compose’s `.env` files, rather than baking them into the image.
  • Health Checks: Implement Docker health checks to ensure your application is truly ready after deployment.
  • Multi-Container Applications: For more complex setups, use Docker Compose to define your Nginx, PHP-FPM, and database services, linking them together. The `php-fpm:9000` in the Nginx config assumes this setup.

By adopting multi-stage builds, you can achieve significantly smaller, more secure, and faster-deploying Docker images for your Laravel applications, leading to more efficient development workflows and robust production environments.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Unlocking Next-Gen Performance: Advanced Caching Strategies for Laravel with Redis and Cloudflare Workers
  • Beyond the Basics: Advanced Docker Multi-Stage Builds for Optimized Laravel Deployments and CI/CD Pipelines
  • Leveraging PHP 8.3 JIT and OpCache for Sub-Millisecond WordPress API Response Times with Laravel Octane
  • Leveraging Laravel Octane with Docker Swarm for High-Concurrency, Serverless-like PHP Applications
  • Leveraging PHP 9 JIT and Vector Extensions for Extreme WordPress Headless Performance

Categories

  • apache (1)
  • AWS (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (664)
  • Desktop Applications (14)
  • DevOps (18)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (16)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • Performance & Security Optimization (3)
  • PHP (56)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (10)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (650)
  • SEO & Growth (492)
  • Server (118)
  • Softwares (1)
  • Ubuntu (9)
  • Uncategorized (103)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (47)
  • WordPress Plugin Development (728)
  • WordPress Theme Development (357)

Recent Posts

  • Unlocking Next-Gen Performance: Advanced Caching Strategies for Laravel with Redis and Cloudflare Workers
  • Beyond the Basics: Advanced Docker Multi-Stage Builds for Optimized Laravel Deployments and CI/CD Pipelines
  • Leveraging PHP 8.3 JIT and OpCache for Sub-Millisecond WordPress API Response Times with Laravel Octane

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (728)
  • Debugging & Troubleshooting (664)
  • Security & Compliance (650)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala