How a B2B SaaS Team Migrated a Rails Monolith to EKS With Zero Downtime
A 120-engineer SaaS company moved a decade-old Rails app from hand-managed EC2 to Amazon EKS using a strangler-fig approach, cutting deploy time from 47 minutes to 4 and eliminating weekend outages.
Containers & K8s · intermediate · 12 min read · 2450 words · By Sandip Das · Published
Composite scenario: built from common industry patterns; the figures are illustrative.
What this covers
- The Situation
- The Problem
- Constraints
- Options Considered
- The Solution
- Implementation
- Results
- What Went Wrong Along the Way
- Lessons Learned
- Apply It Yourself
Key numbers
| Metric | Before | After |
|---|---|---|
| End-to-end deploy time | 47 minutes | 4 minutes |
| Monthly compute spend | $28,400 | $19,100 |
| Compute-related incidents (6-month window) | 9 incidents | 0 in first 90 days |
| P99 response time (baseline) | 340 ms | 290 ms |
| Scale-out time (plus 8 nodes equivalent) | ~12 minutes (ASG) | 90 seconds (HPA) |
Key components
- Amazon EKS Managed Node Groups — Replaced the hand-managed EC2 fleet with self-healing, automatically patched compute that the Cluster Autoscaler can grow or shrink in under two minutes.
- Weighted ALB Target Groups — Enabled incremental traffic migration from EC2 to EKS in percentage steps, providing a one-click rollback path at every stage of the cutover.
- Argo CD (GitOps) — Made every deployment a Git commit, providing an auditable history of every config change and eliminating configuration drift between environments.
- Horizontal Pod Autoscaler — Replaced the static EC2 fleet with demand-driven scaling, reducing idle compute cost by matching pod count to actual traffic load.
- PgBouncer (connection pooling) — Prevented RDS max_connections exhaustion by multiplexing hundreds of pod-level database connections into a fixed pool of 80 server-side connections.
- Fluent Bit DaemonSet — Collected container stdout logs from every node and forwarded them to Datadog Logs without requiring any changes to the Rails application's logging configuration.
The Situation
This is a composite scenario assembled from patterns observed across multiple production migrations; all figures are illustrative and chosen for internal consistency, not to represent any single organisation.
The subject is a 120-engineer B2B SaaS company that sells workflow-automation software to mid-market finance teams. The product is a single Rails 6.1 monolith that has been in production since 2014. At the time of the migration it handled roughly 18 million requests per day, backed by a 1.4 TB RDS PostgreSQL 14 instance and a six-node ElastiCache Redis cluster. The application ran on a fleet of 22 c5.2xlarge EC2 instances managed with a combination of Capistrano, Ansible, and hand-edited launch templates. Sidekiq workers ran on a separate fleet of eight c5.xlarge instances. There was no container runtime anywhere in the stack.
The engineering organisation had three platform engineers, twelve backend engineers who touched the Rails codebase daily, and a director of engineering who had just come from a company running Kubernetes in production. The on-call rotation covered six engineers.
---
The rest of this case study — the trade-offs, the scaling numbers and the build prompt — is available to LearnXops members. Create an account or see membership.
Topics: kubernetes, rails, aws, containers, migration, zero-downtime
Related case studies
- How a B2B SaaS Platform Halved Kubernetes Compute Costs with Karpenter and Right-Sized Workloads
- How a B2B SaaS Platform Upgraded 40 Kubernetes Clusters Across Three Versions Without an Outage
- How a New Telemetry Service Took Down OpenAI's Kubernetes API Servers and DNS in December 2024
- How One Platform Team Tamed 30 Product Teams on a Single Kubernetes Cluster
- The Pi Day Outage: How a Renamed Kubernetes Node Label Took Reddit Down for Five Hours