How To Run Laravel Docker Compose Ubuntu 22.04

Set up a complete, production-ready Laravel development environment with Docker Compose on Ubuntu 22.04. Containerize PHP-FPM, Nginx, MySQL, and Redis for consistent deployments.

Why Containerize Laravel Applications?

Containerizing a Laravel application transforms how teams develop, test, and deploy their PHP applications. Rather than managing system-level dependencies, developer machines, and production servers separately, Docker provides a unified abstraction layer that guarantees identical behavior everywhere. This approach eliminates the classic "works on my machine" problem while streamlining onboarding for new team members and simplifying CI/CD pipeline integration.

Our DevOps services team specializes in helping organizations transition their PHP applications to containerized infrastructure. Whether you're running a Laravel application or any other PHP framework, containerization provides the consistency and scalability modern applications demand.

The architecture we're building consists of multiple specialized containers working in concert: a PHP-FPM container to process PHP code, an Nginx container to handle HTTP requests and serve static assets, a MySQL container for data persistence, and Redis for caching and session management. This separation of concerns mirrors production-grade infrastructure patterns while remaining lightweight enough for local development.

Ubuntu 22.04 serves as an excellent host for Docker-based Laravel development due to its stability, extensive documentation, and native support for Docker's installation workflows. The Long-Term Support (LTS) status ensures that security updates remain available throughout the development lifecycle, making it a reliable foundation for hosting containerized applications.

Installing Docker on Ubuntu 22.04

Before containerizing Laravel applications, the host system must have Docker Engine and Docker Compose installed. The installation process on Ubuntu 22.04 involves adding Docker's official repository to ensure access to the latest stable versions while maintaining system integrity through package signing.

Begin by updating the existing package index and installing prerequisites for the repository:

sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release

Add Docker's GPG key and repository to ensure package authenticity:

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine, containerd, and Docker Compose plugin:

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Verify the installation by checking Docker version and ensuring the daemon runs correctly:

docker --version
docker compose version
sudo docker run hello-world

The Docker daemon requires sudo privileges for execution, which can be inconvenient during frequent development tasks. Add your user to the docker group to enable command execution without elevation:

sudo usermod -aG docker $USER
newgrp docker

After group membership changes, log out and back in for the new permissions to take effect.

Creating Laravel Project Structure

A well-organized project structure separates application code from Docker configuration, making the codebase maintainable and deployment-ready. The recommended structure places Docker-related files in a dedicated docker directory while keeping Laravel's standard directory structure intact.

Create a new Laravel project or navigate to an existing one:

composer create-project laravel/laravel /path/to/project
cd /path/to/project

Establish the Docker configuration directory hierarchy:

mkdir -p docker/php-fpm docker/nginx docker/mysql docker/common

The resulting structure organizes Docker configurations logically:

project/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── docker/
│ ├── php-fpm/
│ │ ├── Dockerfile
│ │ └── php.ini
│ ├── nginx/
│ │ ├── Dockerfile
│ │ └── default.conf
│ └── mysql/
│ └── my.cnf
├── docker-compose.yml
├── .env
└── ...

This organization allows different services to maintain their own configurations while keeping them version-controlled alongside the application code. The separation also facilitates environment-specific customization without duplicating entire configuration files.

PHP-FPM Container Configuration

The PHP-FPM container serves as the core of the Laravel application, processing PHP requests from the Nginx reverse proxy. Building a custom Dockerfile allows fine-tuning PHP extensions, installing dependencies, and configuring debugging tools specific to Laravel's requirements.

For teams working with web development projects, properly configured PHP containers are essential for maintaining consistent development environments across distributed teams. Our web development expertise ensures your PHP configuration follows industry best practices for performance and security.

Create the PHP-FPM Dockerfile:

FROM php:8.2-fpm-alpine

# Install system dependencies
RUN apk add --no-cache \
 curl \
 git \
 unzip \
 libzip-dev \
 mysql-client \
 nodejs \
 npm \
 && docker-php-ext-install \
 zip \
 pdo_mysql \
 pdo_pgsql \
 bcmath \
 gd \
 && rm -rf /var/cache/apk/*

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

# Set working directory
WORKDIR /var/www

# Copy application code
COPY . .

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

# Create non-root user for security
RUN addgroup -g 1000 -S www && \
 adduser -S www -u 1000 -G www -h /var/www -s /bin/sh
RUN chown -R www:www /var/www

USER www

EXPOSE 9000

CMD ["php-fpm"]

This Dockerfile uses Alpine Linux for a minimal image size while including all dependencies Laravel requires for operation. Add PHP configuration customization by creating docker/php-fpm/php.ini:

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120

The PHP-FPM container listens on port 9000 by default, which Nginx uses as its upstream server. This separation allows horizontal scaling of PHP workers while keeping Nginx as a single entry point for request handling.

Nginx Reverse Proxy Setup

Nginx acts as the front-facing web server, handling HTTPS termination, serving static assets, and forwarding PHP requests to the upstream PHP-FPM pool. Creating a dedicated Nginx configuration for Laravel ensures proper URL rewriting, caching policies, and security headers.

Create docker/nginx/default.conf:

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

 # Security headers
 add_header X-Frame-Options "SAMEORIGIN" always;
 add_header X-Content-Type-Options "nosniff" always;
 add_header X-XSS-Protection "1; mode=block" always;

 # Gzip compression
 gzip on;
 gzip_vary on;
 gzip_min_length 1024;
 gzip_proxied expired no-cache no-store private auth;
 gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;

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

 location ~ \.php$ {
 fastcgi_pass php:9000;
 fastcgi_index index.php;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 include fastcgi_params;
 fastcgi_read_timeout 300;
 }

 location ~ /\.ht {
 deny all;
 }

 location = /favicon.ico { access_log off; log_not_found off; }
 location = /robots.txt { access_log off; log_not_found off; }

 # Cache static assets
 location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
 expires 1y;
 add_header Cache-Control "public, immutable";
 }
}

The Nginx configuration implements several production-grade practices: security headers protect against common attack vectors, gzip compression reduces bandwidth usage and improves response times, and static asset caching through the expires directive decreases server load for frequently requested files.

Create the Nginx Dockerfile:

FROM nginx:alpine

COPY default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

MySQL Database Container

The MySQL container provides relational database services for Laravel's Eloquent ORM and migration system. Docker Compose manages container lifecycle and networking automatically, making database setup remarkably simple compared to traditional installation methods.

Add MySQL configuration in docker/mysql/my.cnf:

[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

The utf8mb4 character set supports full Unicode including emoji characters, which modern applications often require. This configuration eliminates character encoding issues that can cause data corruption in internationalized applications.

Orchestrating with Docker Compose

Docker Compose coordinates all containers, defining their relationships, networking, and environment configurations in a declarative YAML file. This file serves as infrastructure-as-code documentation that teams can version control and reproduce across environments.

Create docker-compose.yml at the project root:

version: '3.8'

services:
 nginx:
 build: ./docker/nginx
 ports:
 - "8080:80"
 volumes:
 - .:/var/www
 - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
 depends_on:
 - php
 networks:
 - laravel
 restart: unless-stopped

 php:
 build: ./docker/php-fpm
 volumes:
 - .:/var/www
 networks:
 - laravel
 restart: unless-stopped

 mysql:
 image: mysql:8.0
 environment:
 MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-secret}
 MYSQL_DATABASE: ${DB_DATABASE:-laravel}
 MYSQL_USER: ${DB_USERNAME:-laravel}
 MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
 volumes:
 - mysql_data:/var/lib/mysql
 - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
 networks:
 - laravel
 restart: unless-stopped

 redis:
 image: redis:alpine
 networks:
 - laravel
 restart: unless-stopped

networks:
 laravel:
 driver: bridge

volumes:
 mysql_data:

Key configuration patterns:

  • Environment Variables: Externalizing configuration to environment variables allows the same compose file to serve development, staging, and production environments without modification. The ${VAR:-default} syntax provides fallback values when variables aren't set.

  • Named Volumes: The mysql_data volume persists database data across container restarts and recreations. Without named volumes, data would be lost when containers are removed.

  • Dependency Ordering: The depends_on directive ensures Nginx waits for PHP-FPM to be ready before attempting connections, preventing race conditions during startup.

  • Network Isolation: The laravel bridge network provides DNS resolution between containers using service names, allowing PHP to connect to mysql and redis as hostnames.

  • Restart Policies: unless-stopped ensures containers restart automatically after system reboots or Docker daemon restarts, improving availability for development environments.

Launching the Environment

With all configurations in place, build and start the containerized Laravel environment. Docker Compose handles image building, network creation, and container startup in a single command:

docker compose build
docker compose up -d

The build command executes Dockerfile instructions, creating images for PHP-FPM and Nginx services. The -d flag runs containers in detached mode, allowing terminal access for subsequent commands.

Verify all services are running:

docker compose ps

Access the Laravel application by navigating to http://localhost:8080 in a browser. The welcome page indicates successful configuration, while API routes test database connectivity.

Running Artisan Commands:

docker compose exec php php artisan migrate:fresh --seed
docker compose exec php php artisan queue:work

The exec command runs commands in existing containers, using the configured working directory and environment variables automatically.

Container Security Best Practices

Container security requires attention at multiple layers: image construction, container configuration, and runtime behavior. Implementing security measures protects both the application and the host system from potential vulnerabilities.

Non-Root Container Execution: Running containers as non-root users prevents privilege escalation attacks. The Dockerfile creates a dedicated www user with UID 1000, ensuring consistent permissions across environments.

Image Scanning: Integrate container image scanning into CI/CD pipelines to identify vulnerabilities before deployment. Tools like Trivy, Snyk, or GitHub's native scanning can flag outdated packages and known CVEs.

Secrets Management: For production deployments, avoid embedding sensitive values in Dockerfiles or compose files. Use Docker Swarm secrets, external secret management services, or runtime environment injection:

services:
 mysql:
 image: mysql:8.0
 environment:
 MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
 secrets:
 - db_root_password

Network Segmentation: The bridge network isolates Laravel services from other containers while allowing necessary inter-service communication. Production deployments should consider overlay networks with encryption for multi-host setups.

Monitoring and Health Checks

Containerized applications require different monitoring approaches than traditional deployments. Understanding container logs, health checks, and resource utilization helps maintain application reliability. For teams implementing comprehensive container monitoring strategies, our DevOps services include observability setup and alerting configuration for production environments.

Health Checks: Docker Compose supports health checks that determine container readiness for dependent services:

services:
 php:
 build: ./docker/php-fpm
 healthcheck:
 test: ["CMD", "php-fpm-healthcheck"]
 interval: 30s
 timeout: 3s
 retries: 3
 start_period: 30s

Key Metrics to Monitor:

  • Container resource usage (CPU, memory, disk)
  • Application performance metrics (response times, queue length)
  • Database query performance and connection pool status
  • External service dependencies health

Logging Configuration: Configure Laravel to output JSON-formatted logs for easier parsing by log aggregation systems. Centralized logging with Docker logging drivers enables correlation across services.

Recommended Tools: Prometheus for metrics collection, Grafana for visualization, and Loki or ELK stack for log aggregation provide a complete observability stack for containerized Laravel applications.

Common Issues and Solutions

Day-to-day Laravel development with Docker involves troubleshooting common scenarios that differ slightly from traditional installations.

502 Bad Gateway: Typically indicates PHP-FPM isn't running or Nginx can't connect. Check PHP container logs for startup errors.

docker compose logs php

Connection Refused to MySQL: Verify DB_HOST matches the service name in Docker Compose. Ensure MySQL has finished initializing before application connections.

Permission Denied: File permission issues occur when host UID/GID doesn't match container users. Recreate containers after group membership changes.

Debugging Commands:

# View container logs
docker compose logs -f php
docker compose logs -f nginx

# Execute commands in running containers
docker compose exec php bash

# Check resource usage
docker stats

# Inspect container details
docker inspect container_name

Managing Dependencies:

docker compose exec php composer update
docker compose exec php npm install
docker compose exec mysql mysql -u laravel -p laravel
Production Deployment Considerations

Container Orchestration

Use Kubernetes or Docker Swarm for multi-host deployments, horizontal scaling, and automated failover.

CI/CD Integration

Docker Compose files integrate naturally with GitHub Actions, GitLab CI, and similar platforms for automated testing and deployment.

SSL/TLS Termination

Terminate HTTPS at the load balancer or reverse proxy. Let's Encrypt provides free certificates through automated ACME protocols.

Disaster Recovery

Configure backup procedures, automate volume snapshots, and establish recovery time objectives for production databases.

Common Questions

Need Help Containerizing Your Laravel Application?

Our DevOps team can help you build secure, scalable container infrastructure for your PHP applications. From initial setup to production-grade orchestration, we have the expertise to support your containerization journey.