Blue-Green Deploys for a Elixir App

Khimananda Oli 8 min read Programming and Languages
Blue-Green Deploys for a Elixir App

By Khimananda Oli | Last reviewed: August 2026

Shipping stateful BEAM applications without dropping connections requires more than a simple rolling update. Blue-Green Deploys for a Elixir App solve the specific challenge of maintaining long-lived WebSocket channels and GenServer state during version transitions by running two identical production environments in parallel. This approach eliminates the "drain timeout" failures common in Phoenix deployments and provides an instant, deterministic rollback mechanism that rolling updates cannot match. If you are managing critical real-time systems, understanding this pattern is essential before considering alternatives like canary or rolling strategies.

Blue-Green Architecture for ElixirBLUE (Active)Phoenix v1.7.10 PodsGenServer State AWebSocket ConnectionsGREEN (Standby)Phoenix v1.7.11 PodsGenServer State BNo Live TrafficShared PostgreSQLIngress / LB
High-level topology for Blue-Green Deploys for a Elixir App showing isolated application stacks sharing a single data layer.

Why choose Blue-Green Deploys for a Elixir App over rolling updates?

The Erlang VM (BEAM) is famous for reliability, but its deployment characteristics differ fundamentally from stateless HTTP services like Go or Node.js. Elixir applications, particularly those built with Phoenix LiveView or custom GenServers, maintain significant in-memory state. During a standard Kubernetes rolling update, pods are terminated sequentially. Even with preStop hooks and graceful shutdown periods, there is an inherent window where clients may reconnect to a terminating pod or experience interrupted TCP streams.

Blue-Green Deploys for a Elixir App address this by decoupling deployment from activation. The new version spins up completely independently, establishes its own supervision trees, warms up caches, and connects to external services before receiving a single user request. This is critical for teams needing to meet strict SLOs; as discussed in defining meaningful SLIs and SLOs, eliminating deployment-induced error spikes is often the easiest win for improving availability metrics.

  • Zero Connection Drops: Existing WebSocket clients stay on Blue until they naturally disconnect or are explicitly migrated; no forced RST packets during switchover.
  • Deterministic Rollback: Reverting is a metadata change at the Ingress level, not a re-deployment. Recovery time objective (RTO) drops from minutes to seconds.
  • Full Validation Window: You can run integration tests against the Green environment using production data patterns without risking user impact.
  • State Isolation: Avoids the "mixed version" state where some nodes run v1.7.10 and others v1.7.11, which can cause subtle message passing failures in distributed Elixir clusters.

How do you configure Kubernetes Services for Blue-Green Deploys for a Elixir App?

The implementation relies on label selectors rather than multiple physical clusters. You maintain two Deployments (myapp-blue and myapp-green) and a single Service that acts as the traffic switch. The key is ensuring your Elixir application exposes a robust health endpoint that verifies not just HTTP responsiveness, but actual BEAM readiness.

Define Separate Deployments with Version Labels

Your Helm chart or Kustomize overlay should parameterize the color label. Never use the same Deployment object and try to update it in place; that triggers a rolling update. Instead, create distinct resources.

<!-- k8s/deployment-green.yaml -->
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-green
  labels:
    app.kubernetes.io/name: myapp
    deploy.color: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: myapp
      deploy.color: green
  template:
    metadata:
      labels:
        app.kubernetes.io/name: myapp
        deploy.color: green
    spec:
      containers:
      - name: phoenix
        image: registry.example.com/myapp:v1.7.11
        ports:
        - containerPort: 4000
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 4000
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health/live
            port: 4000
          initialDelaySeconds: 30
          periodSeconds: 10

Implement the Traffic Switch Service

The Service selector targets only the active color. During deployment, you apply the new Deployment, wait for readiness, then patch the Service selector. This atomic label update is what makes Blue-Green Deploys for a Elixir App safe.

apiVersion: v1
kind: Service
metadata:
  name: myapp-active
spec:
  selector:
    app.kubernetes.io/name: myapp
    deploy.color: green  # PATCH THIS VALUE TO SWITCH TRAFFIC
  ports:
  - protocol: TCP
    port: 80
    targetPort: 4000
  type: ClusterIP

A common mistake is relying solely on Kubernetes' default readiness probe. For Elixir, you must implement a custom /health/ready endpoint that checks database connectivity, Redis pool status, and critical GenServer initialization. Without this, the pod reports "Ready" before the application has finished booting, causing early requests to fail. See Kubernetes resource limits and requests for proper sizing to ensure startup completes within probe windows.

Deployment Switchover SequenceCI/CD PipelineGreen StackK8s ServiceBlue Stack1. Apply Green Deploy2. Readiness Probe Passes3. Patch Service Selector → Green4. Blue Drains (Optional)5. Delete Blue Deploy (After Verification)
Temporal flow of Blue-Green Deploys for a Elixir App demonstrating atomic service selector updates and safe teardown.

How do you handle database migrations during Blue-Green Deploys for a Elixir App?

This is where most Elixir blue-green implementations fail. You cannot have two different application versions writing to incompatible schemas simultaneously. Since both Blue and Green connect to the same PostgreSQL instance during the transition window, your migrations must be backward-compatible.

  1. Decouple Migration from Release: Run mix ecto.migrate as a separate Job or initContainer before the Green deployment starts. Never run migrations inside the application startup script during blue-green transitions.
  2. Expand-Contract Pattern: If renaming a column, first add the new column (expand), deploy Green to write to both, then migrate data, then remove the old column (contract) in a subsequent release cycle.
  3. Idempotent Migrations: Ensure every migration can run safely multiple times. Use CREATE INDEX IF NOT EXISTS and check for column existence before altering.
  4. Lock Awareness: Ecto migrations acquire advisory locks. If Blue is still running heavy queries, Green's migration job might block. Schedule migrations during low-traffic windows or use CONCURRENTLY options for index creation.

For teams managing complex schema evolution, pairing this with PostgreSQL replication and high availability patterns ensures the migration target itself remains resilient during the dual-write phase. Always test migrations against a restored production snapshot before executing in the blue-green pipeline.

What are the trade-offs compared to other Elixir deployment strategies?

Blue-Green is not free. Understanding the cost-benefit ratio helps you decide when to apply it versus simpler methods. The table below compares approaches specifically for BEAM workloads in 2026.

CriteriaBlue-GreenRolling UpdateCanary
Downtime RiskNear-zero (atomic switch)Moderate (drain race conditions)Low (limited blast radius)
Resource Cost2x peak capacity required~1.2x (surge factor)1.1x–1.5x (subset)
Rollback SpeedInstant (selector revert)Slow (re-deploy previous image)Moderate (shift weight back)
State HandlingIsolated (clean slate)Mixed versions coexistMixed versions coexist
ComplexityMedium (infra duplication)Low (native K8s)High (traffic splitting + metrics)
Best ForCritical real-time / financialStateless APIs / batchUser-facing feature validation

In practice, I reserve Blue-Green Deploys for a Elixir App for systems where connection stability directly impacts revenue or compliance. For internal tools or batch processors, rolling updates with proper preStop hooks are sufficient. The 2x resource cost is significant for Nepal-based startups operating on tight NPR budgets, but for global SaaS products serving enterprise clients, the insurance policy against botched releases pays for itself after one prevented outage.

Strategy Trade-off MatrixBlue-GreenHigh Resource Cost | Lowest Risk | Instant RollbackCanaryModerate Cost | Low Risk | Metric-DrivenRollingLow Cost | Moderate RiskResource Footprint →Recommendation: Use Blue-Green for stateful BEAM apps where uptime > cost efficiency.
Visual comparison of operational trade-offs when selecting Blue-Green Deploys for a Elixir App versus alternative strategies.

Automating Blue-Green Deploys for a Elixir App Safely

Manual kubectl patching is unacceptable for production. Automate the switch with verification gates. Your CI/CD pipeline should follow this exact sequence:

  1. Deploy Green: Apply the new Deployment manifest. Wait for kubectl rollout status deployment/myapp-green --timeout=300s.
  2. Smoke Test: Execute a synthetic transaction suite against the Green service endpoint directly (bypassing the main Ingress). Verify critical paths: auth, DB read/write, WebSocket handshake.
  3. Switch Traffic: Atomically update the Service selector. Log the timestamp and previous selector value for audit trails.
  4. Monitor Error Budget: Watch error rates and latency for 5–10 minutes post-switch. If thresholds breach, auto-revert the selector.
  5. Teardown Blue: Only after the verification window passes, delete the old Deployment. Keep it scaled to zero for 24 hours as a warm backup if storage permits.

Integrate this with your observability stack. As detailed in Prometheus and Grafana full monitoring stack setups, you should have alerts specifically tuned to detect post-deployment anomalies. For Elixir, monitor vm_memory_total, phoenix_channel_join_count, and ecto_query_duration immediately after switchover. Automated rollback triggered by these signals transforms Blue-Green Deploys for a Elixir App from a manual procedure into a self-healing system.

Final Considerations for Production Elixir Systems

Blue-Green Deploys for a Elixir App provide the highest safety guarantee for stateful BEAM workloads, but they demand discipline in migration management and resource planning. Start by implementing the dual-deployment pattern in staging, validate your health checks catch real boot failures, and only promote to production once your automated rollback has been tested under load. The upfront infrastructure cost is the price of predictable releases. If your team needs help designing a compliant, audit-ready deployment pipeline for Elixir or other cloud-native systems, reach out to discuss your architecture.

Frequently Asked Questions

It runs two identical production environments, switching traffic instantly between old and new Elixir releases to eliminate downtime during updates.

Rolling updates nodes gradually causing mixed versions, while blue-green switches all traffic atomically after validating the complete new environment.

HAProxy or NGINX with upstream toggling handles TCP/HTTP switching reliably, preserving WebSocket connections common in Phoenix applications during cutover.

No, both environments share one database. Use Ecto migrations with backward-compatible schema changes to prevent breaking the active blue environment.

Configure graceful shutdowns allowing existing sockets to drain before stopping green nodes, preventing abrupt client disconnections during the traffic flip.

Distillery or Elixir Releases with custom boot scripts enable atomic version switching and pre-start health checks required for safe green validation.

Run automated smoke tests against the green cluster’s internal endpoint, verifying database connectivity, RPC calls, and critical business logic paths.

Yes, temporarily. Provisioning identical staging capacity increases compute spend by roughly 100% during deployment windows only if not using autoscaling.

Retain blue for at least thirty minutes to allow instant rollback if latent bugs surface in the green release under real traffic.

Yes, using Argo Rollouts or Flagger automates traffic shifting between ReplicaSets while respecting Phoenix readiness probes and connection draining timeouts.

Load balancers stop sending new requests to blue but allow existing ones to complete, ensuring zero dropped transactions during the cutover moment.

Use Vault or AWS Secrets Manager to inject identical credentials into both clusters, avoiding config drift that causes post-switch authentication failures.

Often yes. Simpler rolling deploys suffice until you require zero-downtime guarantees or have complex stateful dependencies demanding atomic version transitions.

Expose dedicated /health endpoints checking Postgres, Redis, and cluster membership, queried by CI pipelines before authorizing the load balancer switch.

Forgetting to synchronize Mnesia or Erlang distribution cookies between environments causes silent RPC failures when green nodes attempt cross-cluster communication.