Docker for Laravel: Complete Beginner to Intermediate Guide

Master Docker for Laravel development with this complete beginner-to-intermediate guide. Learn to build, configure, and scale Laravel apps using Docker, Dockerfiles, and docker-compose.

Docker for Laravel: Complete Beginner to Intermediate Guide

What is Docker?

Docker is an open-source platform that allows you to develop, ship, and run applications inside lightweight, portable containers. It simplifies application deployment by packaging everything an application needs to run, including dependencies, into a single unit.

Containers vs. Virtual Machines (VMs)

Feature Containers (Docker) Virtual Machines (VMs)
Isolation Process-level isolation Full OS-level isolation
Size Lightweight (MBs) Heavy (GBs, includes OS)
Performance Faster startup (seconds) Slower startup (minutes)
Resource Usage Shares OS kernel, less RAM Requires a full OS, more RAM
Portability Runs the same everywhere Less portable, OS-dependent

Benefits of Using Docker

  • Portability: Runs consistently across environments.
  • Scalability: Easily scale apps with Compose or Kubernetes.
  • Isolation: Prevents conflicts between apps.
  • Efficiency: Uses fewer system resources than VMs.

Install Docker on Debian

Step 1: Install Required Packages

Update your package index and install the packages needed to securely add Docker's repository.

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

Step 2: Add Docker’s Official GPG Key

Create a directory for Docker’s GPG key and add it to verify package authenticity.

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Step 3: Set Up the Docker Repository

Add Docker’s official repository to your system’s sources list so you can install Docker from it.

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

Step 4: Update Package Index Again

Refresh the package index to include Docker packages from the newly added repository.

sudo apt update

Step 5: Install Docker Engine

Install Docker Engine, command-line tools, and essential plugins for building and managing containers.

Docker Engine is the core component of the Docker platform. It enables developers to build and run containers—lightweight, portable packages that bundle an application together with everything it needs to run, including code, runtime, libraries, and system tools. This containerization ensures that applications run consistently across different environments, simplifying development, testing, and deployment.

Docker Compose is a complementary tool used to define and manage multi-container Docker applications. Instead of starting containers individually, you can describe all your services (such as a web server, database, or cache) in a single docker-compose.yml file and launch them simultaneously with a single command. Docker Compose streamlines the orchestration of complex applications, making it especially useful for local development and testing.

To install Docker Engine along with the CLI and Docker Compose plugin, run the following command:

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

Step 6: Verify Installation

Check that Docker is installed correctly by verifying its version and running a test container. If the test container runs successfully, Docker is installed and ready to use.

sudo docker --version
sudo docker run hello-world

Dockerize a Laravel APP

laravel-cms/
├── app/                   # Laravel project (will be created by Docker)
├── docker/
│   ├── nginx/
│   │   └── default.conf   # NGINX config (you’ll create this)
│   └── php/
│       └── Dockerfile     # PHP + Composer environment
├── docker-compose.yml     # Orchestration file

docker-compose.yml

Create this file in your laravel-cms/ root:

services:
  app:
    build:
      context: ./docker/php
    container_name: laravel-app
    volumes:
      - ./app:/var/www/html
    depends_on:
      - db

  web:
    image: nginx:stable-alpine
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  db:
    image: mariadb:10.6
    container_name: mariadb
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
    volumes:
      - db_data:/var/lib/mysql

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    ports:
      - "8080:80"
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: root
    depends_on:
      - db

volumes:
  db_data:

Dockerfiles: Writing and Optimising Docker Images

What is a Dockerfile?

A Dockerfile is a script that defines how a Docker image is built.

Key Dockerfile Directives

Directive Description
FROM Base image (e.g., php:8.3-fpm)
COPY Copies files into image
WORKDIR Sets the working directory
RUN Executes shell commands
CMD Default container command
ENTRYPOINT Fixed command always run
EXPOSE Port the container listens on

laravel-cms/docker/php/Dockerfile

This builds the Laravel-ready PHP environment:

FROM php:8.3-fpm

# Install dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    libonig-dev \
    libxml2-dev \
    zip unzip git curl

# Install PHP extensions
RUN docker-php-ext-install pdo pdo_mysql mbstring exif pcntl bcmath gd

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

WORKDIR /var/www/html

docker/nginx/default.conf

This is the NGINX config for Laravel:

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$ {
        include fastcgi_params;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

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

Build and Start the Project

In your laravel-cms/ folder, run:

docker compose up -d --build

Then, enter the app container and create Laravel:

docker exec -it laravel-app bash
composer create-project laravel/laravel .
exit

Now visit:

http://localhost → Laravel app
http://localhost:8080 → phpMyAdmin

Host: db, User: root, Password: root

Note: Only use docker run when testing a single image quickly (e.g., trying out nginx:alpine alone). Other than that, inside the Laravel directory, you can write:

docker compose up -d       # Start all services
docker compose stop        # Stop all services
docker compose start       # Start stopped services
docker compose ps          # See status of services

Basic Docker Commands

Running and Stopping Containers

Run a container from a specific image (e.g., nginx, mysql, or your custom image). This pulls the image (if not already present) and starts a new container from it.

Stop a running container by its name or container ID, This gracefully shuts down the container.

Start a previously stopped container, This resumes the container without creating a new one.

docker run <image_name>
docker stop <container_name>
docker start <container_name>

Checking Running Containers

docker ps          # List running containers
docker ps -a       # List all containers (including stopped)

Executing Commands Inside a Container

docker exec -it <container_id> bash

Viewing Container Logs

docker logs <container_id>

Deleting Containers

docker rm <container_id>
docker rm -f <container_id>  # Force remove running container

Managing Images

docker pull nginx
docker build -t my-laravel-app.

Purpose Comparison

  Dockerfile docker-compose.yml
What it does Builds app image Orchestrates services (app, db, etc.)
Optimization Build time, caching Service dependencies, networks

Docker Networking

You're Using Docker Networking (Even Without Declaring It). When you run: docker compose up -d

Docker Compose automatically creates a default network for your project, usually named like: laravel-cms_default

Create a Custom Bridge Network

docker network create my-net
docker run --network my-net --name web nginx
docker run --network my-net --name app php

Built-in Network Drivers

Driver Use Case
bridge Default for single-host containers
host Shares host network stack
none Fully isolated
overlay Multi-host networking in Swarm

This default Compose network isolates your containers from other running containers on your system. This is good for security and predictability. To inspect your Compose network:

docker network ls # Look for something like laravel-cms_default
docker network inspect laravel-cms_default

Create and Use Custom Network

You can declare the network yourself like this (optional):

services:
  app:
    networks:
      - laravel
  web:
    networks:
      - laravel
  db:
    networks:
      - laravel
  phpmyadmin:
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

Docker Volumes: Persistent Data

Docker volumes are used to persist data. Unlike regular containers, volumes:

  • Survive container restarts/removal
  • Are managed by Docker
  • Are ideal for databases (e.g., MySQL, MariaDB)

Types of Volumes

Type Description
Named Docker-managed (good for DBs)
Bind Mount Maps a host path (ideal for source code)

Commands

docker volume create laravel-db
docker volume ls
docker volume rm laravel-db

Test Persistence

Try stopping and removing the container:

docker compose down

Then bring it back up:

docker compose up -d

Mounting Volumes

Part Meaning
docker run Runs a new Docker container.
-v laravel-db:/var/lib/mysql Mounts a named volume called laravel-db to the path /var/lib/mysql inside the container.
mariadb Uses the official MariaDB image from Docker Hub.
docker run -v laravel-db:/var/lib/mysql mariadb

# Bind mount (current directory) docker run -v $(pwd):/var/www my-laravel-app

Why Use /var/lib/mysql

That’s where MariaDB stores DB files. Named volume ensures data is kept even if the container is deleted.

In Practical

services:
  app:
    build:
      context: .
    volumes:
      - .:/var/www
      - laravel-storage:/var/www/storage

  mysql:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: laravel
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:
  laravel-storage:

Docker Compose

What is Docker Compose?

Docker Compose allows you to define and manage multi-container applications using a docker-compose.yml file. It simplifies launching, scaling, and connecting services (like Laravel, MySQL, NGINX) with one command.

Installing Docker Compose

Docker Desktop (Windows/macOS): Comes pre-installed.

Linux: It will be auto-installed if you are using Docker version 20.10+

Key Directives

Directive Purpose
services Define containers
build/image Build from Dockerfile or pull from registry
volumes Mount persistent or bind data
depends_on Control service start order
networks Internal communication between services

Docker with PHP/Laravel

Config Files

  • Dockerfile – Defines the PHP application environment.
  • docker-compose.yml – Orchestrates the services.
  • nginx/default.conf – Routes HTTP traffic to PHP.

Laravel in Docker

  • Use a Dockerfile and docker-compose.yml.
  • Services: PHP-FPM, MySQL, NGINX.
  • Configured with nginx/default.conf.

Running Artisan Commands

docker compose exec app php artisan migrate
docker compose exec app php artisan config:cache
docker compose exec app php artisan queue:work

Caching Composer Dependencies

Docker caches layers. If composer.json and composer.lock don’t change, Docker will reuse this layer without re-running composer install.

In Dockerfile:

COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --optimize-autoloader

Note: Placing composer steps early helps cache this layer and speed up builds.

Docker Registries

Docker registries are storage and distribution systems for Docker images. There are two main types:

Type Description
Public (e.g., Docker Hub) Share with the world or authenticated users
Private (e.g., self-hosted) Keep images internal, often for security or compliance

Using Docker Hub (Public or Private Repos)

Login First:

docker login

Tag Your Image

First, check if the image exists in your Docker. To check the docker images docker images

Assume your image is named laravel-cms:

docker tag laravel-cms your-dockerhub-username/my-laravel-app:latest
docker tag laravel-cms-app skarnov/laravel-cms-app:latest

Push the Image

docker push skarnov/laravel-cms-app:latest

Pull the Image

To pull this image on any system:

docker pull skarnov/laravel-cms-app:latest

Docker Swarm

What is Docker Swarm?

An orchestration tool to manage container clusters.

Feature Docker Swarm Kubernetes
Complexity Simple Complex
Setup Quick Longer
Learning Curve Lower Steeper
Integration Native with Docker Broad Ecosystem

Docker Swarm lets you manage a cluster of Docker engines as a single virtual Docker host. It adds features like:

  • Service replication
  • Load balancing
  • High availability

Initialize the Swarm

Run this on your main (manager) node: docker swarm init

Here is the modified version of your Compose file that is compatible with Docker Swarm:

docker-compose.yml

services:
  app:
    build:
      context: ./docker/php
    volumes:
      - ./app:/var/www/html
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
    depends_on:
      - db  # This is ignored in Swarm, kept here just for clarity

  web:
    image: nginx:stable-alpine
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  db:
    image: mariadb:10.6
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
    volumes:
      - db_data:/var/lib/mysql
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    ports:
      - "8080:80"
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: root
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

volumes:
  db_data:

Let's initialise Docker Swarm in our existing laravel-cms

docker build -t skarnov/laravel-cms-app:latest ./docker/php

Push it to Docker Hub

docker push skarnov/laravel-cms-app:latest

For Swarm production-ready orchestration Update docker-compose.yml: Replace this section:

app:
  build:
    context: ./docker/php

With:

app:
  image: skarnov/laravel-cms-app:latest

Redeploy the stack:

docker stack deploy -c docker-compose.yml laravel_stack