How to Dockerize a Laravel 11 and React 18 Application for Development
Introduction
Docker has become a popular choice for development environments due to its ability to containerize applications and simplify deployment across different systems. This guide will walk you through dockerizing a Laravel 11 backend and React 18 frontend application, using Docker Compose to orchestrate multiple services. We wil assume that your laravel and react apps live in seperate folders and will be served through different domain. Suffice to say that the react app will not be served from the laravel backend but will use the laravel backend only as an api service.
Docker Compose Configuration
First, create a docker-compose.yml
file in your project’s root directory:
version: '3'
services:
app:
build:
context: ./laravel-11-backend
dockerfile: Dockerfile
ports:
- "8000:9000"
volumes:
- ./laravel-11-backend:/var/www/html
- ./laravel-11-backend/storage:/var/www/html/storage
- ./laravel-11-backend/bootstrap/cache:/var/www/html/bootstrap/cache
networks:
- laravel-network
depends_on:
- mysql
environment:
- DB_HOST=mysql
mysql:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_DATABASE: laravel_react_blog_user_permission
MYSQL_USER: app
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: vicecity
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- laravel-network
nginx:
build:
context: ./nginx
ports:
- "80:80"
volumes:
- ./laravel-11-backend:/var/www/html
networks:
- laravel-network
depends_on:
- app
frontend:
build:
context: ./react-18-frontend
dockerfile: Dockerfile
ports:
- "5173:5173"
volumes:
- ./react-18-frontend:/usr/src/app
- /usr/src/app/node_modules
networks:
- laravel-network
depends_on:
- app
networks:
laravel-network:
driver: bridge
volumes:
mysql_data:
Then in the root folder where the docker-compose.yml
file is, create three folders if they dont exist already.laravel-11-backend
for your laravel folder. react-18-frontend
for the react app and nginx
for the nginx files.
Breakdown of docker-compose.yml
:
app
Service (Laravel Backend):Builds from
./laravel-11-backend
usingDockerfile
. Exposes port9000
internally, mapped to8000
externally on the local machine. Mounts Laravel directories for code and storage. Depends on mysql service and connects to by settingDB_HOST
environment variable.mysql
Service:Uses MySQL 8.0 image with predefined environment variables for database setup. Exposes port
3306
for database connections. Persists MySQL data tomysql_data
volume.nginx
Service:Builds from ./nginx folder using its Dockerfile. Forwards port 80 to serve Laravel’s public directory. Mounts Laravel directory to serve PHP files through PHP-FPM.
frontend
Service (React Frontend):Builds from ./react-18-frontend using Dockerfile. Exposes port 5173 for React development server. Mounts React source code and node_modules for live updates.
Networks and Volumes:
Defines a laravel-network for communication between services. Creates mysql_data volume for MySQL data persistence.
Dockerfiles and Configuration Files
Here are the Dockerfiles and configuration files used in the setup:
- Laravel Backend Dockerfile (laravel-11-backend/Dockerfile):
FROM php:8.2-fpm
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
zip \
unzip \
libonig-dev \
libxml2-dev \
netcat-openbsd \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd pdo pdo_mysql mbstring exif pcntl bcmath xml
# Install Composer
COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www/html
# Copy existing application directory contents
COPY . .
# Copy entrypoint script
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Install Laravel dependencies
RUN composer install --optimize-autoloader --no-dev
# Expose port 9000
EXPOSE 9000
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Breakdown of Laravel Backend Dockerfile:
Base Image:
php:8.2-fpm
Uses PHP 8.2 with FPM (FastCGI Process Manager) for handling PHP requests efficiently.
Dependencies Installation:
Installs essential packages like
git
,curl
,zip
,unzip
, etc., necessary for Laravel and Composer operations. Installs specific PHP extensions (gd
,pdo_mysql
,mbstring
, etc.) required by Laravel for image processing, database connectivity, and string manipulation.Composer Installation:
Copies Composer from the Composer image (
composer:2.2
) into the PHP image, making it globally accessible. Allows dependency management and package installation within the container.Working Directory and Application Setup:
Sets the working directory within the container to
/var/www/html
, the standard directory for web applications in many PHP environments. Copies all application files into the container, including Laravel’s codebase.Permissions and Laravel Setup:
Adjusts file ownership (
www-data:www-data
) to ensure Laravel can read and write necessary files. Installs Laravel dependencies using Composer, optimizing the autoloader and excluding development packages (--optimize-autoloader --no-dev
).Port Exposition:
Exposes port
9000
to allow PHP-FPM to communicate with Nginx for processing PHP scripts.Entrypoint Script:
Copies an entrypoint script (
entrypoint.sh
) that waits for MySQL to be ready before executing commands like database migrations and seeding. Executes PHP-FPM to serve PHP applications.
- Nginx Dockerfile (nginx/Dockerfile):
FROM nginx:1.21.6
# Copy the Nginx configuration file
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
Breakdown of Nginx Dockerfile:
Base Image:
nginx:1.21.6
Uses the official Nginx image version
1.21.6
from Docker Hub as the base.Custom Configuration:
Copies a custom Nginx configuration file (
nginx.conf
) to replace the default configuration. Configures Nginx to serve the Laravel application located in/var/www/html/public
, directing traffic to PHP-FPM for PHP script processing.Port Exposition:
Exposes port
80
to allow external HTTP traffic to access the web application served by Nginx.
- Nginx Configuration (nginx/nginx.conf):
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
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;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
}
- React Frontend Dockerfile (react-18-frontend/Dockerfile):
FROM node:18-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev"]
Breakdown of React Frontend Dockerfile:
Base Image:
node:18-alpine
Uses Node.js version
18
based on Alpine Linux, a lightweight and secure base image for Node.js applications.Working Directory and Dependencies:
Sets the working directory within the container to
/usr/src/app
, a common practice for Node.js applications. Copiespackage.json
andpackage-lock.json
to install dependencies separately from application code to leverage Docker’s layer caching.Dependency Installation:
Runs
npm install
to install all dependencies listed inpackage.json
, optimizing for Docker builds.Application Setup:
Copies all application code (
COPY . .
) into the container after installing dependencies. This step ensures that changes in the application code trigger rebuilds of Docker layers that depend on it, speeding up development workflows.Port Exposition:
Exposes port
5173
, which is typically used by React’s development server (npm run dev
), allowing external access during development.Command to Run Application:
Specifies the default command (
CMD
) to start the application usingnpm run dev
, which runs the React development server.
Vite Configuration
If you happen to be usng vite
for building your react app, make sure your Vite configuration (vite.config.js) is set up to allow the server to be accessed from outside the container. Update your vite.config.js as follows:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
host: true, // This allows Vite to accept connections from outside the container
port: 5173, // The port Vite will run on
strictPort: true, // If the port is already used, Vite will fail instead of picking another port
watch: {
usePolling: true, // This is needed to work well inside Docker
},
},
})
Conclusion
Each Dockerfile serves a crucial role in containerizing different components of your application stack. The Laravel backend Dockerfile configures PHP and Composer dependencies, the Nginx Dockerfile customizes Nginx for serving PHP applications, and the React frontend Dockerfile sets up Node.js for developing and serving React applications. Together with Docker Compose, these files orchestrate a unified development environment, ensuring consistency across different machines and simplifying the setup for your Laravel 11 and React 18 application development.
This setup ensures that your development environment closely mirrors your production setup, facilitating smoother deployments and collaboration.