Technote

Development workflow Practical

Series From local to production Part 6 of 8

Three deploy strategies: rsync, git on the server, built artefacts

What separates the three is not speed but the rollback story. Decide how you undo a deploy before you decide how you make one.

Most teams choose a deploy method by asking how to get files onto the server. Turn the question round: decide how you will undo it and the right choice among the three becomes obvious.

Three deploys — each has a different rollback story, and that is the deciding factor

1. rsync — make the tree match

You mirror a checkout from CI or your machine onto the server directory. All the server needs is SSH, and if the deployed tree has drifted, --delete tidies it up.

rsync -az --delete --exclude-from=deploy-exclude.txt --dry-run ./ deploy@host:/srv/site/current/

Two rules are non-negotiable. Run --dry-run first and read what would be removed. And put wp-content/uploads/ and wp-config.php in the exclude file — leave them out and --delete will remove every uploaded file. Those two lines prevent the most expensive mistake in rsync deploys.

Rollback means checking out the previous revision and running the same command. Simple — but the switch is not atomic. While files update one by one, a visitor can be served a new template alongside old stylesheets.

2. Git on the server — the simplest arrangement

ssh deploy@host 'cd /srv/site/current && git fetch --tags && git checkout v2026.09.05'

The server needs git and a read-only deploy key. Rollback is a checkout of the previous tag, the history stays intact, and you can always ask the server exactly what is deployed.

Two risks come with it. First, if anyone edits the working tree on the server, the next deploy stops on a conflict — the habit of “just fixing one line in production” cannot coexist with this method. Second, if the .git directory is reachable over HTTP, your entire source is public. Block it at the web server.

3. Built artefact plus symlink — one instant switch

You build elsewhere, upload the result as a release directory, and move the current symlink. Because the switch is a single link, there is no intermediate state.

ssh deploy@host 'ln -sfn /srv/site/releases/20260905-1200 /srv/site/current'

The -n is not optional. Without it, ln follows the existing symlink and creates the new link inside the directory it points at. It appears to succeed, the deploy does not take effect, and you find out days later.

Keep wp-config.php and wp-content/uploads/ in a shared directory outside the releases and link them into each one. Rollback is repointing the symlink at the previous release, and it is the fastest of the three.

What is left to do after any deploy

Moving files does not finish the job. Caches are still holding the old code.

The order after a deploy — stop after the first box and the old code keeps answering

OPcache holds compiled bytecode, and after a symlink switch the realpath cache keeps resolving the old path. Reloading PHP-FPM gives you fresh workers with clean caches. Then wp cache flush for the object cache, and finally purge the page cache.

Deployment and server configuration continue in the Servers & infrastructure archive, and building a reproducible deploy setup from scratch is part of our optimization program.

Next part

With deploys down to one command, the next thing to add is an automatic gate in front of them. Part seven covers CI for WordPress projects — what you can realistically check.

More on this topic

All technotes

Development workflow Advanced

The design QA checklist to run before release

Most design problems found after release are catchable before it, in order. Here is the pass, in three stages: rules, states and real devices.

Designers 6 min read

Development workflow Advanced

Never deploy without a way back

A rollback is not one button. It is two tracks — code and database — with an order to reverse them in, written down before you deploy.

Designers 9 min read

Development workflow Advanced

Adding your own hooks: extension points instead of edits

Code with no extension points gets forked or edited in place. Where you put do_action and apply_filters — and how many — decides how long that code survives.

Developers 7 min read

₩270,000 · Join the program