Skip to content

Blue-Green Deployments with API Gateways — Zero-Downtime Releases

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you'll learn about Blue-Green. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Blue-green deployment uses the gateway to instantly switch traffic between two identical environments, enabling zero-downtime releases with immediate rollback capability.

What You'll Learn

By the end of this lesson, you will implement blue-green environment switching at the gateway, manage database compatibility across environments, drain connections gracefully, and automate rollback.

Why It Matters

Blue-green deployments eliminate downtime during releases and provide instant rollback by simply switching traffic back to the previous environment.

Real-World Use

Durga Antivirus Pro maintains blue (current) and green (staging) environments, with the gateway switching all traffic to green within seconds after validation.

Blue-Green Architecture

flowchart LR
    Traffic-->Gateway
    Gateway-->Switch{Active Environment}
    Switch-->|Blue Active|Blue[Blue Environment]
    Switch-->|Green Active|Green[Green Environment]
    Blue-->Backend1
    Green-->Backend2

Environment Router

Route all traffic to the active environment with instant switching.

from typing import Dict, Optional, Tuple
import time

class BlueGreenRouter:
    def __init__(self, blue_host: str, green_host: str):
        self.blue_host = blue_host
        self.green_host = green_host
        self.active: str = "blue"
        self.previous: str = "green"
        self.switch_history: list = []

    def get_active_host(self) -> str:
        return self.blue_host if self.active == "blue" \
            else self.green_host

    def get_staging_host(self) -> str:
        return self.green_host if self.active == "blue" \
            else self.blue_host

    def switch(self) -> Dict:
        self.previous = self.active
        self.active = "green" if self.active == "blue" \
            else "blue"
        switch_event = {
            "timestamp": time.time(),
            "from": self.previous,
            "to": self.active,
        }
        self.switch_history.append(switch_event)
        return switch_event

    def rollback(self) -> Dict:
        return self.switch()

    def get_status(self) -> Dict:
        return {
            "active": self.active,
            "active_host": self.get_active_host(),
            "staging_host": self.get_staging_host(),
            "last_switch": self.switch_history[-1]
            if self.switch_history else None,
        }

    def route(self, path: str) -> str:
        return self.get_active_host()

router = BlueGreenRouter("blue-svc:8080", "green-svc:8080")
print(f"Active: {router.get_active_host()}")
switch = router.switch()
print(f"Switched to: {router.get_active_host()}")
print(f"Switch event: {switch}")

Connection Draining

Drain active connections before switching environments.

from typing import Dict, Set, Optional
import time
import threading

class ConnectionDrainer:
    def __init__(self, drain_timeout: int = 30):
        self.active_connections: Dict[str, Set[str]] = {}
        self.drain_timeout = drain_timeout
        self.draining = False

    def register_connection(self, env: str,
                            conn_id: str):
        if env not in self.active_connections:
            self.active_connections[env] = set()
        self.active_connections[env].add(conn_id)

    def remove_connection(self, env: str,
                          conn_id: str):
        if env in self.active_connections:
            self.active_connections[env].discard(conn_id)

    def drain_environment(self, env: str) -> Dict:
        connections = self.active_connections.get(env, set())
        if not connections:
            return {"status": "no_connections", "drained": 0}
        self.draining = True
        start = time.time()
        while connections and \
                time.time() - start < self.drain_timeout:
            time.sleep(0.5)
            connections = self.active_connections.get(
                env, set()
            )
        remaining = len(connections)
        return {
            "status": "drained" if remaining == 0
            else "timeout",
            "drained": len(connections) - remaining,
            "remaining": remaining,
            "duration": time.time() - start
        }

    def health_check(self, host: str) -> bool:
        return True

drainer = ConnectionDrainer(drain_timeout=5)
drainer.register_connection("blue", "conn-1")
drainer.register_connection("blue", "conn-2")
result = drainer.drain_environment("blue")
print(f"Drain result: {result}")

Database Compatibility Check

Ensure the new environment's database schema is compatible before switching.

from typing import Dict, List, Optional, Tuple

class DBCompatibilityChecker:
    def __init__(self):
        self.required_migrations: List[str] = []
        self.breaking_changes: List[str] = []

    def add_required_migration(self, migration: str):
        self.required_migrations.append(migration)

    def add_breaking_change(self, change: str):
        self.breaking_changes.append(change)

    def check_environment(self, env: str
                          ) -> Tuple[bool, List[str]]:
        issues = []
        for migration in self.required_migrations:
            if not self._migration_applied(env, migration):
                issues.append(
                    f"Migration {migration} not applied on {env}"
                )
        for change in self.breaking_changes:
            if self._change_present(env, change):
                issues.append(
                    f"Breaking change {change} present on {env}"
                )
        return len(issues) == 0, issues

    def _migration_applied(self, env: str,
                           migration: str) -> bool:
        return True

    def _change_present(self, env: str,
                        change: str) -> bool:
        return False

checker = DBCompatibilityChecker()
checker.add_required_migration("V20260628_add_scan_status")
checker.add_breaking_change("DROP COLUMN old_format")
ok, issues = checker.check_environment("green")
print(f"DB compatible: {ok}, issues: {issues}")

Common Mistakes

Mistake 1: Long-Lived Connections

Websocket and long-poll connections survive environment switches. Drain them before switching.

Mistake 2: Database Backward Incompatibility

If the new schema is incompatible with the old code, rollback is impossible. Ensure both directions work.

Mistake 3: Cache Warm-up Delay

Cold caches in the new environment cause slow responses. Pre-warm caches before switching.

Mistake 4: Not Testing the Switch Process

Test the switch itself. Failures in the switch mechanism cause outage even if both environments are healthy.

Mistake 5: Configuration Drift

If environments have different configurations, behavior changes unexpectedly. Use infrastructure as code.

Practice Questions

  1. How does blue-green differ from Canary Deployment?
  2. What is connection draining and why is it necessary?
  3. Why must blue-green environments share the database?
  4. How do you handle cache warm-up in blue-green?
  5. What is the rollback procedure for blue-green?

Challenge

Build a blue-green deployment manager for the gateway that supports instant environment switching, connection draining with configurable timeout, health checks before switching, and automatic rollback if the new environment is unhealthy.

FAQ

What is blue-green deployment?

Blue-green deployment maintains two identical environments. Traffic is routed to the active one. The new version is deployed to the inactive environment, tested, and then traffic is switched.

How long does a blue-green deployment take?

The switch itself takes seconds. The total deployment takes as long as it takes to deploy and validate the inactive environment, typically 5-15 minutes.

Do blue-green environments share databases?

Yes. Both environments connect to the same database. Schema changes must be backward compatible to allow rollback.

How do you handle cache invalidation after a switch?

Pre-warm the new environment's caches before switching. After switching, the old environment's caches expire naturally.

What is the cost of maintaining two environments?

Since only one environment serves traffic at a time, you pay for double the infrastructure. Use auto-scaling to minimize idle capacity costs.

Mini Project

Build a blue-green deployment system for the gateway that maintains two environment configurations, supports instant switching with a single API call, drains active connections before switching, performs health checks on the new environment, and provides instant rollback capability.

What's Next

Learn about Canary Deployments for gradual traffic shifting, or explore Gateway Testing for comprehensive test strategies.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro