Database

How to Deploy MariaDB 11.2.2 Using Docker Compose Behind Caddy v2.7.6

MariaDB turns data into structured information in a wide array of applications, ranging from banking to websites. It is an enhanced, drop-in replacement for MySQL.

Rajasekhar Gundala··6 min read

MariaDB turns data into structured information across a wide array of applications, ranging from banking systems to enterprise websites. It is an enhanced, highly performant, drop-in replacement for MySQL.

Previously, I documented how to deploy MariaDB in a Docker Swarm environment.

Today, I am going to show you how to deploy MariaDB 11.2.2 using Docker Compose behind a Caddy reverse proxy. In this setup, we are not exposing the MariaDB service to the external network. It will be securely isolated and available only to internal services or applications.

MariaDB is used because it is fast, scalable, and robust. Its rich ecosystem of storage engines, plugins, and tools makes it incredibly versatile for a wide variety of use cases.

Let’s start with the actual deployment.

Prerequisites

Please ensure you fulfill the following requirements before proceeding with the deployment:

  1. A server running the latest Ubuntu release (any Linux distribution works, but Ubuntu is used throughout this guide).
  2. Docker and Docker Compose installed on the Ubuntu server.

Introduction to MariaDB

MariaDB is one of the most popular database servers in the world. It is a fork of MySQL, created by the original developers of MySQL.

MariaDB is guaranteed to stay open source forever. Notable users include Wikipedia, WordPress.com, and Google.

Developed as open-source software, MariaDB provides a robust SQL interface for accessing data and features modern capabilities, including built-in GIS and JSON support.

You can read more about MariaDB here.

Securing the MariaDB Environment

MariaDB Docker images offer various ways to set the MySQL root password, but some methods are far more secure than others:

  1. Environment Variables: Specifying MYSQL_ROOT_PASSWORD directly in the compose file. (Least secure)
  2. Mounted Files: Mounting a plain-text password file into the container. (Better, but still exposed on the host)
  3. Random Generation: Specifying MYSQL_RANDOM_ROOT_PASSWORD to have MariaDB generate and print a random password to the logs. (Recommended)
  4. Docker Secrets: Securely storing credentials. In Swarm, this uses an encrypted Raft log; in local Docker Compose, it mounts local files into memory securely. (Highly Recommended)

We will use the Docker Secrets method to pass the required password values to our MariaDB container.

Specifying MYSQL_ROOT_PASSWORD in standard environment variables is the least secure option, as variables are exposed to the host system and container inspection, leaving the password at a high risk of exposure. Docker introduced Docker Secrets in version 1.13 to manage sensitive data natively. You can read the overview here.

Setting up containers with secrets in Docker Compose is straightforward and keeps passwords completely out of your source code.

MariaDB Custom Configuration (Optional)

The primary startup configuration is located at /etc/mysql/my.cnf. This file also includes any .cnf files found in the /etc/mysql/conf.d directory.

If you want to use a customized MySQL configuration, you can create an alternative configuration file on the host and mount its directory into the container:

version: "3.7"

services:
  mariadb:
    image: mariadb:latest
    volumes:
      - /my/custom:/etc/mysql/conf.d

This will start the mariadb container using the combined startup settings from /etc/mysql/my.cnf and /etc/mysql/conf.d/config-file.cnf, with the custom settings taking precedence.

Alternatively, many configuration options can be passed directly as flags to mysqld via the command key. For example, to change the default encoding to utf8mb4 and adjust timeout settings:

version: "3.7"

services:
  mariadb:
    image: mariadb:latest
    command: ["mariadb", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci", "--wait_timeout=28800", "--interactive_timeout=28800", "--max_allowed_packet=256M"]

Check the MariaDB Docker Hub page for more configuration options.

Prepare the MariaDB Environment

Let’s start by creating the password files for our MariaDB container using the Docker Secrets method.

Create the required .txt files in your deployment directory:

cd /opt
sudo nano mysql_user.txt
sudo nano mysql_database.txt
sudo nano wp_db_password.txt
sudo nano mysql_root_password.txt

(Enter the respective values into each file, save, and exit).

For disaster recovery and data persistence, we need to map the /var/lib/mysql directory inside the container to the host filesystem.

Create a persistent folder in the /opt directory:

cd /opt
sudo mkdir -p mariadata

Docker Compose Configuration

Now, it’s time to create a docker-compose.yml file that contains both the Caddy Reverse Proxy service and the MariaDB service.

First, create a Docker network named caddy. We will use this to expose internal applications to the outside world safely.

docker network create caddy

We will use an additional bridge network named inet to connect the MariaDB service securely without exposing it externally.

Create the Compose file:

cd /opt
sudo touch docker-compose.yml
sudo nano docker-compose.yml

Paste the following stack configuration:

version: "3.7"

services:
  maria:
    image: mariadb:11.2.2
    container_name: maria
    restart: unless-stopped
    depends_on:
      caddy:
        condition: service_started
    volumes:
      - ./mariadata:/var/lib/mysql
    secrets:
      - mysql_user
      - mysql_database
      - mysql_db_password
      - mysql_root_password
    environment:
      - MYSQL_USER_FILE=/run/secrets/mysql_user
      - MYSQL_DATABASE_FILE=/run/secrets/mysql_database
      - MYSQL_PASSWORD_FILE=/run/secrets/mysql_db_password
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password
    command: ["--wait_timeout=28800", "--interactive_timeout=28800", "--max_allowed_packet=256M", "--transaction-isolation=READ-COMMITTED", "--binlog-format=ROW"]
    networks:
      - inet
    healthcheck:
      test: ['CMD', '/usr/local/bin/healthcheck.sh', '--innodb_initialized']
      start_period: 5s
      timeout: 5s
      interval: 5s
      retries: 5

  caddy:
    image: rajaseg/caddy
    restart: unless-stopped
    container_name: caddy
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
      - target: 443
        published: 443
        mode: host
        protocol: udp
      - target: 2019
        published: 2019
        mode: host
    networks:
      - caddy
      - inet
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddydata:/data
      - ./caddyconfig:/config
      - ./caddylogs:/var/log/caddy
      - ./mariadata:/var/lib/mysql

secrets:
  mysql_user:
    file: ./mysql_user.txt
  mysql_database:
    file: ./mysql_database.txt
  mysql_root_password:
    file: ./mysql_root_password.txt
  mysql_db_password:
    file: ./mysql_db_password.txt

volumes:
  caddydata:
  caddyconfig:
  caddylogs:
  mariadata:

networks:
  caddy:
    external: true
  inet:
    driver: bridge

Let’s discuss a few configuration options utilized above:

  • depends_on: Expresses startup and shutdown dependencies between services. In this scenario, caddy is guaranteed to start before maria. Compose waits for dependency services to be ready before starting the dependent service.
  • restart_policy: Configures how containers should behave when they crash or the host reboots. unless-stopped ensures the database always comes back online automatically.

Deploy the Docker Compose Stack

Deploy the stack using the following command:

docker compose up -d

Check the status of the running containers using:

docker ps

Create and Manage Databases

Type docker ps on your server to view the running containers. The output should look similar to the image below.

docker-ps

Note down the CONTAINER ID of the MariaDB container from the output.

Log into the container’s interactive shell using that ID:

docker exec -it <CONTAINER_ID> bash

Once inside, log into the MariaDB admin console:

mariadb -u root -p

Enter the root password you created via the .txt file earlier.

Type SHOW DATABASES; to see all the databases in the server. By default, it will have created the database specified in your mysql_database.txt file (e.g., testdb).

You can manually create new databases for future applications:

CREATE DATABASE app_database_name;

And delete them if necessary:

DROP DATABASE app_database_name;

Watch the video below for a walkthrough of deploying MariaDB in a Docker Swarm Cluster.

I hope you enjoyed this post! Please share your thoughts or feedback in the comments below.

Stay tuned for more application deployments! 🙂

Share
Written by
Rajasekhar Gundala

Senior Infrastructure & Web Platform Leader.

Continue reading

Weekly Engineering Notes.

A weekly digest on infrastructure, observability, Rust, and the open web. No spam, just technical signals.

Free. Unsubscribe in one click.