Sathsara K.
  • Home
  • About
  • Projects
  • Case Studies
  • Blog
  • Contact
Hire me
  • Home
  • About
  • Projects
  • Case Studies
  • Blog
  • Contact
Sathsara K.
© 2026 Sathsara. Built with intent.
Back to Blog
Stop SSH-ing Into Production: Build Nginx & Docker CI/CD
DevOpsMay 12, 20264 min read

Stop SSH-ing Into Production: Build Nginx & Docker CI/CD

Why manual deployments are a risk and how to construct zero-downtime deployment loops using Jenkins, GitHub Actions, Nginx reloads, and container orchestration.

Table of Contents

  • • The Risk of SSH Deployments
  • • Designing a Zero-Downtime Deployment Cycle
  • ↳ 1. Build and Test (GitHub Actions)
  • ↳ 2. Package and Push (Jenkins)
  • ↳ 3. Deploy and Switch (Nginx Reloads)
  • ↳ Why "Blue" Never Actually Disappears
  • ↳ What the Health Check Actually Has to Verify
  • • What This Replaces, Concretely
  • • The Core Lesson

How a manual `git pull` deployment habit turns into downtime, and how a Jenkins-driven blue-green pipeline with Nginx removes the risk entirely.

Server rack with green status lights
Server rack with green status lights

The Risk of SSH Deployments

There's a familiar ritual on a lot of small and mid-size teams: something needs to ship, so someone SSHs into the production box, runs git pull, maybe rebuilds a container, and restarts the process. It works, right up until it doesn't.

Deploying code manually by running git pull or building containers on production servers is highly error-prone. One syntax mistake, missing configuration flag, or port conflict immediately results in downtime. And the failure isn't hypothetical. It's the natural result of doing a repeatable, mechanical process by hand, under time pressure, directly on the one machine your users depend on.

A few reasons this pattern keeps biting teams even after everyone "knows better":

  • There's no undo button. If the new code breaks something mid-pull, the old code is already half-overwritten. Rolling back means reconstructing what was there before, by hand, while the site is down.
  • The server is a single point of failure during the deploy itself. The moment you restart the process to pick up new code, there's a window (however small) where nothing is listening on that port at all.
  • It doesn't scale past one person. Manual deploys depend on whoever's doing it remembering every step, every time. Add a second or third engineer and you've added a second or third way for the process to be done slightly differently.
  • It leaves no audit trail. When something breaks at 2 AM, "someone ran some commands on the server" is a much worse starting point for debugging than a pipeline log showing exactly what was built, tested, and deployed, and when.
  • The fix isn't "be more careful next time." It's removing the manual step from the critical path entirely.

    Designing a Zero-Downtime Deployment Cycle

    Sathsara implemented a clean pipeline layout using Jenkins and Nginx reverse proxies. The pipeline has three distinct stages, and each one exists to catch a different category of mistake before it can reach a real user.

    1. Build and Test (GitHub Actions)

    Every code branch push triggers automatic lint checking, syntax validation, and unit test suites. This stage runs on every branch, not just main, which matters: it means a broken change gets flagged the moment it's pushed, long before anyone is deciding whether to merge it. By the time a pull request is ready for review, the question "does this even run?" has already been answered by a machine, not by a human reading a diff.

    2. Package and Push (Jenkins)

    Once code is merged to main, Jenkins builds a lightweight Docker container image and publishes it to a secure repository. This step is what turns "a bunch of source files" into a single, immutable, versioned artifact. That distinction matters more than it sounds like it should: a Docker image either builds successfully or it doesn't, and once it's built, it's the exact same image whether it runs on a laptop, a staging server, or production. There's no "well it worked on my machine" gap between what was tested and what gets deployed, because the tested artifact and the deployed artifact are literally the same file.

    3. Deploy and Switch (Nginx Reloads)

    This is the stage that actually removes downtime from the equation, and it's built on a pattern called blue-green deployment.

    Instead of overwriting the active container, Jenkins boots up the new version on an alternate port:

  • Active version runs on port 3001 (Blue).
  • New version starts up on port 3002 (Green).
  • Once the health check passes, Jenkins modifies the Nginx reverse proxy routing file and reloads Nginx dynamically.
  • bash
    # Reloading Nginx without dropping traffic
    sudo nginx -s reload

    The reason nginx -s reload is the linchpin of this whole approach is worth spelling out. When Nginx receives this signal, it doesn't drop its listening socket and restart from scratch. It spins up new worker processes with the updated configuration while the old worker processes finish handling whatever requests they were already mid-flight on, and only then does it retire them. There is no moment where the socket is closed and nothing is listening. From the outside, a client mid-request never sees a connection refused or reset; they either get served by the old workers or the new ones, but never nothing.

    Why "Blue" Never Actually Disappears

    The Blue container isn't deleted the instant Green takes over traffic. It's usually left running (stopped, not removed) for a rollback window. If the health check that gated the traffic switch turns out to have missed something, and Green starts erroring out under real production load in a way it didn't during the check, rolling back is just pointing Nginx's config back at port 3001 and reloading again. That's the same one command, nginx -s reload, and it's just as fast in reverse as it was going forward.

    This is the actual payoff of the pattern: it doesn't just prevent downtime on the way up, it makes going back down (or rather, back to the previous version) equally cheap. A deployment strategy that only handles the happy path isn't really solving the problem manual SSH deploys have; it's just automating the same fragility.

    What the Health Check Actually Has to Verify

    The whole safety of this pipeline hinges on one gate: Jenkins only flips the Nginx routing after Green's health check passes. That check needs to do more than confirm the process started. A container can be "up" (accepting TCP connections) while the application inside it is still failing every real request, still connecting to a database, or still loading a large model into memory. A meaningful health check hits an actual application endpoint, expects a real success response, and retries a few times with a short delay rather than checking once and moving on. Skipping this step, or making it too shallow, is how teams end up with a "zero-downtime" pipeline that still ships a broken Green environment straight to production, just without the SSH session.

    What This Replaces, Concretely

    It's worth being explicit about what changes for the person deploying code, because the value here isn't abstract:

    Manual SSH DeployNginx + Docker CI/CD
    Who runs the deployA person, by handJenkins, from a defined pipeline
    What gets testedWhatever the person remembers to checkEvery push, automatically
    Downtime during deployA restart window, however briefNone; old version serves until new version is verified
    RollbackManual reconstructionOne config reload back to the previous port
    Audit trailShell history, if anyone thinks to checkPipeline logs, every time

    None of these individual pieces (Docker, Nginx, Jenkins, health checks) are exotic. The value is in wiring them together so that the dangerous, error-prone step, a human making a judgment call under pressure on a live server, gets replaced by a script that does the same three things, in the same order, every single time.

    The Core Lesson

    Downtime from deployments is rarely a tooling problem. It's a process problem wearing a tooling costume. git pull on a production box works exactly as well as the person running it that day, and that's precisely the failure mode a pipeline is designed to remove: not by making people more careful, but by making the deploy itself boring, repeatable, and reversible. Blue-green with Nginx doesn't make deployments risk-free. It makes the risk small, contained to a health check, and trivially reversible, which is the most any deployment strategy can honestly promise.

    All ArticlesDevOps
    Sathsara

    Sadeesha Sathsara

    Backend & DevOps Developer

    Writing on async systems design, automated CI/CD pipelines, containerization, and backend patterns.

    Recommended Reads

    What Is Identity and Access Management (IAM)? A Beginner's ExplanationJuly 19, 2026 · 4 min readAre Roles the Same as Identities in IAM? A Clear BreakdownJuly 19, 2026 · 9 min readAuthentication vs Authorization: Security Best PracticesJuly 11, 2026 · 6 min readKPIs Explained: The Dashboard Numbers That Actually Run a BusinessJuly 3, 2026 · 8 min read

    Keep Reading

    More Articles

    What Is Identity and Access Management (IAM)? A Beginner's Explanation
    ArticleSoftware Engineering
    July 19, 2026

    What Is Identity and Access Management (IAM)? A Beginner's Explanation

    Read
    Are Roles the Same as Identities in IAM? A Clear Breakdown
    ArticleSoftware Engineering
    July 19, 2026

    Are Roles the Same as Identities in IAM? A Clear Breakdown

    Read
    Authentication vs Authorization: Security Best Practices
    ArticleSoftware Engineering
    July 11, 2026

    Authentication vs Authorization: Security Best Practices

    Read
    KPIs Explained: The Dashboard Numbers That Actually Run a Business
    ArticleBusiness
    July 3, 2026

    KPIs Explained: The Dashboard Numbers That Actually Run a Business

    Read
    Data Visualization Is Not Decoration, It Is Business Storytelling
    ArticleBusiness
    July 3, 2026

    Data Visualization Is Not Decoration, It Is Business Storytelling

    Read
    How Money Really Flows Through a Business
    ArticleBusiness
    July 3, 2026

    How Money Really Flows Through a Business

    Read
    What Is Data, Really? Understanding How Businesses Turn Real Life Into Records
    ArticleBusiness
    July 3, 2026

    What Is Data, Really? Understanding How Businesses Turn Real Life Into Records

    Read
    Data Analysis Is Not About Tools, It Is About Reading a Business Through Its Data
    ArticleBusiness
    July 3, 2026

    Data Analysis Is Not About Tools, It Is About Reading a Business Through Its Data

    Read
    Building Async CV Microservices with RabbitMQ & OpenCV
    ArticleMicroservices
    June 30, 2026

    Building Async CV Microservices with RabbitMQ & OpenCV

    Read
    From Monolith to Event-Driven Microservices: An Architectural Blueprint
    ArticleArchitecture
    June 28, 2026

    From Monolith to Event-Driven Microservices: An Architectural Blueprint

    Read