Deploy a Secure LEMP Stack on Docker

By Anurag Singh

Updated on Aug 19, 2024

Deploy a Secure LEMP Stack on Docker

In this tutorial, we'll explain how to deploy a secure LEMP stack on Docker with Let's Encrypt SSL on Ubuntu 24.04.

Learn how to deploy a complete LEMP stack (Nginx, MariaDB, PHP) on Docker with SSL security using Let's Encrypt. This step-by-step guide covers the entire process, from setting up Docker containers to securing your site with a free SSL certificate from Let's Encrypt. By the end, you'll have a fully functional and secure environment ready for production. Perfect for developers looking to streamline their web application deployment with Docker and ensure HTTPS security.

Prerequisites

  • A Ubuntu 24.04 installed dedicated server or KVM VPS.
  • A root user access or normal user with sudo rights.
  • Basic knowledge of Docker.

Install LEMP Stack on Docker

Step 1: Create a Project Directory

First, create a directory for your project. This directory will hold your Docker configuration files.

mkdir lemp-docker && cd lemp-docker

Step 2: Configure Firewall

We're configuring firewall first, because on next step we are generating SSL certificate. It needs HTTP port open in firewall.

ufw allow 80/tcp
ufw allow 443/tcp
ufw reload

Step 3: Generate SSL Certificate

Before proceeding further, we need to generate standalone SSL certificate using Certbot. We need the certificate path to add in the docker compose file.

sudo apt install certbot -y

Run the following command to obtain an SSL certificate:

Note: Replace your_domain with your domain.

sudo certbot certonly --standalone -d your_domain

After your execute above command it will provide the SSL certificate installed path. Copy SSL certificate saved path, we need it for next step to add in the default.conf file. It looks like:

Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem
Key is saved at: /etc/letsencrypt/live/your_domain/privkey.pem

Step 4: Install Docker

We need to install Docker. If you have already installed Docker in your system, you can skip this step.

Following commands are copied from official website:

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update

To install the latest version, run:

sudo apt-get install docker-ce -y

Step 5: Configure Nginx

Create a default configuration file for Nginx in the nginx directory.

mkdir nginx && cd nginx

Create a file named default.conf in the nginx directory with the following content:

nano default.conf

Replace yourdomain.com with your domain and add following content:

server {
    listen 80;
    listen [::]:80;
    server_name yourdomain.com www.yourdomain.com;

    # Redirect all HTTP traffic to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name lemp.hnxcode.dev www.yourdomain.com;

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

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass php_fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.ht {
        deny all;
    }
}

Save and exit the file.

Step 6: Create a PHP File to Test the Setup

Create an html directory in your project and add a index.php file:

cd ../ && mkdir html && cd html

Create a file named index.php in the html directory with the following content:

nano index.php

Add following code:

<?php
phpinfo();
?>

This simple PHP script will display the PHP configuration page.

Step 7: Create a docker-compose.yml File

In your project directory, create a docker-compose.yml file. This file will define the services for Nginx, MariaDB, and PHP.

cd ../ && nano docker-compose.yml

Add following content:

version: '3.8'

services:
  nginx:
    image: nginx:latest
    container_name: nginx_server
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./html:/var/www/html
      - /etc/letsencrypt:/etc/letsencrypt
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - php

  php:
    image: php:8.2-fpm
    container_name: php_fpm
    volumes:
      - ./html:/var/www/html
    depends_on:
      - mariadb

  mariadb:
    image: mariadb:latest
    container_name: mariadb_server
    environment:
      MYSQL_ROOT_PASSWORD: your_root_password
      MYSQL_DATABASE: your_database_name
      MYSQL_USER: your_user
      MYSQL_PASSWORD: your_password
    volumes:
      - ./db_data:/var/lib/mysql
    ports:
      - "3306:3306"

Step 8: Deploy the Containers

Now, you can deploy the Docker containers using Docker Compose.

docker compose up -d

This command will pull the necessary Docker images, create the containers, and start the services in detached mode.

Check the Docker container running using following command:

docker ps

If any container is not running, check the logs of the container.

docker ps -a 

Copy the stopped container id 

docker container logs [CONTAINER ID]

Check the log and fix the issue. Remove all the existing container and redeploy:

docker compose down
docker compose up -d

Step 9: Verify the Setup

Open your web browser and navigate to https://yourdomain.com. You should see the PHP information page, which confirms that Nginx is serving PHP files through PHP-FPM.

Conclusion

You now have a working LEMP stack running on Docker, with Nginx, MariaDB, and PHP all configured and ready to use. This setup is ideal for developing and testing web applications in a consistent environment.

Key Points

  • Nginx serves as the web server, handling requests and forwarding them to PHP-FPM.
  • PHP-FPM processes PHP scripts.
  • MariaDB handles the database, storing all your application data.
  • Docker Compose simplifies the setup by allowing you to define and manage multi-container Docker applications.
  • Let's Encrypt provides free SSL certificates, making it easy to secure your site.
  • Certbot automates the process of obtaining and renewing SSL certificates.
  • Nginx Configuration ensures that both HTTP and HTTPS traffic are correctly handled, with HTTP traffic being redirected to HTTPS.

This environment is easy to scale, maintain, and replicate, making it an excellent choice for modern web development.