Dockerize Laravel: Nginx, MariaDB, PhpMyAdmin Setup

by Alex Johnson 52 views

Developing Laravel applications often requires a consistent and reproducible environment. This article outlines how to set up a Dockerized development environment for a Laravel application, incorporating Nginx, MariaDB, and phpMyAdmin. This setup ensures that developers have a standardized stack, reducing environment drift and setup friction across different machines.

Introduction to Dockerizing Laravel Applications

Dockerizing your Laravel application offers numerous advantages. By encapsulating your application and its dependencies into containers, you ensure consistency across development, testing, and production environments. This approach eliminates the common ā€œit works on my machineā€ problem and streamlines the deployment process. Using Docker, you can easily manage different versions of PHP, databases, and other services without conflicts. In this comprehensive guide, we'll walk you through setting up a complete Dockerized environment for your Laravel application, including Nginx, MariaDB, and phpMyAdmin. We will cover everything from configuring the docker-compose.yml file to setting up web server routing and optimizing the PHP environment. Additionally, we'll provide detailed documentation to help you get started, manage your daily workflow, and troubleshoot common issues. This setup not only simplifies development but also enhances collaboration among team members by providing a unified and reproducible development environment. By the end of this guide, you'll have a robust Dockerized environment that supports larger PHP memory and upload limits, preventing common development-time errors and ensuring a smooth development experience.

Prerequisites

Before diving into the setup, ensure you have the following prerequisites installed:

  • Docker: Install Docker Desktop from the official Docker website.
  • Docker Compose: Docker Compose is usually included with Docker Desktop. Verify the installation by running docker-compose version in your terminal.

Dockerized Stack Setup: docker-compose.yml Configuration

The core of our Dockerized environment lies in the docker-compose.yml file. This file defines the services needed for our Laravel application: app (PHP-FPM 8.3), web (Nginx), db (MariaDB 10.5), and optionally phpmyadmin.

Configuring the docker-compose.yml File

To begin, create a new file named docker-compose.yml in the root directory of your Laravel project. This file will define the services that make up your development environment. Here’s a detailed breakdown of how to configure each service:

1. App (PHP-FPM 8.3)

The app service will run PHP-FPM 8.3 and serve as the application container. It's responsible for executing PHP code and handling requests passed from the web server (Nginx). Define the app service in your docker-compose.yml as follows:

version: "3.8"
services:
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    container_name: app
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    ports:
      - "9000:9000"
    depends_on:
      - db
    environment:
      PHP_IDE_CONFIG: "serverName=Docker"
      XDEBUG_MODE: debug
      XDEBUG_CONFIG: "client_host=host.docker.internal client_port=9003"

Explanation:

  • build: Specifies the build context and Dockerfile used to build the image. The context is set to the current directory (.), and the Dockerfile is located at docker/app/Dockerfile.
  • container_name: Assigns the name app to the container, making it easier to reference.
  • working_dir: Sets the working directory inside the container to /var/www/, which is where your Laravel application code will reside.
  • volumes: Mounts the project source code into the container. The ./:/var/www mapping ensures that any changes made to the code on your host machine are immediately reflected in the container.
  • ports: Exposes port 9000 on the container, which is the default port for PHP-FPM.
  • depends_on: Ensures that the db service is started before the app service.
  • environment: Sets environment variables for PHP, including configuration for Xdebug, which is used for debugging.

2. Web (Nginx)

The web service uses Nginx to serve the Laravel application. It routes HTTP requests to the app service for PHP processing. Define the web service as follows:

  web:
    build:
      context: .
      dockerfile: docker/web/Dockerfile
    container_name: web
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www
      - ./docker/web/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

Explanation:

  • build: Similar to the app service, this specifies the build context and Dockerfile for the Nginx image.
  • container_name: Assigns the name web to the container.
  • ports: Maps port 8000 on your host machine to port 80 on the container, allowing you to access the application via http://localhost:8000.
  • volumes: Mounts the project source code and the Nginx configuration file into the container.
  • depends_on: Ensures that the app service is running before starting Nginx.

3. DB (MariaDB 10.5)

The db service runs MariaDB 10.5 and provides the database for your Laravel application. Define the db service as follows:

  db:
    image: mariadb:10.5
    container_name: db
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: laravel

Explanation:

  • image: Specifies the MariaDB 10.5 image from Docker Hub.
  • container_name: Assigns the name db to the container.
  • ports: Maps port 3306 on your host machine to port 3306 on the container, allowing you to connect to the database using a client like phpMyAdmin or a database management tool.
  • volumes: Creates a named volume db_data to persist the database data across container restarts. This ensures that your data is not lost when the container is stopped or removed.
  • environment: Sets environment variables for the MariaDB instance, including the root password, database name, user, and password.

4. phpMyAdmin (Optional)

The phpmyadmin service provides a web interface for managing your MariaDB database. This is an optional but highly convenient service for development. Define the phpmyadmin service as follows:

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    ports:
      - "8080:80"
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
    depends_on:
      - db

Explanation:

  • image: Specifies the phpMyAdmin image from Docker Hub.
  • container_name: Assigns the name phpmyadmin to the container.
  • ports: Maps port 8080 on your host machine to port 80 on the container, allowing you to access phpMyAdmin via http://localhost:8080.
  • environment: Sets environment variables for phpMyAdmin, including the host and port of the MariaDB database.
  • depends_on: Ensures that the db service is running before starting phpMyAdmin.

Complete docker-compose.yml File

Here is the complete docker-compose.yml file for your reference:

version: "3.8"
services:
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    container_name: app
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    ports:
      - "9000:9000"
    depends_on:
      - db
    environment:
      PHP_IDE_CONFIG: "serverName=Docker"
      XDEBUG_MODE: debug
      XDEBUG_CONFIG: "client_host=host.docker.internal client_port=9003"

  web:
    build:
      context: .
      dockerfile: docker/web/Dockerfile
    container_name: web
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www
      - ./docker/web/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  db:
    image: mariadb:10.5
    container_name: db
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: laravel

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    ports:
      - "8080:80"
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
    depends_on:
      - db

volumes:
  db_data:

Web Server Routing: Nginx Configuration

Nginx needs to be configured to serve the Laravel application from the public directory and route PHP requests to the app container on port 9000. This is achieved through a custom Nginx configuration file.

Setting Up Nginx Configuration

Create a directory named docker/web in the root of your Laravel project. Inside this directory, create a file named nginx.conf with the following content:

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

Explanation:

  • listen 80: Listens for HTTP requests on port 80.
  • index index.php index.html: Specifies the default index files.
  • root /var/www/public: Sets the root directory to the public folder of your Laravel application.
  • location ~ \.php$: Configures how Nginx handles PHP files. It passes PHP requests to the app service on port 9000 using FastCGI.
  • try_files $uri $uri/ /index.php?$query_string: Attempts to serve static files first. If a file or directory is not found, it passes the request to index.php.

PHP Environment Tuning

To avoid common development-time errors, it’s essential to set appropriate PHP memory and upload limits. This can be done by creating a custom PHP configuration file.

Creating a Custom PHP Configuration

Create a directory named docker/app in the root of your Laravel project. Inside this directory, create a file named Dockerfile with the following content:

FROM php:8.3-fpm

# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/

# Set working directory
WORKDIR /var/www

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

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

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

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash www -g www

# Copy existing application source code
COPY . /var/www

# Change current user to www
USER www

# Run composer install
RUN composer install --ignore-platform-reqs

# Copy entrypoint.sh
COPY docker/app/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

EXPOSE 9000

CMD ["php-fpm"]

Next, create a file named entrypoint.sh inside the same docker/app directory with the following content:

#!/bin/sh

# Set PHP memory limit
echo "memory_limit = 256M" > /usr/local/etc/php/conf.d/custom.ini

# Set PHP upload limit
echo "upload_max_filesize = 32M" >> /usr/local/etc/php/conf.d/custom.ini
echo "post_max_size = 32M" >> /usr/local/etc/php/conf.d/custom.ini

# Execute PHP-FPM
exec "$@"

Make sure to give execute permissions to the entrypoint.sh file:

chmod +x docker/app/entrypoint.sh

Explanation:

  • The Dockerfile extends the php:8.3-fpm image and installs necessary PHP extensions and tools.
  • The entrypoint.sh script sets the PHP memory_limit, upload_max_filesize, and post_max_size in a custom .ini file. This ensures that PHP has enough memory and can handle larger file uploads.

Developer Documentation: docker-compose.md

Providing clear documentation is crucial for developers to quickly understand and use the Dockerized environment. Create a docker-compose.md file in the root of your project with the following sections:

Prerequisites

  • Docker: Ensure Docker Desktop is installed.
  • Docker Compose: Verify Docker Compose is installed.

Setup Steps

  1. Clone the repository.
  2. Navigate to the project directory: cd your-project.
  3. Run docker-compose up -d to build and start the containers.
  4. Run docker exec -it app composer install to install PHP dependencies.
  5. Generate the application key: docker exec -it app php artisan key:generate.
  6. Set up the database: docker exec -it app php artisan migrate.

Daily Usage

  • Start the containers: docker-compose up -d.
  • Stop the containers: docker-compose down.
  • Access the application: http://localhost:8000.
  • Access phpMyAdmin (if installed): http://localhost:8080.

Useful Commands

  • Enter the app container: docker exec -it app bash.
  • Run Artisan commands: docker exec -it app php artisan <command>.
  • View container logs: docker-compose logs -f <service> (e.g., docker-compose logs -f app).

Troubleshooting

  • Issue: Application not accessible.
    • Solution: Ensure Docker containers are running and ports are correctly mapped.
  • Issue: Database connection errors.
    • Solution: Verify database credentials and that the db container is running.
  • Issue: Composer errors.
    • Solution: Ensure Composer is installed and run composer install inside the app container.

Source Mounting and Persistence

Source mounting is configured in the docker-compose.yml file using volumes. This allows changes to the project source code on the host machine to be immediately reflected in the containers.

Configuring Volumes

The following lines in the docker-compose.yml file ensure source mounting:

volumes:
  - ./:/var/www

This mounts the current directory (.) on the host machine to the /var/www directory in the app and web containers. Database data persistence is achieved using a named volume:

volumes:
  db_data:/var/lib/mysql

This creates a named volume db_data that persists the MariaDB data across container restarts.

Conclusion

Setting up a Dockerized development environment for Laravel applications with Nginx, MariaDB, and phpMyAdmin ensures consistency, reproducibility, and ease of use. This setup reduces environment drift, simplifies setup friction, and provides a standardized stack for all developers. By following this guide, you can create a robust and efficient development environment for your Laravel projects.

For more information on Docker and its best practices, visit the official Docker documentation. Happy coding!