How to Build a Zero-Downtime CI/CD Pipeline with an AI Pair

Part 4 of a 5-part series on using Claude AI to run, secure, and ship a real production server. The server is now secure (Parts 1–3). Time to ship code the right way.
Build a Zero-Downtime CI/CD Pipeline with an AI Pair (Part 4)
Up to now I'd been deploying by hand — SSH in, pull, hope. For my main SaaS project I wanted the real thing: push to GitHub, tests run automatically, and if they pass, the server swaps to a fresh release with a symlink so rollback is instant. In this post, Claude plays two roles: code-review partner for my pipeline, and server-prep engineer to get the box ready — using the same investigate-then-execute pattern from the earlier parts.
The Architecture (Capistrano-Style Releases)
The idea is simple and powerful. Instead of overwriting your live app in place, each deploy lands in a timestamped folder, and a symlink points to the current one:
/var/www/visa-saas/
├── releases/
│ ├── 20260709224220/ ← previous release
│ └── 20260712172016/ ← new release
├── shared/ ← .env, storage (survive across deploys)
├── api → releases/20260712172016 ← symlink, flipped atomically
└── web → releases-web/... ← same idea for the frontendDeploy = build a new release folder, then flip the symlink. Rollback = flip the symlink back. That's the whole magic: rollback becomes one command instead of a panic.
Claude as Code Reviewer: What It Caught in My Pipeline
I had draft GitHub Actions workflows and asked Claude to review them like a senior engineer. It found real problems I'd have shipped:
| Issue Claude flagged | Why it mattered |
|---|---|
Web deploy did rm -rf then move — no rollback | The old release was destroyed at activation. One bad build = no way back. |
| Prune step would eventually delete the original backup | "Keep 5 newest" quietly wipes your initial safety copy after 5 deploys. |
Bare php on a server with 6 PHP versions | Migrations could run under the wrong PHP than the site serves. |
| Migrations with no database backup first | A symlink rollback can't undo a schema change — you need a dump. |
| Third-party Actions pinned to tags, not commit SHAs | A moved tag could inject malicious code into a job that holds your SSH key. |
It also caught the classic PHP-plus-symlink gotcha: without the right nginx setting ($realpath_root), OPcache can keep serving old code after you flip the symlink. That's the kind of subtle, experience-earned detail that makes an AI reviewer genuinely valuable — not because it's magic, but because it's read every variation of this mistake.
The Security Angle Nobody Writes Down
One point Claude raised that I hadn't considered: once GitHub Actions can SSH into production, anyone who can push a workflow change can run code on your server. So the deploy key got its own dedicated keypair, restricted in authorized_keys (no-agent-forwarding,no-port-forwarding), and the deploy job was gated behind a protected GitHub environment. Your CI is only a safety gate if nothing can route around it.
Server Prep: Investigate, Then One Script
Before merging, the server needed preparing — release directories, a database-backup folder, the nginx storage path, a narrow passwordless-sudo rule for reloading PHP. True to the pattern, Claude did a read-only readiness report first, and it changed the plan: three things I'd assumed needed fixing were already correct. Had we skipped straight to scripting, we'd have installed a duplicate sudo rule and "fixed" a setting that was already right. The verify-first phase paid for itself again.
Then Claude generated one prep script with its now-familiar signature: timestamped backups, diff checkpoints, per-step verification, and printed (never auto-run) rollback commands. It even excluded Passport's private keys from a bulk permission change so they'd stay locked at 600 — a detail I'd have missed.
Merge, and the First Real Deploy
All checks green, PR merged. Both API and web pipelines fired. Watching the symlinks flip in real time was the payoff:
watch -n 2 'readlink /var/www/visa-saas/api; readlink /var/www/visa-saas/web'
# api → releases/20260712172016
# web → releases-web/20260712174334 ← it flipped!The First Failure (And Why It Didn't Matter)
The web deploy failed on its first attempt — an SSH i/o timeout uploading the release. My first instinct was "the hardening broke CI." But we checked the logs instead of guessing: the deploy key authenticated fine; it was just a transient network blip on one job. A re-run sailed through.
The crucial point: because of the release architecture, the site never went down during that failure. The old release stayed live until the new one was ready to flip. A failed deploy on this design is a non-event, not an outage. That safety net is the entire reason to build it this way.
Key Takeaways
- Timestamped releases + a symlink flip turn rollback from a crisis into one command.
- An AI code reviewer shines at catching the subtle, experience-earned mistakes — missing backups, wrong PHP, OPcache traps, supply-chain risks.
- When CI can reach production, treat the deploy key like the sensitive credential it is.
- Investigate-then-script saved us from "fixing" things that were already fine — twice.
- A good pipeline fails safely: the live site stays up until the new release is proven.
The pipeline worked. Both releases were live. And then I opened the site to admire it — and the login was broken. Not a little broken. A cascading, five-layers-deep broken that started as a blank page and ended at a one-word typo buried in a route file. That debugging journey — the most instructive part of the whole day — is Part 5.
👉 Coming up in Part 5: "The Login That Never Worked — Debugging Five Layers Deep with AI." What's the worst production bug you've shipped through a green pipeline?
Frequently Asked Questions
What is a zero-downtime deployment with symlinks?
Why should GitHub Actions use commit SHAs instead of version tags?
How do I safely give GitHub Actions SSH access to my server?
Should I back up my database before running migrations in a deploy?
Senior Full Stack Developer · Building SaaS products & teaching Laravel/React · 10+ years experience · Founder of Orion360 · Based in Dubai, UAE.
Was this post helpful?



