Multi-stage Docker to speed up code deploy

Of course. Using a multi-stage Docker build is a fantastic way to optimize your Laravel application's build time in a CI/CD pipeline. The strategy is to separate the installation of dependencies (which don't change often) from your application code (which changes frequently).

Here is a complete, production-ready example using a multi-stage Dockerfile for your Laravel application. This setup assumes you are using Vite for your frontend assets, which is the default in recent Laravel versions.

### The Multi-Stage Dockerfile

Create a file named Dockerfile in the root of your Laravel project with the following content:

# --- Stage 1: PHP Dependencies (Composer) ---

# This stage installs composer dependencies. It's only rebuilt when

# composer.json or composer.lock changes.

FROM composer:2.5 as vendor


WORKDIR /app

COPY database/ database/

COPY composer.json composer.lock ./


# Install dependencies for production

RUN composer install --no-interaction --no-dev --no-scripts --prefer-dist --optimize-autoloader


# Run composer scripts to copy assets

RUN composer run-script post-install-cmd



# --- Stage 2: Frontend Assets (NPM/Vite) ---

# This stage builds your JavaScript and CSS assets. It's only rebuilt

# when your package.json, package-lock.json, or frontend source files change.

FROM node:18-alpine as frontend


WORKDIR /app

COPY package.json package-lock.json ./

RUN npm install


# Copy the rest of the frontend source code and build

COPY . .

RUN npm run build



# --- Stage 3: Final Production Image ---

# This is the final, lean image that will be deployed. It copies

# artifacts from the previous stages.

FROM php:8.2-fpm-alpine


WORKDIR /var/www/html


# Install required system packages and PHP extensions for Laravel

RUN apk add --no-cache \

    libpng-dev \

    libzip-dev \

    jpeg-dev \

    freetype-dev \

    nginx \

    supervisor


RUN docker-php-ext-configure gd --with-freetype --with-jpeg

RUN docker-php-ext-install pdo pdo_mysql zip gd bcmath pcntl


# Clean up apk cache

RUN rm -rf /var/cache/apk/*


# Copy composer dependencies from the 'vendor' stage

COPY --from=vendor /app/vendor/ /var/www/html/vendor/


# Copy built frontend assets from the 'frontend' stage

COPY --from=frontend /app/public/build /var/www/html/public/build


# Copy the application code last, so cache is used for previous steps

COPY . /var/www/html


# Copy Nginx and Supervisor configurations

# (You will need to create these files, examples below)

COPY docker/nginx.conf /etc/nginx/nginx.conf

COPY docker/supervisord.conf /etc/supervisord.conf


# Set correct permissions for Laravel

RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache && \

    chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache


# Expose the port Nginx will listen on

EXPOSE 80


# Start Supervisor to manage Nginx and PHP-FPM

CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]


### Supporting Configuration Files

You'll need to create a docker directory and add the configuration files for Nginx and Supervisor.

1. Nginx Configuration ( docker/nginx.conf )

This file configures Nginx to serve your Laravel application.

user www-data;

worker_processes auto;

pid /run/nginx.pid;

events { worker_connections 1024; }


http {

    include /etc/nginx/mime.types;

    default_type application/octet-stream;

    sendfile on;

    keepalive_timeout 65;


    server {

        listen 80 default_server;

        server_name _;

        root /var/www/html/public;

        index index.php;


        location / {

            try_files $uri $uri/ /index.php?$query_string;

        }


        location ~ \.php$ {

            try_files $uri =404;

            fastcgi_split_path_info ^(.+\.php)(/.+)$;

            fastcgi_pass 127.0.0.1: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;

        }

    }

}

2. Supervisor Configuration ( docker/supervisord.conf )

Supervisor is a process manager that will start and keep both Nginx and PHP-FPM running inside the single container.

[supervisord]

nodaemon=true


[program:php-fpm]

command=/usr/local/sbin/php-fpm --fpm-config /usr/local/etc/php-fpm.d/www.conf -R

autostart=true

autorestart=true


[program:nginx]

command=/usr/sbin/nginx -g "daemon off;"

autostart=true

autorestart=true

### How It Speeds Up Your GitLab CI/CD Pipeline

1. Caching Composer Dependencies : The composer install step only runs if you change composer.json or composer.lock . In your GitLab CI pipeline, Docker will reuse the cached layer from the previous build, saving several minutes.

2. Caching NPM Dependencies : Similarly, the npm install and npm run build steps are only executed if you change your frontend dependencies or source files. This is another significant time saver.

3. Fast Rebuilds for Code Changes : When you push a change to a PHP file (e.g., a controller or model), only the final COPY . /var/www/html layer and subsequent layers are rebuilt. Since the heavy dependency installation is already cached, this rebuild process is incredibly fast—often just a few seconds.

To make this work effectively in GitLab CI, ensure you have enabled Docker layer caching in your runner's configuration. This allows the runner to reuse layers from previous pipeline runs.

Comments

Popular posts from this blog

TCPDF How to show/display Chinese Character?

How to fix fancy box/Easy Fancybox scroll not work in mobile

Wordpress Load balancing: 2 web servers 1 MySQL without any Cloud services