Dockerize Laravel App: Step-by-step Guide

Summary:
Create Dockerfile and docker-compose for Laravel app.


Dockerizing your Laravel application enables consistent development environments, effortless deployments, and simplified scalability. Whether you’re deploying to the cloud or simply seeking to standardize your team’s development stack, containerizing Laravel is a wise move. In this step-by-step tutorial, we’ll cover how to create a Dockerfile and a docker-compose.yml file tailored for a modern Laravel application.


Table of Contents

  1. Prerequisites
  2. Laravel Project Structure
  3. Create Dockerfile
  4. Create docker-compose.yml
  5. Adding Environment Variables
  6. Build and Run Containers
  7. Extra: Dockerizing Queue Workers & Scheduler
  8. Conclusion

Prerequisites

  • Basic understanding of Docker and Laravel
  • Docker and Docker Compose installed (Installation Guide)
  • A Laravel application (or create one with composer create-project laravel/laravel example-app)

Laravel Project Structure

Here’s a typical Laravel folder structure (trimmed for clarity):

laravel-app/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── artisan
├── composer.json
├── package.json
└── ...

After you place your Laravel project inside a directory (e.g., laravel-app), you’ll be working from this location.


Create Dockerfile

The Dockerfile defines the application runtime environment for Laravel. Here’s a standard Dockerfile using PHP 8.2, Composer, and Node.js for asset compilation with npm.

# Dockerfile

FROM php:8.2-fpm

# Install system dependencies and PHP extensions
RUN apt-get update && apt-get install -y \
    git curl libpng-dev libonig-dev libxml2-dev zip unzip npm \
    && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

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

# Set working directory
WORKDIR /var/www

# Copy application files
COPY . .

# Install dependencies
RUN composer install --optimize-autoloader --no-dev
RUN npm install && npm run build

# Copy existing application key, storage and vendor folder permissions
RUN chown -R www-data:www-data /var/www \
    && chmod -R 755 /var/www/storage

EXPOSE 9000

CMD ["php-fpm"]

Explanation

  • FROM php:8.2-fpm: Uses the official PHP FPM image as base.
  • Install dependencies: Adds required libraries and PHP extensions.
  • Install Composer: Pulls Composer from its image.
  • WORKDIR: Sets /var/www as the working directory.
  • COPY . .: Copies all app files into the container.
  • Install dependencies: Runs composer install and npm install && npm run build.
  • Permissions: Properly sets permissions for the storage directory.
  • EXPOSE: Exposes port 9000 (default for PHP-FPM).

Create docker-compose.yml

docker-compose.yml orchestrates multiple services — PHP (your app), MySQL, and Nginx.

# docker-compose.yml

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: laravel-app
    container_name: laravel-app
    restart: unless-stopped
    volumes:
      - ./:/var/www
    environment:
      - DB_HOST=db
      - DB_DATABASE=laravel
      - DB_USERNAME=laravel
      - DB_PASSWORD=secret
    depends_on:
      - db

  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www
      - ./docker/nginx/conf.d/:/etc/nginx/conf.d/
    depends_on:
      - app

  db:
    image: mysql:8.0
    container_name: mysql
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: root
    ports:
      - "3306:3306"
    volumes:
      - dbdata:/var/lib/mysql

volumes:
  dbdata:

Nginx Configuration

Create a folder docker/nginx/conf.d and add a file called default.conf:

# docker/nginx/conf.d/default.conf

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

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

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /var/www/public$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

Adding Environment Variables

Copy .env.example to .env:

cp .env.example .env

In your .env file, set the database credentials to match docker-compose.yml:

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret

Generate the application key:

docker-compose run --rm app php artisan key:generate

Build and Run Containers

  1. Build and run all services:

    docker-compose up --build -d
    
  2. Access your app at http://localhost:8000.

  3. Run migrations:

    docker-compose exec app php artisan migrate
    
  4. View logs:

    docker-compose logs -f app
    

Stop and remove containers:

docker-compose down

Extra: Dockerizing Queue Workers & Scheduler

If your app uses queues or scheduled jobs, add worker and scheduler services to docker-compose.yml:

  queue:
    build:
      context: .
      dockerfile: Dockerfile
    command: php artisan queue:work
    volumes:
      - ./:/var/www
    depends_on:
      - db
      - app

  scheduler:
    build:
      context: .
      dockerfile: Dockerfile
    command: php artisan schedule:work
    volumes:
      - ./:/var/www
    depends_on:
      - db
      - app

Conclusion

Dockerizing your Laravel application simplifies setup, boosts portability, and streamlines deployment. With a custom Dockerfile and docker-compose.yml, your development and production environments become consistent and easy to manage. Expand your setup by adding cache and session stores, custom worker images, or secure secrets management as needed.

Happy shipping with Docker and Laravel!