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 apphttp://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