How to Set Up a LAMP Stack with Docker Compose

Learn how to quickly deploy a LAMP (Apache, MariaDB, PHP) stack using Docker Compose with our easy-to-follow guide.

If you’re looking to streamline the process of setting up an Apache, MariaDB, and PHP environment for your web development projects, you’re in the right place.

Docker Compose provides a convenient way to define and manage multi-container applications, allowing you to create, configure, and deploy your LAMP stack in minutes.

However, suppose you still prefer to take the traditional approach and install the LAMP stack from the native packages for the distro you are using. In that case, our tutorials for UbuntuDebianRocky Linux, and AlmaLinux will help you do it effortlessly.

By the end of this guide, you’ll have a fully functional LAMP stack running in a Docker environment, ready to power your web applications.

You’ll also gain a solid understanding of Docker Compose and its capabilities, empowering you to manage your containerized applications in the future efficiently. So let’s dive in!


Of course, having Docker and Docker Compose installed is required to deploy a containerized LAMP 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 must Install Docker before adding Docker Compose; otherwise, Compose will not function.

Installing it is pretty simple, even if it is not already on your Linux system. Type the following two commands:

sudo curl -L "$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-composeCode language: JavaScript (javascript)

Create Project’s Structure

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 -d” to start all the containers and link them together.

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

The LAMP deployment directory structure.
The LAMP deployment directory structure.

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

mkdir lamp

Then we make three subdirectories inside it: “apache_docker,” “php_docker,” and “www.”

mkdir lamp/{apache_docker,php_docker,www}
  • The “apache_docker” directory contains the file (Dockerfile) which builds the Apache container, as well as the additional configuration for the Apache server (“apache-vhost.conf“), providing the integration of the web server with PHP and adding a virtual host.
  • The “php_docker” directory contains the file “Dockerfile,” which builds the PHP container and adds additional modules.
  • Finally, the “www” directory is 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 these directory names arbitrarily. But, of course, you are free to use different ones.

Set Up Apache, MariaDB, PHP, and phpMyAdmin with Docker Compose

It’s time to create our deployment file describing all the services in our containerized LAMP stack. First, create a file named “docker-compose.yaml” inside the “lamp” directory with your preferred text editor:

cd lamp
vim docker-compose.yamlCode language: CSS (css)

Add the following content to it:

version: "3.8"

  # PHP Service
    build: './php_docker/'
      - ./www/:/var/www/html/

  # Apache Service
    build: './apache_docker/'
      - php
      - "80:80"
      - ./www/:/var/www/html/

  # MariaDB Service
    image: mariadb:10.11
      MYSQL_ROOT_PASSWORD: your_password
      - mysqldata:/var/lib/mysql

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

# Volumes
  mysqldata:Code language: PHP (php)

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.yaml” file. The key point here is the “build” directive, which points to the “php_docker” directory, where the default “Dockerfile” is located.

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 “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 limited 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.2 one, but additionally, we will compile and include some of the most often-used PHP extensions, such as MySQL, GD, ZIP, XML, and so on.

So, create a file named “Dockerfile” inside the “php_docker” directory with your preferred text editor and add the following content to it:

vim php_docker/Dockerfile
FROM php:8.2-fpm

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

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

# Cleaning APT cache
RUN apt cleanCode language: PHP (php)

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

The “volumes” directive from the “php” service part in “docker-compose.yaml” specifies where the app’s PHP files are. 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 local “www” directory points to “/var/www/html” inside the PHP container, where PHP will look for executable PHP scripts. In other words, we map a folder on the host filesystem (outside of the container context) to within the running container.

Next, let’s create an “index.php” file into it, which will just be used for testing.

vim www/index.php

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

<?php phpinfo(); ?>Code language: HTML, XML (xml)

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

Apache Service

Things are similar here. We use the most recent Docker image and expose the container’s port 80 to port 80 of our local Linux system.

Let’s first create our “apache-vhost.conf” file, which sets the root directory containing the PHP files for our application to be the container “/var/www/html,” which, as we already know, we have mapped to our local one “www.”

In addition, we load the modules we need in Apache so that the web server can proxy the requests, in this case, to the PHP container.

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

vim apache_docker/apache-vhost.conf
ServerName localhost

LoadModule deflate_module /usr/local/apache2/modules/
LoadModule proxy_module /usr/local/apache2/modules/
LoadModule proxy_fcgi_module /usr/local/apache2/modules/

<VirtualHost *:80>
    # Proxy .php requests to port 9000 of the PHP container
    ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php:9000/var/www/html/$1
    DocumentRoot /var/www/html/
    <Directory /var/www/html/>
        DirectoryIndex index.php
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    # Send Apache logs to stdout and stderr
    CustomLog /proc/self/fd/1 common
    ErrorLog /proc/self/fd/2
</VirtualHost>Code language: PHP (php)

Save and exit the file.

Now, again inside the “apache_docker” directory, let’s create the “Docker” file that will build our Apache image and import into it the “apache-vhost.conf” file we just created.

vim apache_docker/Dockerfile
FROM httpd:2.4-alpine

RUN apk update; apk upgrade;

# Copy Apache virtual host file to proxy .php requests to PHP container
COPY apache-vhost.conf /usr/local/apache2/conf/apache-vhost.conf
RUN echo "Include /usr/local/apache2/conf/apache-vhost.conf" \
    >> /usr/local/apache2/conf/httpd.confCode language: PHP (php)

Again, save and exit the file.

MariaDB Service

Our MariaDB server’s Docker Compose configuration is simple. All that needs to be done is replace the “MYSQL_ROOT_PASSWORD” directive, “your_password,” with the one you choose. As the name implies, this is the password for the MariaDB root account.

The “volumes” directive ensures 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 LAMP stack to function, it dramatically 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 Apache 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 LAMP Stack with Docker Compose

Finally, we can run Apache, MariaDB, PHP, and phpMyAdmin using Docker Compose. To do this, execute the below-given command from the “lamp” directory where our “docker-compose.yaml” file is located.

Start and run the containers in the background:

docker-compose up -d

The images will start downloading, and the PHP and Apache Docker images will be built during the process.

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.

Start and Run the LAMP Stack with Docker Compose
Start and Run the LAMP Stack with Docker Compose

The initial delay of the containers’ start will only be the first time because Docker must download images from the Internet and then build some of them. After that, all subsequent runs will take seconds since they will be already available locally on your Linux system.

Finally, you can open your browser and navigate to “http://localhost.” The “index.php” file we prepared in the “www” directory will greet you.

The Apache container with PHP support has been successfully started.
The Apache container with PHP support has been successfully started.

You can also reach your containerized phpMyAdmin instance at “http://localhost:8080” and use the username “root” and password that you set in the “docker-compose.yaml” file to connect to and manage the MariaDB server.

phpMyAdmin running as a container in the LAMP stack.
phpMyAdmin running as a container in the LAMP stack.

It’s time to congratulate yourself on a well-done job! So let’s quickly walk through the essential Docker Compose commands for managing your containerized LAMP 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.yaml” file.

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

docker-compose ps

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

docker-compose stop

Of course, you can stop just one of the containers in your LAMP 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 Apache container, execute the following:

docker-compose stop apache

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

Similarly, from your project directory (“lamp”), type the command below to start up your application:

docker-compose start

The “docker-compose logs” command displays log output from services. For example, to view the Apache container logs:

docker-compose logs apache

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

docker-compose logs -f

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

docker-compose down

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

In addition, if you want an in-depth introduction to Docker Compose, check out our comprehensive guide.


Congratulations! You have mastered quickly deploying Apache, MariaDB, and PHP using Docker Compose. With the knowledge and skills you have gained from our guide, you are well-equipped to streamline your web development workflow and accelerate your project deployment process.

The benefits of using Docker Compose for your LAMP stack deployment are numerous. First, it provides isolation and portability, ensuring your applications run consistently across different environments.

It also enables scalability and easy scaling of individual services within your stack, allowing you to handle increased traffic and demand seamlessly. Furthermore, the modular nature of Docker Compose makes it effortless to add or remove services from your stack as your application evolves.

However, if you decide to go with Nginx instead of Apache and choose the LEMP (Nginx, Apache, MySQL/MariaDB) stack, we’ve got you covered in our detailed guide.

We hope our guide has been valuable in demystifying deploying a LAMP stack using Docker Compose. Now, armed with this knowledge, you can confidently embark on your projects and easily deploy your applications.

Happy coding and happy deploying!

Bobby Borisov

Bobby Borisov

Bobby, an editor-in-chief at Linuxiac, is a Linux professional with over 20 years of experience. With a strong focus on Linux and open-source software, he has worked as a Senior Linux System Administrator, Software Developer, and DevOps Engineer for small and large multinational companies.

Think You're an Ubuntu Expert? Let's Find Out!

Put your knowledge to the test in our lightning-fast Ubuntu quiz!
Ten questions to challenge yourself to see if you're a Linux legend or just a penguin in the making.

1 / 10

Ubuntu is an ancient African word that means:

2 / 10

Who is the Ubuntu's founder?

3 / 10

What year was the first official Ubuntu release?

4 / 10

What does the Ubuntu logo symbolize?

5 / 10

What package format does Ubuntu use for installing software?

6 / 10

When are Ubuntu's LTS versions released?

7 / 10

What is Unity?

8 / 10

What are Ubuntu versions named after?

9 / 10

What's Ubuntu Core?

10 / 10

Which Ubuntu version is Snap introduced?

The average score is 68%