Skip to content
TutorialDevOps

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

D

Dinesh Wijethunga

August 22, 2026Reviewed Jul 12, 2026 5 min readBeginner
ShareX / TwitterLinkedIn
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 frontend

Deploy = 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 flaggedWhy it mattered
Web deploy did rm -rf then move — no rollbackThe 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 versionsMigrations could run under the wrong PHP than the site serves.
Migrations with no database backup firstA symlink rollback can't undo a schema change — you need a dump.
Third-party Actions pinned to tags, not commit SHAsA 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?
Each deploy lands in a new timestamped release folder instead of overwriting the live app. A symlink points to the current release, and deploying just flips that symlink to the new folder. Because the switch is instant and the old release stays intact, there's no downtime and rollback is a single command — flip the symlink back.
Why should GitHub Actions use commit SHAs instead of version tags?
A version tag like @v1 can be moved by the action's author to point at different code, so a compromised action could inject malicious code into a job that holds your SSH key. Pinning to a full commit SHA locks the action to exact, unchangeable code. This matters most for third-party actions that touch your credentials.
How do I safely give GitHub Actions SSH access to my server?
Use a dedicated SSH keypair only for deployment, never your personal key. Restrict it in authorized_keys with options like no-agent-forwarding and no-port-forwarding, and gate the deploy job behind a protected GitHub environment. Remember: anyone who can push a workflow change can run code on your server, so treat CI access as production access.
Should I back up my database before running migrations in a deploy?
Yes. A symlink rollback reverts your code, but it cannot undo a database schema change from a migration. Add a step that dumps the database to a backup file before running migrations, so you have a restore point if a migration causes problems. Code rollback and data rollback are separate safety nets.
D
Dinesh Wijethunga

Senior Full Stack Developer · Building SaaS products & teaching Laravel/React · 10+ years experience · Founder of Orion360 · Based in Dubai, UAE.

Was this post helpful?

Add a comment

Comments

Guest comments are held for moderation.

You might also like

Deploy Laravel to a VPS with Laravel Forge: Complete Walkthrough
TutorialIntermediateDevOps

Deploy Laravel to a VPS with Laravel Forge: Complete Walkthrough

Laravel Forge isn't a host — it turns any VPS into a managed Laravel server. Full walkthrough: provisioning, GitHub push-to-deploy, the deploy script, one-click SSL, queue workers, the scheduler, and an honest look at zero-downtime options.

D
Dinesh Wijethunga
5 months ago
6m