How Dewy Achieves Zero-Downtime Rolling Updates for Containers: A Deep Dive into the Source Code

Dewy achieves zero-downtime rolling updates by orchestrating a three-phase deployment (pull, replace, clean-up) while a TCP proxy continuously routes traffic to healthy containers, ensuring at least one replica is always serving requests.

The open-source deployment tool linyows/dewy implements a sophisticated container orchestration strategy that eliminates service interruptions during application updates. By combining incremental container replacement with dynamic proxy reconfiguration, Dewy ensures that incoming requests are never dropped during deployments. This article examines the specific implementation details in the Go source code that enable these zero-downtime rolling updates for containers.

The Three-Phase Rolling Update Architecture

Dewy's deployment logic in dewy.go implements a strict sequence that maintains service availability throughout the update process. The architecture separates concerns into distinct phases: pre-deployment preparation, incremental replacement, and post-deployment cleanup.

Phase 1: Pre-deployment Image Pulling

Before any running containers are touched, Dewy fetches the new image to ensure the update can proceed without delay:

// Pull the new image first
d.logger.Info("Pulling new image", slog.String("image", imageRef))
if err := dockerRuntime.Pull(ctx, imageRef); err != nil {
    return 0, fmt.Errorf("pull failed: %w", err)
}

Source: dewy.go lines 774-778

This approach ensures that the existing application version remains fully operational during the potentially slow image download process. If the pull fails, the deployment aborts before any containers are affected.

Phase 2: Incremental Container Replacement

After resolving port mappings and discovering existing containers, Dewy enters the core rolling update loop:

for i := 0; i < replicas; i++ {
    containerID, mappedPorts, err := d.startSingleContainer(...)
    // on error → rollback the ones already started
    d.addProxyBackend("localhost", mappedPort, proxyPort) // add to load balancer
}

Source: dewy.go lines 905-945

Each iteration performs three critical actions:

  1. Container Creation: startSingleContainer invokes dockerRuntime.Run to create and start a new container instance
  2. Health Verification: Before traffic admission, Dewy executes health checks via createHealthCheckFunc, retrying HTTP requests to http://localhost:<mappedPort><healthPath> until the container reports healthy
  3. Proxy Registration: Only after passing health checks does addProxyBackend register the container with the TCP proxy, making it eligible to receive traffic

This sequential approach ensures that the total number of healthy replicas never drops below the configured count, maintaining continuous service availability.

Phase 3: Graceful Cleanup of Legacy Containers

Once all new replicas are healthy and receiving traffic, Dewy removes the old containers in a controlled manner:

for i, oldContainerID := range existingContainers {
    // detach from proxy first
    d.removeProxyBackend("localhost", oldPort, mapping.ProxyPort)
    dockerRuntime.Stop(ctx, oldContainerID, 10*time.Second)
    dockerRuntime.Remove(ctx, oldContainerID)
}

Source: dewy.go lines 945-975

The critical sequence here is proxy removal before container termination. By calling removeProxyBackend before dockerRuntime.Stop, Dewy ensures that no new requests are routed to the container before it receives the shutdown signal. The 10-second grace period allows in-flight requests to complete before the container is forcibly removed.

TCP Proxy and Traffic Management

The zero-downtime guarantee relies heavily on Dewy's internal TCP proxy implementation, which decouples traffic routing from container lifecycle management.

Dynamic Backend Registration

The proxy maintains a dynamic list of active backends that can be modified without restarting the proxy process. When addProxyBackend is called during the rolling update, the new container's port is immediately added to the proxy's routing table, allowing it to receive a share of incoming traffic while old containers continue serving existing connections.

Health-Check Gating Before Traffic Admission

Dewy implements strict health-check gating to prevent premature traffic routing:

// Health check function creation
healthCheck := d.createHealthCheckFunc(...)
// Retries HTTP endpoint until healthy or timeout

Source: dewy.go lines 886-910

The health check function performs HTTP GET requests to the container's health endpoint, retrying several times before declaring the container ready. Only after passing this verification does the container join the proxy's backend pool, ensuring that users never see connection errors from partially initialized application instances.

Failure Handling and Rollback Mechanisms

Dewy's rolling update implementation includes robust failure handling to maintain service stability when deployments go wrong. If startSingleContainer fails during the rolling loop, Dewy immediately invokes rollbackContainers to remove any newly created containers and their proxy backends:

// Rollback on error
rollbackContainers(newContainerIDs...)

Source: dewy.go lines 1015-1035

This rollback mechanism ensures that if a new image fails to start or pass health checks, the deployment aborts without affecting the currently running containers. The original application version continues serving traffic uninterrupted, allowing operators to investigate the failure without service degradation.

Key Source Files and Implementation Details

Understanding Dewy's zero-downtime rolling updates requires familiarity with several key source files:

File Role
dewy.go Core deployment logic including the three-phase update sequence, health-check coordination, and proxy backend management. Contains startSingleContainer, rollbackContainers, and the main rolling loop.
container/docker.go / container/podman.go Runtime abstractions that interface with container engines. Implements Pull, Run, Stop, and Remove operations used during the update phases.
proxy.go (TCP proxy section) Implements tcpProxy, addProxyBackend, and removeProxyBackend. Manages the dynamic routing table that enables traffic shifting between old and new containers.
config.go Defines replica counts, port mappings, health-check paths, and grace periods that control the rolling update behavior.
README.md Documents high-level configuration options and deployment workflows for operators.

These components work together to implement a rolling update strategy that maintains continuous service availability while replacing container instances.

Summary

Dewy achieves zero-downtime rolling updates through a carefully orchestrated deployment process that maintains service availability at every stage:

  • Pre-deployment image pulling ensures new containers can start immediately without waiting for downloads, keeping old containers operational during the transfer.
  • Incremental container replacement starts new replicas one-by-one, verifying health before adding them to the proxy pool, ensuring replica count never drops below configured levels.
  • Proxy-driven traffic management uses a TCP proxy with dynamic backend registration to shift traffic instantly between old and new containers without connection drops.
  • Graceful cleanup removes old containers only after they are detached from the proxy, with configurable grace periods allowing in-flight requests to complete.
  • Automatic rollback aborts deployments and removes partial containers if any replica fails to start or pass health checks, preserving the original running version.

Frequently Asked Questions

How does Dewy ensure no requests are lost during container replacement?

Dewy uses a TCP proxy that manages traffic independently of container lifecycles. When replacing containers, Dewy first registers new containers with the proxy only after they pass health checks, then removes old containers from the proxy before stopping them. This ensures active connections complete during the grace period while new requests route only to healthy instances.

What happens if a new container fails to start during a rolling update?

If any container fails to start or pass health checks during the rolling update loop, Dewy immediately triggers a rollback mechanism. The rollbackContainers function removes any newly created containers and their proxy backends, leaving the original containers untouched and continuing to serve traffic. This prevents partial deployments from causing service degradation.

Can Dewy perform rolling updates with multiple replicas?

Yes, Dewy supports configurable replica counts via config.go and processes them sequentially in the rolling update loop. The system starts new containers one-by-one, adding each to the proxy pool after health verification, ensuring the total number of healthy replicas never drops below the configured count. This sequential approach maintains service availability even with single-replica deployments.

How does Dewy handle port mapping during updates?

Dewy automatically resolves port mappings through the resolvePortMappings function in dewy.go, which detects exposed ports from the container image or uses explicit configuration from config.go. During the rolling update, each new container receives its mapped ports, which are then registered with the TCP proxy via addProxyBackend, ensuring traffic routes to the correct dynamic ports without manual intervention.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →