How Trellis’s database-pull Playbook Works (And What We Fixed)
One of the most useful Trellis commands in day-to-day WordPress development is trellis db pull. It syncs your production (or staging) database down to local — switching URLs automatically so your dev site works immediately. But the playbook behind it does more than most developers realise, and the default Trellis version had some issues we needed to fix before it worked reliably.
This post walks through exactly what database-pull.yml does, the safety feature hidden in the middle of it, and the two patches we made to get it working correctly with a Bedrock project.
What the Playbook Does
Running trellis db pull production executes database-pull.yml against your production host. Here’s the sequence:
1. Pre-flight validation
Before touching any database, the playbook checks three things:
- name: Abort if environment variable is equal to development
fail:
msg: "ERROR: development is not a valid environment for this mode..."
when: env == "development"
You cannot pull from development to development — a useful guard if you ever mis-type the environment flag.
It also checks that your local project folder exists:
- name: Check if Jekyll::Drops::SiteDrop local folder exists
delegate_to: localhost
stat:
path: ""
register: result
become: no
If the path doesn’t exist the playbook aborts with a clear message rather than partially running and leaving things in an inconsistent state.
2. Create local backup directory
- name: Create local database_backup directory if it doesn't exist
delegate_to: localhost
file:
path: "/database_backup"
state: directory
mode: 0755
become: no
On first run this creates a database_backup/ folder inside your local Bedrock project root. All backups end up here.
3. Dump and transfer the remote database
- name: Create database dump on
shell: wp db export --allow-root - | gzip >
args:
chdir: ""
The dump is created on the remote server, piped through gzip. Then it’s fetched to local via Ansible’s fetch module:
- name: Pull database dump from to development
fetch:
src: "/"
dest: "/"
flat: yes
After the transfer the remote dump is deleted immediately — no production files left hanging around on the server.
4. The hidden safety feature: backup local first
This is the part most developers don’t expect. Before importing the production dump, the playbook backs up your current local database:
- name: Export development database before importing dump (backup)
delegate_to: localhost
shell: wp db export - | gzip > database_backup/
args:
chdir: "/web/wp"
become: no
The backup filename includes a timestamp:
imagewize_com_development_2026_02_27_10_30_45.sql.gz
So if the import causes any problems — or you realise after the fact that you needed that local data — you can restore it from database_backup/. Every pull creates a new timestamped backup, meaning you accumulate a history of your local database states. Worth clearing out periodically.
5. Import and search-replace
- name: Import database dump on development
delegate_to: localhost
shell: gzip -c -d | wp db import -
args:
chdir: "/web/wp"
become: no
Then the URL swap:
- name: Search for and replace with on development
delegate_to: localhost
command: wp search-replace '//' '//' --allow-root --all-tables --precise
Both url_from (production) and url_to (local) are resolved from config automatically. After this step your local WordPress is fully functional with production content.
What We Fixed
The version of database-pull.yml we started with had two problems.
Fix 1: Broken hostvars references
The original playbook resolved local site config like this:
host: "_host"
from_host: ""
url_from: ""
url_to: ""
local_bedrock_dir: ""
It was trying to read the development site config via hostvars.development_host, which required that host to be in the play’s host inventory. Since the playbook only targets web:& (the remote server), development_host wasn’t always populated at the right time. This caused intermittent failures that were hard to reproduce.
The fix was to load the development config directly using vars_files and a file lookup:
vars_files:
- group_vars/development/wordpress_sites.yml
vars:
url_from: ""
dev_wordpress_sites: ""
url_to: ""
project_local_path: ""
Reading the file directly is more reliable than relying on hostvars being populated for a host that isn’t part of the current play.
Fix 2: Wrong delegate_to target
The original used delegate_to: development_host for local tasks:
- name: Create database_backup directory if it doesn't exist
delegate_to: development_host
file:
path: "/database_backup"
...
Two problems here: development_host has the same availability issue as above, and the path was using project_web_dir (the remote path) instead of the local Bedrock path.
Replacing with delegate_to: localhost and correcting the path:
- name: Create local database_backup directory if it doesn't exist
delegate_to: localhost
file:
path: "/database_backup"
state: directory
mode: 0755
become: no
The become: no is also important — local tasks shouldn’t run as root.
Using It
Once the playbook is correct, pulling production to local is a single command run from the Trellis directory:
cd trellis
trellis db pull production
For a Bedrock project the WP path is in the web/wp subdirectory, which the playbook handles automatically via the chdir arguments.
If you want to run just the search-replace step again (useful if something went wrong):
trellis db pull production --tags search-replace
Takeaway
The Trellis database-pull playbook is well-designed, but the default version had brittle hostvars lookups that could fail depending on how hosts were configured. Switching to direct file lookups and delegate_to: localhost made it deterministic.
The built-in local backup before import is a genuinely useful safety net that’s easy to miss since it’s buried in the middle of the playbook. Your database_backup/ directory quietly accumulates timestamped snapshots of every pull — worth knowing about before you hit a situation where you need it.
This is part of the Trellis-based WordPress deployment workflow we use at Imagewize for client projects. If you’re running a Trellis stack and want help setting up or debugging your deployment pipeline, get in touch.
Questions or issues with your Trellis setup? Find me on Mastodon at @jfrumau@mastodon.social.