Docker containers - LAMP stack to host a site with SQL and PHP

Introduction
Example of a LAMP stack to host a website

Introduction

Docker is set of technogies that allows us to run applications. It uses images that are built according to docker-compose.yml. These images are deployed to containers, each including the software needed to run a web server, database, etc..

Dockerfile → (Build) → Image → (Run) → Container.

  • Dockerfile: contains a set of Docker instructions that installs and configures the software you specify.

  • Image: compiled Dockerfile. It saves you time from rebuilding the Dockerfile every time you need to run a container. The Docker registry has official images that you can use to build your own images. You can use many images to build your own.

  • Container: an image that is instantiated, a running environment, similar to an application that is executed (Notepad, for example).
    It can have one of the states: created, restarting, running, removing, paused, exited, or dead.
    You can ssh into it and run commands, as if it were a real environment. You can run multiple containers from the same image.

You can install it for linux as per this link.

Basics

To specify docker commands, we use the command docker with a verb. After installing docker, we can check it with docker version:

$ docker version
Client: Docker Engine - Community
 Version:           27.5.1
 API version:       1.47
 Go version:        go1.22.11
 Git commit:        9f9e405
 Built:             Wed Jan 22 13:41:17 2025
 OS/Arch:           linux/amd64
 Context:           default

To see a list of running containers for example, we use docker ps:

$ docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED       STATUS       PORTS                                       NAMES
85c98fc70491   my_project-nginx        "/docker-entrypoint.…"   2 hours ago   Up 2 hours   0.0.0.0:80->80/tcp, :::80->80/tcp           learn-ngnix
38507900c54c   my_project-phpmyadmin   "/docker-entrypoint.…"   2 hours ago   Up 2 hours   0.0.0.0:81->80/tcp, [::]:81->80/tcp         learn-phpmyadmin
01a2a2c56113   my_project-php          "docker-php-entrypoi…"   2 hours ago   Up 2 hours   9000/tcp                                    learn-php
26884c23deeb   my_project-mariadb      "docker-entrypoint.s…"   2 hours ago   Up 2 hours   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp   learn-mariadb

If there are no containers currently running, the list will be empty:

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

We can list all containers by using:

docker ps -a

We can get more information on a docker command by adding the --help parameter:

$ docker ps --help

Usage:  docker ps [OPTIONS]

List containers

Aliases:
  docker container ls, docker container list, docker container ps, docker ps

Options:
  -a, --all             Show all containers (default shows just running)
  -f, --filter filter   Filter output based on conditions provided

If we have built docker images, we can list them with docker image ls:

$ docker image ls
REPOSITORY              TAG       IMAGE ID       CREATED       SIZE
my_project-nginx        latest    5ee111c65203   4 hours ago   267MB
my_project-phpmyadmin   latest    4463e2b475b7   4 hours ago   589MB
my_project-php          latest    54e3df9f5159   4 hours ago   522MB
my_project-mariadb      latest    6a08ad620c9c   4 hours ago   402MB

Example of a LAMP stack to host a website

Files need are here Below we will set up multiple containers, each hosting its own software (web server, SQL server, PHP web scripting language, phpmyadmin to manage SQL databases and run queries).
We will have a main compose.yml file, in which we will specify four services, one for each piece of software specified above.
It is used to define services, networks, and volumes for a Docker application.
Each service will include its own dockerfile, for installing the software; it will keep the main compose.yml less cluttered.
The docker-compose.yml or compose.yml file is used to define and run multi-container Docker applications.
Let’s list the files we will be using. Create a folder, for example my_project. In it should be the files:

compose.yml
config.inc.php
Dockerfile.mariadb
Dockerfile.nginx
Dockerfile.php
Dockerfile.phpmyadmin

Create a subfolder in the my_project folder, name it html. In it we will be adding the web server config file, and some php files to test the server’s functionality.

index.php
mariadb.php
nginx.conf
test.php

Let’s analyse a section of compose.yml.

services:
  nginx:
    container_name: learn-ngnix
    build:
      context: .
      dockerfile: Dockerfile.nginx
    hostname: nginx-host
    ports:
      - "80:80" # host port:container port
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
      - mariadb
      - phpmyadmin

This section defines the service nginx. We:

  • define the container name with container_name
  • include the dockerfile located in the context folder (. means current folder); for addional software and configuration
  • give a name to the host in the running container via hostname
  • map container ports to ports on the host (host - the machine on which you installed docker; your laptop, for example).
  • define a volume in the volume section; a volume is a persistent data store for containers
  • add the depends_on section; the nginx service starts only after first starting the services it depends on (php, mariadb, phpmyadmin)

The next section in compose.yml is for the php web scripting language.
We add a volume section, meaning that the subfolder html will be mapped to the /var/www/html folder, where the web server expects by default the home page and other pages.

  php:
    container_name: learn-php
    build:
      context: .
      dockerfile: Dockerfile.php
    hostname: php-host
    volumes:
      - ./html:/var/www/html

The Dockerfile.nginx file installs the lastest nginx web server from the Docker registry (notice the FROM section).
In the RUN section it installs telnet and ping to test network connectivity from the container.

# Dockerfile.nginx
FROM nginx:latest

RUN apt-get update && \
    apt-get install -y iputils-ping telnet mc && \
    rm -rf /var/lib/apt/lists/*

For the SQL server we map its database folder /var/lib/mysql to a volume named db_data.
We also can add passwords in the environment section for SQL:

  mariadb:
    container_name: learn-mariadb
    build:
      context: .
      dockerfile: Dockerfile.mariadb
    hostname: mariadb-host
    ports:
      - "3306:3306"  # Expose MariaDB port
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: my_database
      MYSQL_USER: user
      MYSQL_PASSWORD: user_password
    volumes:
      - db_data:/var/lib/mysql

The Dockerfile.mariadb gets the latest SQL server from the Docker registry; it also installs the same software as for the nginx file:

# Dockerfile.mariadb
FROM mariadb:latest

RUN apt-get update && \
    apt-get install -y iputils-ping telnet mc && \
    rm -rf /var/lib/apt/lists/*

To build and run the images as containers, use the command below, -d parameter will return to the terminal.

docker compose up --build -d

To run the images, use docker compose up -d.
You can specify the -f parameter with the yaml file to instantiate containers from a different file.
Docker will look for files namedcompose.yml/.yaml, docker-compose.yml/.yaml by default.

$ docker compose -f compose.yml up -d
[+] Running 4/4
 ✔ Container learn-mariadb     Started                                                                                                                     0.4s
 ✔ Container learn-php         Started                                                                                                                     0.4s
 ✔ Container learn-phpmyadmin  Started                                                                                                                     0.6s
 ✔ Container learn-ngnix       Started

To stop the containers, use:

docker compose down

To list the running containers:

$ docker container ls
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                       NAMES
bfe463eae3cd   my_project-nginx        "/docker-entrypoint.…"   57 minutes ago   Up 57 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp           learn-ngnix
b88806ecfd10   my_project-phpmyadmin   "/docker-entrypoint.…"   57 minutes ago   Up 57 minutes   0.0.0.0:81->80/tcp, [::]:81->80/tcp         learn-phpmyadmin
30c10b0239f1   my_project-mariadb      "docker-entrypoint.s…"   57 minutes ago   Up 57 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp   learn-mariadb
4dc9e61ede95   my_project-php          "docker-php-entrypoi…"   57 minutes ago   Up 57 minutes   9000/tcp                                    learn-php

We can customise the columns:

$ docker container ls --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES              STATUS             PORTS
learn-ngnix        Up About an hour   0.0.0.0:80->80/tcp, :::80->80/tcp
learn-phpmyadmin   Up About an hour   0.0.0.0:81->80/tcp, [::]:81->80/tcp
learn-mariadb      Up About an hour   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp
learn-php          Up About an hour   9000/tcp

To connect to a container, use docker exec, the -it parameter runs an interactive session, bash shell is executed:

$ docker exec -it learn-mariadb bash
root@mariadb-host:/# ls
bin                boot  docker-entrypoint-initdb.d  home  lib.usr-is-merged  media  opt   root  sbin                srv  tmp  var
bin.usr-is-merged  dev   etc                         lib   lib64              mnt    proc  run   sbin.usr-is-merged  sys  usr

To see logs for a container, use docker logs:

$ docker logs learn-mariadb
2025-02-17 20:28:16+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.7.2+maria~ubu2404 started.
2025-02-17 20:28:17+00:00 [Warn] [Entrypoint]: /sys/fs/cgroup/et_cls:/
0:://memory.pressure not writable, functionality unavailable to MariaDB
2025-02-17 20:28:17+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2025-02-17 20:28:17+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.7.2+maria~ubu2404 started.
ls -las $(docker volume inspect my_project_db_data | grep Mountpoint | cut -d\" -f 4)