Dockerizing a Laravel app

Apr 8, 2025

Dockerizing a Laravel app

# Introduction

Docker is a powerful tool that allows developers to package applications and their dependencies into isolated containers, ensuring consistent environments across different stages of development, testing, and production. By Dockerizing a Laravel application, you can ensure that the app runs consistently across various environments, making it easier to develop, test, and deploy.

This guide will walk you through the steps needed to Dockerize a Laravel application, including setting up a Dockerfile, creating a docker-compose.yml file, configuring Nginx as a web server, and running the containers for your application. By the end of this guide, you’ll have a fully containerized Laravel application ready for local development or production deployment.

# Prerequisites

Before proceeding, ensure you have the following:

  • Docker installed on your machine (you can check here for installation instructions).
  • A working Laravel application.

# Step 1: Create a Dockerfile

First, create a Dockerfile in the root of your Laravel project. This file will contain instructions on how to build the Laravel app container.

FROM php:8.3.11-fpm

# Update package list and install dependencies
RUN apt-get update && apt-get install -y \
    libzip-dev \
    libpng-dev \
    postgresql-client \
    libpq-dev \
    nodejs \
    npm \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

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

ENV COMPOSER_ALLOW_SUPERUSER=1

# Install required packages
RUN docker-php-ext-install pdo pgsql pdo_pgsql gd bcmath zip \
    && pecl install redis \
    && docker-php-ext-enable redis

WORKDIR /usr/share/nginx/html/

# Copy the codebase
COPY . ./

# Run composer install for production and give permissions
RUN sed 's_@php artisan package:discover_/bin/true_;' -i composer.json \
    && composer install --ignore-platform-req=php --no-dev --optimize-autoloader \
    && composer clear-cache \
    && php artisan package:discover --ansi \
    && chmod -R 775 storage \
    && chown -R www-data:www-data storage \
    && mkdir -p storage/framework/sessions storage/framework/views storage/framework/cache

# Copy entrypoint
COPY ./scripts/php-fpm-entrypoint /usr/local/bin/php-entrypoint

# Give permisisons to everything in bin/
RUN chmod a+x /usr/local/bin/*

ENTRYPOINT ["/usr/local/bin/php-entrypoint"]

CMD ["php-fpm"]

# Step 2: Create a Docker Compose File

Next, create a docker-compose.yml file in the root of your project. This file defines the services that make up your Laravel app — Postgres, Nginx, Redis and a queue worker.

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - 9000:9000
    depends_on:
      - db
    env_file:
      - .env
    volumes:
      - storage:/usr/share/nginx/html/storage:rw
      - public:/usr/share/nginx/html/public:rw
  queue-worker:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    command: php artisan queue:work
    environment:
      IS_WORKER: "true"
    env_file:
      - .env
    depends_on:
      - db
      - redis
    volumes:
      - storage:/usr/share/nginx/html/storage:rw
      - public:/usr/share/nginx/html/public:rw
  nginx:
    image: nginx:1-alpine
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./nginx.conf:/etc/nginx/templates/default.conf.template
      - storage:/usr/share/nginx/html/storage:rw
      - public:/usr/share/nginx/html/public:ro
  db:
    image: bitnami/postgresql:16.3.0
    platform: linux/amd64
    ports:
      - 5432:5432
    restart: always
    volumes:
      - db-data:/bitnami/postgresql
    environment:
      - POSTGRESQL_DATABASE=${POSTGRESQL_DATABASE}
      - POSTGRESQL_USERNAME=${POSTGRESQL_USERNAME}
      - POSTGRESQL_PASSWORD=${POSTGRESQL_PASSWORD}
  redis:
    image: bitnami/redis:7.2
    platform: linux/amd64
    ports:
      - 6379:6379
    restart: always
    volumes:
      - redis-data:/bitnami/redis/data
    environment:
      - ALLOW_EMPTY_PASSWORD=no
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL

volumes:
  storage:
  public:
  db-data:
  redis-data:

# Step 3: Configure Nginx

Create a folder named nginx in the root of your Laravel project. Inside it, create a file named nginx.conf with the following content:

server {
    listen       80;
    server_name  localhost;
    root         /usr/share/nginx/html/public;

    access_log /dev/stdout;
    error_log  /dev/stderr error;

    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location /storage/ {
        alias /usr/share/nginx/html/storage/app/public/;
        access_log off;
        expires max;
        add_header Cache-Control "public";
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi.conf;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

# Step 4: Create entry point script

#!/bin/bash

main() {
    if [ "$IS_WORKER" = "true" ]; then
        exec "$@"
    else
        prepare_file_permissions
        run_npm_build
        prepare_storage
        wait_for_db
        run_migrations
        optimize_app
        run_server "$@"
    fi
}

prepare_file_permissions() {
    chmod a+x ./artisan
}

run_npm_build() {
    echo "Installing NPM dependencies"
    if [ -f "package.json" ]; then
        echo "Running NPM clean install"
        npm ci

        echo "Running NPM build"
        npm run build
    else
        echo "No package.json found, skipping NPM build"
    fi
}

prepare_storage() {
    # Create required directories for Laravel
    mkdir -p /usr/share/nginx/html/storage/framework/cache/data
    mkdir -p /usr/share/nginx/html/storage/framework/sessions
    mkdir -p /usr/share/nginx/html/storage/framework/views

    # Set permissions for the storage directory
    chown -R www-data:www-data /usr/share/nginx/html/storage
    chmod -R 775 /usr/share/nginx/html/storage

    # Ensure the symlink exists
    php artisan storage:link
}

wait_for_db() {
    echo "Waiting for DB to be ready"
    until ./artisan migrate:status 2>&1 | grep -q -E "(Migration table not found|Migration name)"; do
        sleep 1
    done
}

run_migrations() {
    ./artisan migrate
}

optimize_app() {
    ./artisan optimize:clear
    ./artisan optimize
}

run_server() {
    exec /usr/local/bin/docker-php-entrypoint "$@"
}

main "$@"

# Step 5: Check your .env

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

# Step 6: Build and run the containers

docker-compose up --build -detach

# Step 7: Run Laravel Commands Inside the Container

Once the containers are running, you can interact with your Laravel app like normal.

docker-compose exec app php artisan migrate

# Bring Containers Down

To stop and remove the containers:

docker-compose down

Or, to stop and remove containers, networks, and volumes:

docker-compose down --volumes
LaravelDockerDocker composeNginxPostgresRedis