DevOps

Learning just enough shell scripting to automate our deploys

OPS

Deploying used to mean SSHing in, pulling the branch, clearing a cache directory, restarting PHP-FPM, and hoping we didn't forget a step, usually while a client waited on the other end of an email thread asking whether the fix was live yet. We finally sat down and wrote it as an actual bash script.

  • git pull origin master
  • clear the WordPress transients cache with a wp-cli command
  • sudo service php7.0-fpm restart, or whatever version we're on for that server

It's maybe twenty lines total, nothing that would impress anyone with real ops experience, but it turned a nervous fifteen-minute manual process into a single command we run with confidence.

Why the manual version kept going wrong

Manual deploys fail in boring, predictable ways, a step gets skipped because it's the last thing on a mental checklist and we're tired, or the exact restart command is subtly different on a server we haven't touched in a few weeks and we type the wrong service name. None of these are dramatic failures, but each one meant a broken deploy that had to be diagnosed and fixed live, sometimes with a client already refreshing the page waiting to see their fix.

What writing it down actually taught us

Writing the script forced us to actually enumerate every single step in the process for the first time, rather than relying on memory, and we found at least one step, a specific cache directory that needed manual clearing on top of the wp-cli transients flush, that one of us did out of habit and the other didn't even know about. That gap alone had probably caused a couple of "why isn't this showing up" moments on past deploys that we'd never traced back to a missing cache clear.

Where we're taking this next

We're slowly learning that a lot of sysadmin work is just remembering to write down what you already do by hand, then automating the boring parts once they're written down clearly enough to automate. This script is intentionally basic, no rollback support, no zero-downtime handling, but it's already a meaningful step up from where we started the year, and we'd rather ship the twenty-line version now than spend a week building a more sophisticated deploy tool for a two-person team that doesn't need one yet.

A mistake that taught us to add error checking

The script ran fine for weeks until we pointed it at a staging server with a slightly different directory layout, and the git pull step failed silently because we were running it from the wrong working directory, but the script carried on to the cache-clearing step anyway and reported success. We hadn't added set -e anywhere, so a failed command just kept going instead of stopping the whole thing, and the deploy quietly did nothing useful while telling us it worked. Adding set -e at the top of the script, plus a couple of explicit exit code checks after the git pull, fixed it, and it's now the first thing we add to any new script rather than an afterthought.

What we're not automating yet

Database migrations still happen by hand, mostly because we've only got a couple of client sites where the schema changes often enough to be worth scripting, and a wrong migration run automatically against production is a much scarier failure mode than a stale cache. We're comfortable with the current split, script the parts that are safe to get wrong and easy to redo, keep a human in the loop for the parts that aren't, at least until we've got enough projects with the same shape that writing a proper migration tool actually pays for itself.

← Back to the journal

Have a project in mind?
Let’s talk.

Tell us where you are and where you want to go. We'll map the fastest route between the two.

Currently accepting new clients