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?
-
Docker simplifies deployment by containerizing applications, ensuring that they work uniformly across different environments.
-
Ansible automates software provisioning and configuration management, reducing the potential for human error.
-
NGINX serves as a high-performance web server and reverse proxy, offering better performance and resource utilization than traditional servers.
-
MySQL is a robust and scalable database management system, ideal for managing data-intensive applications like WordPress.
Step 1: Prerequisites
To follow this guide, you’ll need:
-
A Linux system (preferably Ubuntu)
-
Docker and Docker Compose installed
-
Ansible installed on your local machine
Step 2: Prepare the Ansible Playbook
Create a directory for your Ansible playbook and related files:
mkdir wordpress-deploymentcd wordpress-deploymentStep 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/wordpressWhy This Setup is Secure and Efficient?
-
Isolation: Docker provides an isolated environment for your applications, minimizing conflicts and security risks.
-
Version Control: With Docker and Ansible, specific versions of services can be defined, ensuring consistency across environments.
-
Automation: Ansible automates the entire process, reducing the likelihood of errors and misconfigurations.
-
Scalability: Scaling services is as simple as updating the Docker Compose file and re-running the playbook.
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.
Related Reading
- Automating Docker via Ansible
- Docker Strategies for Load Balancing and Failover
- Access Docker socket via TCP
- Docker Networking: Connecting to the Host from a Container
- Docker Networking Essential Guide for All Skill Levels
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:
- name: Run Docker Compose community.docker.docker_compose_v2: project_src: /home/user/wordpress state: presentInstall the collection first if you haven’t:
ansible-galaxy collection install community.dockerMySQL 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.0One 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_passwordHardcoded Passwords Will Haunt You
password123 in version-controlled YAML is a support ticket waiting to happen. Use Ansible Vault instead:
# Create an encrypted vars fileansible-vault create group_vars/all/vault.ymlThen reference it in your Compose template:
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: 5Then change your WordPress depends_on to:
depends_on: db: condition: service_healthyNow 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.