Apr 8, 2025
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.
Before proceeding, ensure you have the following:
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"]
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:
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;
}
}
#!/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 "$@"
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=laravel
docker-compose up --build -detach
Once the containers are running, you can interact with your Laravel app like normal.
docker-compose exec app php artisan migrate
To stop and remove the containers:
docker-compose down
Or, to stop and remove containers, networks, and volumes:
docker-compose down --volumes