Are you tired of hearing "it works on my machine" when your Laravel application breaks in production? Docker eliminates this frustration by packaging your entire application environment into consistent, portable containers that run identically across development, staging, and production systems.
You gain three critical advantages when you containerize your Laravel applications with Docker. First, consistency ensures your application behaves identically regardless of the underlying infrastructure. Second, portability allows you to deploy seamlessly across different cloud providers, local development environments, and production servers. Third, scalability becomes effortless as you can spin up multiple container instances to handle increased traffic without complex configuration management.
This guide walks you through creating a production-ready setup for Laravel containerization with Docker using Docker Compose. You'll learn to build optimized Docker images, configure web servers, orchestrate multiple services, and implement production-grade security practices. By the end, you'll have a scalable deployment strategy that eliminates environment inconsistencies and accelerates your development workflow.
Prerequisites
When you hire dedicated Laravel developers, you need to ensure that they have two essential components installed on your development machine. Docker provides the core containerization platform, while Docker Compose orchestrates multiple interconnected services.
You must have Docker Engine installed and running on your system. Download the appropriate version for your operating system from Docker's official website. Verify your installation by running docker --version in your terminal. You should see version information confirming successful installation.
Docker Compose handles multi-container applications and comes bundled with Docker Desktop installations. Check your installation with docker-compose --version. If you're using older Docker versions, you might need to install Docker Compose separately.
You also need a functional Laravel application to Dockerize Laravel application effectively. This guide assumes you have a Laravel project with standard directory structure, including composer.json, artisan commands, and environment configuration files. If you're starting fresh, create a new Laravel project using composer create-project laravel/laravel your-app-name.
Part 1: Creating the Dockerfile for Laravel
The Dockerfile serves as your application's blueprint, defining exactly how Docker builds your Laravel container image. This file contains step-by-step instructions that Docker follows to create a consistent, reproducible environment containing your application code, PHP runtime, and all necessary dependencies.
Your Dockerfile transforms your Laravel application into a portable container that runs identically across different environments. Each instruction in the Dockerfile creates a new layer in the final image, allowing Docker to cache and reuse layers efficiently during subsequent builds.
Here's a complete Dockerfile optimized for Laravel PHP-FPM Docker configurations:
FROM php:8.2-fpm
# Set working directory
WORKDIR /var/www/html
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
libzip-dev
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Copy application files
COPY . .
# Install PHP dependencies
RUN composer install --no-dev --optimize-autoloader
# Set proper permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html/storage
# Expose port 9000 for PHP-FPM
EXPOSE 9000
CMD ["php-fpm"]
Let's break down each command's purpose.
- The FROM php:8.2-fpm instruction establishes your base image using PHP 8.2 with FastCGI Process Manager, providing the PHP runtime environment your Laravel application requires.
- The WORKDIR /var/www/html command sets your container's working directory where all subsequent operations occur. This directory becomes your application's home inside the container.
- The RUN apt-get update block installs essential system dependencies your Laravel application needs, including Git for version control, image processing libraries, and compression tools. The cleanup commands at the end reduce your final image size by removing unnecessary package files.
- The RUN docker-php-ext-install command adds PHP extensions Laravel commonly requires, such as PDO for database connections, GD for image manipulation, and ZIP for file compression.
The Composer installation copies the latest Composer binary from the official Composer image, giving you access to PHP package management within your container.
Part 2: Adding a Web Server with Nginx
Your PHP-FPM container processes PHP code but cannot directly handle HTTP requests or serve static assets efficiently. Nginx acts as a reverse proxy, receiving incoming HTTP requests, serving static files directly, and forwarding PHP requests to your PHP-FPM container for processing.
This separation helps optimize Laravel app performance significantly when setting up Laravel with Nginx and Docker. Nginx excels at handling concurrent connections and serving static assets like CSS, JavaScript, and images, while PHP-FPM focuses exclusively on executing your Laravel application logic. The architecture also enhances security by adding an additional layer between external requests and your application code.
Create an nginx.conf file to configure your web server:
server {
listen 80;
index index.php index.html;
server_name localhost;
root /var/www/html/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app: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;
}
}
This configuration directs Nginx to listen on port 80 and serve files from your Laravel public directory. The try_files directive implements Laravel's routing system by attempting to serve requested files directly or falling back to index.php for dynamic routing.
The PHP location block handles all PHP file requests by forwarding them to your PHP-FPM container running on port 9000. The fastcgi_pass app:9000 directive connects to your Laravel container using Docker's internal networking.
Part 3: Orchestrating Services with Docker Compose
Docker Compose defines and orchestrates multiple interconnected services using a single YAML configuration file. Instead of manually creating and linking individual containers, you declare your entire application stack and let Docker Compose handle service creation, networking, and volume management.
Your containerized Laravel application requires three services: the PHP-FPM application container, an Nginx web server, and a database. Docker Compose automatically creates a shared network allowing these services to communicate securely.
Here's your complete Laravel Docker Compose setup configuration:
version: '3.8'
services:
app:
build: .
container_name: laravel_app
restart: unless-stopped
volumes:
- .:/var/www/html
- ./storage:/var/www/html/storage
networks:
- laravel_network
depends_on:
- db
nginx:
image: nginx:alpine
container_name: laravel_nginx
restart: unless-stopped
ports:
- "80:80"
volumes:
- .:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- laravel_network
depends_on:
- app
db:
image: mysql:8.0
container_name: laravel_mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: laravel
MYSQL_USER: laravel
MYSQL_PASSWORD: secret
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- laravel_network
volumes:
mysql_data:
networks:
laravel_network:
driver: bridge
The app service builds your Laravel container using the Dockerfile in your current directory. Volume mounts synchronize your local code with the container, enabling real-time development. The service connects to the laravel_network and depends on the database service starting first.
Your nginx service uses the official Alpine-based Nginx image for minimal resource usage. Port mapping exposes port 80 to your host system, making your application accessible via localhost. The configuration volume mount applies to your custom Nginx settings.
The db service runs MySQL 8.0 with environment variables configuring the database, user, and password. The named volume mysql_data persists your database data between container restarts, preventing data loss during development.
Part 4: Building and Running the Application
Docker Compose provides simple commands to build images, start services, and execute application-specific tasks. These commands handle complex orchestration automatically, allowing you to focus on development rather than infrastructure management.
Start by building your Docker images for your multi-container Laravel setup using this command:
docker-compose build
This command reads your docker-compose.yml file and builds the app service using your Dockerfile. Docker caches layers efficiently, so subsequent builds complete faster unless you modify your Dockerfile or application dependencies.
Launch all services in detached mode with:
docker-compose up -d
The -d flag runs containers in the background, freeing your terminal for other tasks. Docker Compose starts services in dependency order: database first, then your Laravel application, and finally Nginx.
Execute Laravel Artisan commands inside your running container:
docker-compose exec app php artisan migrate
docker-compose exec app php artisan config:cache
docker-compose exec app php artisan key:generate
The exec command runs commands inside specific containers without stopping them. You can run any Artisan command, Composer operations, or shell commands using this pattern.
Monitor your application logs with:
docker-compose logs -f
This command displays real-time logs from all services, helping you debug issues and monitor application behavior.
Part 5: Production Considerations for Scalability
A. How multi-stage builds optimize production images?
Multi-stage builds create smaller, more secure production images by separating build dependencies from runtime requirements. Your final production image contains only essential files and dependencies, reducing attack surface and deployment time.
Here's an optimized multi-stage Dockerfile for Laravel production Docker setup:
# Build stage
FROM php:8.2-fpm as build
WORKDIR /var/www/html
# Install build dependencies
RUN apt-get update && apt-get install -y \
git curl libpng-dev libonig-dev libxml2-dev zip unzip libzip-dev
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts
# Production stage
FROM php:8.2-fpm
WORKDIR /var/www/html
# Install only runtime dependencies
RUN apt-get update && apt-get install -y \
libpng16-16 libonig5 libxml2 libzip4 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
# Copy application and dependencies from build stage
COPY --from=build /var/www/html/vendor ./vendor
COPY . .
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html/storage
EXPOSE 9000
CMD ["php-fpm"]
B. Why should you use .dockerignore files?
A .dockerignore file prevents unnecessary files from being copied into your Docker image, reducing build time and final image size for Laravel scalable deployment. Create a .dockerignore file in your project root:
node_modules
.git
.env
storage/logs/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*
bootstrap/cache/*
.phpunit.result.cache
This configuration excludes development dependencies, version control files, cached data, and sensitive environment files from your container image.
C. How do you manage secrets securely in production?
Never include sensitive information like database passwords, API keys, or encryption keys directly in your Docker images. Use Docker secrets, environment variables, or external secret management services to inject sensitive data at runtime.
Configure your production docker-compose.yml to use environment files for Laravel CI/CD with Docker:
services:
app:
build: .
env_file:
- .env.production
environment:
- APP_ENV=production
- APP_DEBUG=false
Store your .env.production file securely outside your application directory and inject it during deployment. Use tools like HashiCorp Vault, AWS Secrets Manager, or Kubernetes secrets for enterprise deployments.
Conclusion
With this Laravel Docker tutorial, you can transform your deployment strategy from error-prone manual processes to consistent, scalable automation.
Key Takeaways
- Docker containers eliminate environment inconsistencies by packaging your entire application stack into portable, reproducible units
- Separating PHP-FPM and Nginx containers improves performance and security through specialized service responsibilities
- Docker Compose simplifies multi-container orchestration with declarative YAML configuration files
- Multi-stage builds reduce production image size and attack surface by excluding build dependencies from final images
- Proper secret management prevents sensitive data exposure in container images and logs
- Volume mounts and networks enable seamless communication between containerized services
Contact us to learn more about how we can help you leverage Docker for the scalable deployment of your Laravel application.
FAQ
Frequently Asked Questions
Didn’t find what you were looking for here?