Skip to content
Go back

WordPress, Docker, NGINX, and MySQL via Ansible

By SumGuy 5 min read
WordPress, Docker, NGINX, and MySQL via Ansible

In the realm of web development, ensuring the seamless deployment and management of web applications is crucial. This article provides a detailed guide on using Ansible to set up a Dockerized environment for running a WordPress site, backed by NGINX as a web server and MySQL as the database. By automating this setup, we can significantly enhance security, repeatability, and scalability.

Why Choose Docker, Ansible, NGINX, and MySQL?

Step 1: Prerequisites

To follow this guide, you’ll need:

Step 2: Prepare the Ansible Playbook

Create a directory for your Ansible playbook and related files:

mkdir wordpress-deployment
cd wordpress-deployment

Step 3: Define the Docker Compose File

Within the wordpress-deployment directory, create a docker-compose.yml file. This file defines the services, networks, and volumes that compose your WordPress environment.

services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: password123
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
volumes:
- wordpress_data:/var/www/html
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- wordpress
volumes:
db_data:
wordpress_data:

Step 4: NGINX Configuration

Create an NGINX configuration file default.conf in the nginx directory. Configure NGINX to act as a reverse proxy to the WordPress container.

server {
listen 80;
server_name localhost;
location / {
proxy_pass http://wordpress:80;
proxy_set_header Host $host;
proxy_setsecure_protocol $scheme;
proxy_buffering off;
}
}

make sure to change localhost to your domain

Step 5: Ansible Playbook to Deploy Everything

Now, create an Ansible playbook named deploy.yml:

- hosts: all
become: true
tasks:
- name: Ensure Docker is installed
apt:
name: docker-ce
state: latest
- name: Clone project containing Docker Compose file
git:
repo: 'http://github.com/youraccount/wordpress-docker-compose.git'
dest: /home/user/wordpress
clone: yes
update: yes
- name: Run Docker Compose
docker_compose:
project_src: /home/user/wordpress

Why This Setup is Secure and Efficient?

This setup not only ensures a robust deployment strategy for WordPress sites but also simplifies management and scaling, making it an ideal choice for modern web applications.

What Actually Breaks This (And How to Fix It)

The playbook above works — until it doesn’t. Here are the landmines you’ll hit on a real deployment, usually at 11 PM before a launch.

The docker_compose Module Is Gone

If you’re on Ansible 6+ with the community.docker collection 3.x, docker_compose no longer exists. It was replaced by community.docker.docker_compose_v2. The old module silently does nothing on newer installs, which is a fantastic way to spend an hour staring at an unchanged server wondering why nothing deployed.

Fix your task:

deploy.yml
- name: Run Docker Compose
community.docker.docker_compose_v2:
project_src: /home/user/wordpress
state: present

Install the collection first if you haven’t:

Terminal window
ansible-galaxy collection install community.docker

MySQL 5.7 Is EOL — Use 8.0

mysql:5.7 hit end-of-life in October 2023. It still pulls and runs, but you’re not getting security patches, and some hosting environments will refuse it outright. Swap it:

image: mysql:8.0

One catch: MySQL 8 changed the default authentication plugin. WordPress’s mysqli driver expects mysql_native_password. If you get “Authentication plugin ‘caching_sha2_password’ is not supported”, add this to your db service:

command: --default-authentication-plugin=mysql_native_password

Hardcoded Passwords Will Haunt You

password123 in version-controlled YAML is a support ticket waiting to happen. Use Ansible Vault instead:

Terminal window
# Create an encrypted vars file
ansible-vault create group_vars/all/vault.yml

Then reference it in your Compose template:

group_vars/all/vault.yml
vault_mysql_root_password: "your-actual-secret"
vault_mysql_password: "another-actual-secret"

And in your playbook, use {{ vault_mysql_root_password }} instead of the plaintext string. Your future self — the one who accidentally pushes to a public repo — will thank you.

The WordPress Container Starts Before MySQL Is Ready

depends_on only waits for the container to start, not for MySQL to actually accept connections. WordPress will try to connect immediately, fail, and you’ll see a “Error establishing a database connection” page on first load. Restart it and it’s usually fine — but that’s not automation, that’s hoping.

Add a healthcheck to your db service:

healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5

Then change your WordPress depends_on to:

depends_on:
db:
condition: service_healthy

Now Compose actually waits for MySQL to be ready before WordPress starts trying to connect. It’s a small addition that saves a lot of “is it up yet?” refreshing.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Ansible vs. Terraform: Cloud Infrastructure Management
Next Post
Prompts for Image Generation in Stable Diffusion

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts