Skip to the content.

WordPress Ansible Playbook

An Ansible playbook that provisions a complete, production-ready WordPress stack — functionally equivalent to the setup-swap.sh and setup-wp-nginx.sh bash scripts, with the added benefits of idempotency and repeatability.

What It Does

The playbook executes four roles in sequence:

1. common — System Packages

2. swap — Swap File Management

3. wordpress — Full Stack Installation

This is the main role and handles everything from packages to a working WordPress site:

Package Installation

PHP-FPM Tuning

OPcache Configuration

phpMyAdmin

MariaDB Setup & Hardening

Nginx Configuration

WordPress Installation

WordPress Hardening

Essential Plugins

Installs (but does not activate) the following plugins:

Enables auto-updates for all plugins and themes.

Weekly Update Cron

SSL with Certbot

Credentials File

4. security — Server Hardening

Unattended Upgrades

Fail2Ban

File Permissions

Playbook Variables

Variables are defined in playbook.yml and can be overridden via inventory, command-line (--extra-vars), or vars_prompt.

Interactive Prompts (vars_prompt)

The playbook prompts for these at runtime. Press Enter to auto-generate a secure 24-character password.

Prompt Variable Default
WordPress DB password wp_db_pass Auto-generated
WordPress admin password wp_admin_pass Auto-generated
MySQL root password mysql_root_pass Auto-generated

Configurable Variables (vars)

Variable Description Default
domain Domain name for the WordPress site example.com
use_www Enable www.<domain> as a server alias true
wp_db_name WordPress database name wpdb
wp_db_user WordPress database user wpuser
wp_admin_user WordPress admin username user
admin_email Admin email for WordPress and Let’s Encrypt admin@<domain>
enable_fail2ban Install and enable Fail2Ban true
enable_ssl Obtain SSL certificate via Certbot true
swap_size Swap file size 2G

Auto-Generated Variables (pre_tasks)

These are populated automatically and should not be overridden:

Variable Description
pma_blowfish Random 32-character blowfish secret for phpMyAdmin

Requirements

Usage

  1. Update the inventory — edit inventory.ini with your server IP and SSH credentials:

    [wordpress]
    YOUR_SERVER_IP
    
    [wordpress:vars]
    ansible_user=your_ssh_user
    # ansible_ssh_private_key_file=/path/to/key
    
  2. Update playbook variables — edit the vars section of playbook.yml or pass overrides:

    ansible-playbook -i inventory.ini playbook.yml \
      --extra-vars "domain=example.com use_www=true enable_ssl=true"
    
  3. Run the playbook:

    ansible-playbook -i inventory.ini playbook.yml
    
  4. View credentials after completion:

    ssh your_server 'sudo cat /root/.wp-credentials'
    

Testing with Vagrant

A Vagrantfile is included for local testing using a disposable VM.

Prerequisites

Setup

  1. Navigate to the ansible directory:

    cd ansible
    
  2. Start the Vagrant VM:

    vagrant up
    

    This will:

    • Boot an Ubuntu 24.04 VM (bento/ubuntu-24.04) with 2 GB RAM and 2 CPUs.
    • Install Ansible on the guest via the ansible_local provisioner.
    • Run the playbook with test overrides: domain 192.168.56.10.nip.io, SSL disabled, test passwords.
  3. Access the WordPress site:
    • Open http://192.168.56.10.nip.io in your browser.
    • phpMyAdmin: http://192.168.56.10.nip.io/phpmyadmin
    • Or map the domain in /etc/hosts: 192.168.56.10 192.168.56.10.nip.io
  4. Reprovision (test idempotency):

    vagrant provision
    
  5. SSH into the VM:

    vagrant ssh
    
  6. Destroy the VM:

    vagrant destroy -f
    

Directory Structure

ansible/
├── ansible.cfg                                     # Ansible settings (pipelining, Python interpreter)
├── inventory.ini                                   # Target host inventory
├── playbook.yml                                    # Main playbook (vars, prompts, role execution)
├── Vagrantfile                                     # Local test VM (Ubuntu 24.04, VirtualBox)
├── README.md                                       # This file
└── roles/
    ├── common/
    │   └── tasks/main.yml                          # apt update, system package installation
    ├── swap/
    │   └── tasks/main.yml                          # Swap file creation, sysctl tuning
    ├── wordpress/
    │   ├── tasks/main.yml                          # Full stack: PHP, Nginx, MariaDB, WP, phpMyAdmin
    │   ├── handlers/main.yml                       # Service restarts (Nginx, PHP-FPM, MariaDB)
    │   ├── files/
    │   │   └── disable-xmlrpc-pingback.php         # MU-plugin for XML-RPC DDoS mitigation
    │   └── templates/
    │       ├── nginx-site.conf.j2                  # Nginx server block with security rules
    │       ├── security-headers.conf.j2            # Nginx security headers snippet
    │       ├── opcache.ini.j2                      # PHP OPcache configuration
    │       ├── root-my.cnf.j2                      # MariaDB root .my.cnf for idempotent re-runs
    │       ├── wp-credentials.j2                   # Credentials file template
    │       └── wp-updates.sh.j2                    # Weekly WP core/plugin/theme update cron script
    └── security/
        ├── tasks/main.yml                          # Unattended Upgrades, Fail2Ban, file permissions
        ├── handlers/main.yml                       # Fail2Ban service restart
        └── templates/
            └── 50unattended-upgrades.j2            # Unattended Upgrades configuration

Benefits Over Bash Scripts

Feature Bash Scripts Ansible Playbook
Idempotency Partial (manual checks) Built-in — safe to re-run
Readability Shell logic can be complex Declarative YAML tasks
Multi-server One server at a time Inventory-based, run on many
Error handling set -e + manual traps Module-level error reporting
Configuration Environment variables Variables, prompts, inventories
Secret management Plaintext env vars vars_prompt + no_log, Ansible Vault compatible
Testing Vagrant + manual verification Vagrant + ansible-playbook --check dry-run