How to Set Up Nginx, MariaDB, and PHP with Docker Compose

This guide shows you how to set up your LEMP (Nginx, MariaDB, PHP) stack easily and quickly using Docker Compose.

When it comes to application development, containers are now the standard approach. They are popular among software developers due to their flexibility and portability.

The LEMP stack is an open-source solution used to develop web applications. It is an acronym representing Linux, Nginx, MySQL/MariaDB, and PHP. In addition, it is widely popular and well-supported among the Open Source community.

However, installing and configuring all of these services takes time and skills. This is where Docker containers, specifically Docker Compose, come in.

With its help, you can have your LEMP stack up and running in minutes and focus entirely on developing web apps without wasting time installing and configuring services.

This guide will show you how to quickly and easily integrate Nginx, MariaDB, and PHP using Docker Compose. In addition, we’ll add the popular web-based application phpMyAdmin to the LEMP stack to manage your MySQL/MariaDB database.

So without further ado, let’s get to work.

Prerequisites

Of course, having Docker and Docker Compose installed is required to deploy a containerized LEMP stack. So, if you don’t already have Docker installed, any of the following guides may be helpful:

The other essential component is Docker Compose. Remember, it is provided separately from Docker. Therefore, you need to Install Docker before adding Docker Compose; otherwise, Compose will not function.

Fortunately, even if it is not already on your Linux system, it is pretty simple to install. Type the following commands:

sudo curl -L "https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Next, run the command below to ensure that Docker Compose is installed and functioning correctly. You should receive a response similar to the one shown.

docker-compose version
Installing Docker Compose on Linux

Everything seems fine, so let’s get to the actual part.

Set Up Nginx, MariaDB, and PHP with Docker Compose

Docker Compose allows you to build and run stacks of multiple containers. In other words, it is a tool for defining and running multi-container Docker applications.

To use it, you must first create a “docker-compose.yml” file that configures the containers for your application. Then, you can use a single command such as “docker-compose up” to start all the containers and link them together.

Let’s begin by creating the directory structure to house the files required to set up Nginx, MariaDB, and PHP with Docker Compose. The final version should look like the one shown below, and we’ll now explain what each file and directory represents.

LEMP directory structure

We use an arbitrary name for the main directory, such as “lemp.” It will store all the files required for the setup, so let’s create it.

mkdir lemp

Then we make two subdirectories inside it: “nginx-conf” and “php-files.”

mkdir lemp/{nginx-conf,php-files}

The “nginx-conf” directory will hold our modified Nginx web server main configuration file, which we will create later. Finally, the “php-files,” as the name implies, are intended to store the PHP scripts that the application we are developing is expected to execute.

It is important to note that we chose all of these directory names at arbitrary. But, of course, you are free to use different ones.

Let’s continue creating our master file, describing all of the services in our containerized LEMP stack. Create a file named “docker-compose.yml” inside the “lemp” directory with your preferred text editor:

cd lemp
vim docker-compose.yml

Add the following content to it:

version: '3.8'

# Services
services:

    # PHP Service
    php:
        build:
            dockerfile: php-dockerfile
        volumes:
            - './php-files:/var/www/html'
        depends_on:
            - mariadb

    # Nginx Service
    nginx:
        image: nginx:latest
        ports:
            - 80:80
        links:
            - 'php'
        volumes:
            - './php-files:/var/www/html'
            - './nginx-conf:/etc/nginx/conf.d'
        depends_on:
            - php

    # MariaDB Service
    mariadb:
        image: mariadb:10.9
        environment:
            MYSQL_ROOT_PASSWORD: your_password
        volumes:
            - mysqldata:/var/lib/mysql

    # phpMyAdmin Service
    phpmyadmin:
        image: phpmyadmin/phpmyadmin:latest
        ports:
            - 8080:80
        environment:
            PMA_HOST: mariadb
        depends_on:
            - mariadb

# Volumes
volumes:

  mysqldata:

Save and close the file. Don’t be worried if something in its contents is unclear to you. We’ll go through it in more detail below.

PHP Service

We’ll begin with PHP, the first service in our “docker-compose.yml” file. The key point here is the “build” directive, which states “dockerfile: php-dockerfile.”

As you can see, there is no path to a Docker image here. This is because we will be building our own, the instructions for which can be found in the “php-dockerfile” file.

You may be asking why we need to build our own Docker image for PHP instead of using one already available on Docker Hub. The answer is simple: the official image includes a limited set of preinstalled PHP extensions. Unfortunately, they are, in most cases, insufficient for the applications we aim to develop.

Therefore, we will create a new Docker image based on the official PHP 8.1 one, but additionally, we will compile and include some of the most often used PHP extensions, such as MySQL, GD, ZIP, and so on.

So, create a file named “php-dockerfile” in the “lemp” directory with your preferred text editor and add the following content to it:

vim php-dockerfile
FROM php:8.1-fpm

# Installing dependencies for the PHP modules
RUN apt-get update && \
    apt-get install -y zip libzip-dev libpng-dev

# Installing additional PHP modules
RUN docker-php-ext-install mysqli pdo pdo_mysql gd zip

Save and close the file. Of course, as you guessed, if you need more PHP extensions, you can include them in the file.

The following directive, “volumes,” specifies where the app will store our PHP files. As you know, Docker containers are temporary, and any data are expected to be lost when the container is deleted. Fortunately, the Docker volumes provide a mechanism to persist data created inside a container.

So, our “php-files” directory points to “/var/www/html” in the PHP container, which is where PHP looks for executable PHP scripts.

We’ll create an “index.php” file into it, which will just be used for testing.

vim php-files/index.php

Add the single line shown below, then save and exit the file.

<?php phpinfo(); ?>

The “depends_on” directive instructs our PHP container to wait until MariaDB has been started. We do this because we expect PHP to try to connect to the database; thus, it must first be operational.

Nginx Service

Things are considerably more straightforward here. We utilize the most recent Docker image and expose the container’s port 80 to port 80 of our local Linux system. The “links” directive is a way to connect the Nginx container to the one on the PHP. In other words, it allows you to define extra aliases by which a service is reachable from another service.

Again, we specify the “php-files” directory so that the Nginx server knows where to look for the requested files. The interesting part is in the second volume, we define “./nginx-conf:/etc/nginx/conf.d.” This is the Nginx web server’s main configuration file.

We do this since Nginx does not know how to work with PHP files by default. Therefore, we must first add the necessary configuration to add this capability.

So, inside the “nginx-conf” directory, create a file named “nginx.conf” and paste the following content into it:

vim nginx-conf/nginx.conf
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    server_name localhost;

    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~* \.php$ {
        fastcgi_pass php:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    }
}

Finally, save and exit the file.

Again, we tell the Nginx container to wait for the PHP to start first because it is dependent on it.

Furthermore, if you want to become more familiar with Nginx’s server blocks, our handy guide, “How to Create Nginx Virtual Host (Server Block),” will be helpful too.

MariaDB Service

Our MariaDB server’s Docker Compose configuration is simple. All that needs is to replace the “MYSQL_ROOT_PASSWORD” directive, “your_password,” with one of your choosing. This is the password for the MariaDB root account, as the name implies.

The “volumes” directive assures us that we will not lose the data in our database during container restarts. That’s all we need for our MariaDB container.

phpMyAdmin Service

The final container in our Docker Compose deployment is phpMyAdmin. Since it is not required for the LEMP stack to function, it greatly simplifies MariaDB administration, which is why we include it in our deployment.

We specify phpMyAdmin to be accessible on port 8080 because we have already exposed port 80 to the Nginx container on our local Linux system.

The “PMA_HOST: mariadb” directive tells the phpMyAdmin container which database to connect to, in this case, our MariaDB container.

Start and Run the LEMP Stack with Docker Compose

Finally, we are ready to run Nginx, MariaDB, PHP, and phpMyAdmin using Docker Compose. To do this, execute the below-given command from the “lemp” directory where our “docker-compose.yml” file is located.

Start and run the containers in the background:

docker-compose up -d

The images will start downloading, and during the process, the PHP Docker image will be recreated, including the additional modules.

The entire procedure will take a few minutes, depending on your internet connection speed. In the end, you should see a screen similar to the one below, informing you that all containers have been successfully started.

Run the LEMP Stack with Docker Compose

Of course, the delay before the actual start of the Nginx, MariaDB, PHP, and phpMyAdmin containers using Docker Compose will only be the first time because it is necessary to download the images from the Internet.

All subsequent runs will take only seconds since the Docker images will already be available locally on your Linux system.

You can now open your browser and navigate to http://localhost. The “index.php” file we prepared earlier in the “php-files” directory will greet you.

LEMP running via Docker Compose executing PHP script

You can also reach your containerized phpMyAdmin instance at http://localhost:8080.

phpMyAdmin running via Docker Compose

We can say that it’s time to congratulate yourself on a well-done job!

Finally, let us walk you through the essential Docker Compose commands for managing your containerized LEMP stack.

Basic Docker Compose Commands

The basic Docker Compose commands you can use in practice are shown below with examples. Remember to execute them from the directory containing the “docker-compose.yml” file.

List All Docker Containers

To view a list of all the containers that are currently running in your LEMP deployment, type:

docker-compose ps
List containers

Stop All Docker Containers

To stop all docker containers that are running in the background, use the command as shown below:

docker-compose stop
Stop all Docker containers using Docker Compose

Of course, you can stop just one of the containers in your LEMP stack rather than all of them. Run “docker-compose stop” followed by the service name to do so. Notice not the container name but the service name.

For example, to stop the Nginx container, execute:

docker-compose stop nginx
Stop a single Docker containers using Docker Compose

Similarly, you can rerun it again later by executing “docker-compose start nginx.”

Start All Docker Containers

From your project directory (“lemp”), start up your application by running the command as shown below:

docker-compose start
Start all stopped Docker containers using Docker Compose

View the Containers’ Logs

The docker-compose logs command displays log output from services. For example, run the command followed by the service name to view the Nginx container logs.

docker-compose logs nginx

Additionally, if you want to aggregate the logs of every container in your LEMP stack and track what’s happening in them in real-time, type:

docker-compose logs -f

Stop and Destroy Containers

The following command stops and removes containers and networks created by the “docker-compose up” command:

docker-compose down
Stop and destroy containers

Additionally, if you want to stop and remove containers as well as their associated Docker images, use the following command:

docker-compose down --rmi all
Stop and remove containers as well as their Docker images

Accessing the Docker Container

The last scenario we’ll present isn’t directly related to Docker Compose, but it comes in handy when we need to inspect what’s going on inside the container.

For example, let’s say we want to enter inside the Nginx container. First, run the following command to list all running Docker containers and locate the container ID of the target container in the “CONTAINER ID” column.

docker ps
List all running Docker containers

Now we know that the ID of the Nginx container is e11b19b64acb. You can now get into the container by accessing its shell as executing:

docker exec -it e11b19b64acb /bin/sh
Get into the container shell

Conclusion

This guide walks you through all the steps needed to set up and dockerize Nginx, MariaDB, and PHP using Docker Compose. This gives you great flexibility when developing web applications.

Check out the project’s official documentation if you want to learn more about Docker Compose.

We truly hope that we were helpful to you. Any comments or suggestions are welcome in the comments section below.

2 Comments

  1. Thanks Bobby for that tutorial, I did most of it without problem.

    Just at the moment of entering phpmyadmin, I could not get the credentials rights. I had to modify the docker-compose.yml putting
    MYSQL_USERNAME: root
    MYSQL_ROOT_PASSWORD: secret
    both in mariadb/environment and in phpmyadmin/environment.

    • Hey Olivier,

      First, thanks for reading Linuxiac!

      Is it possible that you ran some tests and then deleted the containers, but you forgot to remove the docker volume that stored the MySQL data?

      Because it is persistent and when you start the containers, MySQL has already set a root password, which it takes from the stored data in the volume. So regardless of changing the one available in the docker-compose file, the database continues to use the old one.

      After deleting the containers, you may always use “docker volume ls” to check the available volumes and “docker volume rm volume_name” to delete a single one.

      Of course, you can delete the containers along with their associated docker volumes using the “docker-compose down -v” command. But doing so results in losing all data previously stored in the database.

      Best,
      Bobby

Leave a Reply

Your email address will not be published.