Our Trellis Server Update Process: How We Keep Client Sites Secure and Current
For most growing businesses, a WordPress server isn’t something you think about until something breaks — a plugin stops working, a contact form goes silent, or worse, the site goes down. The cause is often the same: an outdated server stack that hasn’t kept pace with security patches and dependency updates.
This is exactly what we solve with our Trellis-based WordPress hosting service. Built on the Roots Trellis stack, we manage the server so you don’t have to — including keeping it updated, secured, and running reliably. In an earlier post we covered why regular Trellis updates matter. This post goes one level deeper: here’s the actual process we use to keep our clients’ servers current without introducing downtime or configuration drift.
If you’re a developer managing your own Trellis setup, the workflow below is reusable. If you’re a business owner who just wants a server that stays healthy — that’s what we’re here for.
How We Update Trellis Today (2026)
Over time our update process has matured from manually diffing files into a scripted, repeatable workflow. Here’s how we handle Trellis upgrades across the imagewize.com monorepo (private), which holds Trellis, Bedrock, and our themes in one place.
The Upstream Remote
Because our Trellis lives inside a monorepo (not a standalone repository), we track the official Roots Trellis repo as a separate git remote named upstream. This lets us check for new releases without leaving the project:
# Add once — already done in this project
git remote add upstream https://github.com/roots/trellis.git
# Check for new releases at any time
git fetch upstream --dry-run
A dry-run fetch shows new tags immediately. In early 2026 this revealed v1.28.0, v1.29.0, and v1.30.0 — a clear signal that an update is overdue.
The Updater Script
Rather than manually copying files and hoping nothing gets missed, we use a shell script that automates the heavy lifting. It lives at scripts/trellis-updater.sh in the repo and is based on the approach documented in imagewize/wp-ops. The script does four things:
- Backs up the entire current
trellis/directory to a timestamped folder (~/trellis-backup-YYYYMMDD_HHMMSS). - Clones a fresh copy of Trellis from GitHub into a temp directory.
- Generates a diff of what changed between upstream and your current files.
- Rsyncs the new files into your project while explicitly excluding everything you’ve customised.
# Edit the PROJECT variable at the top of the script, then run:
bash scripts/trellis-updater.sh
What Gets Preserved
The rsync step uses an explicit --exclude list so your customisations are never touched. The key files kept intact:
.vault_passandansible.cfg— secrets and vault configgroup_vars/*/vault.yml— encrypted credentialsgroup_vars/*/wordpress_sites.yml— site definitionsgroup_vars/all/main.yml,group_vars/production/main.yml— PHP, PHP-FPM, and MariaDB settingsgroup_vars/all/mail.yml— SMTP provider credentialsgroup_vars/all/security.yml— fail2ban jails and IP allowlistsdeploy-hooks/— custom build hooksnginx-includes/— custom Nginx config snippetshosts/— server inventoryroles/wordpress-setup/templates/php-fpm-pool-wordpress.conf.j2— custom PHP-FPM pool template
After the rsync the script runs a verification pass and prints warnings if any critical file is unexpectedly missing.
The security.yml Gotcha
We learned this the hard way during the February 2026 update. Even though security.yml is now in the exclude list, it’s worth calling out: an upstream Trellis change set the wordpress_wp_login fail2ban jail to enabled: "false", silently disabling brute-force protection on the wp-login endpoint. After any update, verify this explicitly:
grep -A3 'wordpress_wp_login' trellis/group_vars/all/security.yml
# Should show: enabled: "true"
Full Workflow Step by Step
- Check for new versions —
git fetch upstream --dry-run - Create a feature branch —
git checkout -b trellis-update-MMYYYY - Run the updater —
bash scripts/trellis-updater.sh - Review the diff —
git diff trellis/andcat ~/trellis-diff/changes.txt - Re-apply any role template customisations (e.g. the MariaDB
50-server.cnf.j2if upstream touched it) - Update Galaxy roles —
cd trellis && ansible-galaxy install -r galaxy.yml --force - Test in development —
trellis provision development - Verify fail2ban security.yml — see above
- Open a pull request, review, merge
- Provision production —
trellis provision production
Checking What’s New in Upstream
Once you have the upstream remote configured, you can browse the commit log between your current state and upstream master before running the script:
git fetch upstream
git log HEAD..upstream/master --oneline -- trellis/
This gives you a readable list of upstream changes so you can spot anything that might affect your custom configuration before the rsync runs.
Provisioning After the Update
For most updates a full provision is the safest approach. If upstream only touched PHP or Nginx config you can scope it with tags to avoid unnecessary restarts:
# Scoped provision — PHP and Nginx only
trellis provision --tags php,nginx,wordpress-setup production
# Full provision — safest after a large update
trellis provision production
We’ve found the scoped approach works well for routine updates. A full provision is worth doing after bigger jumps (multiple minor versions) or when roles like ntp or redis have changed.
Current Status
As of early April 2026, our last major Trellis update was in February 2026, bringing Ansible 2.20+ compatibility and updating the NTP role from v3 to v4. Upstream has since tagged v1.28.0, v1.29.0, and v1.30.0 — the next update is already in the pipeline.
The updater script and full upgrade notes are maintained in the repo under scripts/trellis-updater.sh and docs/TRELLIS-UPGRADE.md. The base script is also published at imagewize/wp-ops for reuse on other projects.
Social Media Photo by mingche lee: https://www.pexels.com/photo/plant-vines-on-wooden-trellis-11994878/