The Elastic Beanstalk Problem at Scale

Visor.ai builds AI-powered customer service automation. Their platform processes natural language, manages conversation flows and integrates with enterprise contact centres. As the product matured and the customer base grew, the infrastructure underneath had to carry an increasingly complex workload.

The company had built on AWS Elastic Beanstalk from the early stages. Beanstalk is a reasonable starting point: it abstracts away much of the deployment and provisioning overhead, letting small teams move fast. But as Visor.ai added services, each new application meant a new Beanstalk environment, with its own load balancer, its own EC2 fleet, its own configuration and its own scaling rules.

By the time Out.Cloud was engaged, the estate had grown into a constellation of duplicated resources. Every Beanstalk environment was essentially a standalone stack. The same infrastructure patterns were repeated across each one, none shared a common runtime, and operational changes had to be applied individually across environments.

"The issue was not that Beanstalk was broken. It was that our growth had turned a simple deployment model into an expensive management problem. Every new service we launched added another isolated stack with its own infrastructure overhead."

Engineering Lead, Visor.ai

Our Approach: Platform-First Migration

The instinct in many migration projects is to lift and shift, to move workloads from one environment to another with as little change as possible. We recommended a different approach for Visor.ai: use the migration as an opportunity to redesign how applications are deployed and operated.

The target was Amazon EKS (Elastic Kubernetes Service), but the value of the migration was not Kubernetes itself. It was what a shared, managed orchestration layer made possible: consolidated resource allocation, fine-grained autoscaling, a single control plane for all services, and a deployment model that would scale with the business instead of against it.

Assessing the Beanstalk Estate

Before writing any migration plan, we conducted a full audit of the existing Beanstalk environments. The goal was to understand exactly what was running, how resources were allocated, and where the duplication was costing real money. The findings were clear:

  • Multiple Beanstalk environments were running near-identical infrastructure configurations
  • Each environment provisioned its own Application Load Balancer, even when traffic patterns would have allowed consolidation
  • EC2 instances were sized for peak capacity per-environment, resulting in significant underutilisation during normal operations
  • Configuration management was fragmented, with no single source of truth for deployment settings
  • Operational visibility was scattered across independent CloudWatch dashboards and log groups

The assessment confirmed what the Visor.ai team had suspected: the cost of operating the estate was growing linearly with each new service, with no economies of scale.

EKS Target platform
Zero Resource duplication
3x Scalability improvement

Migration Execution

We structured the migration in three phases, each designed to deliver incremental value while reducing risk. At no point was the production platform left in an ambiguous state between old and new.

Phase 1: Containerisation and Standardisation

The first step was to containerise every Beanstalk application. This was not a simple "wrap it in Docker" exercise. We took the opportunity to standardise base images, health check patterns, logging formats and configuration injection across all services. Each container was built to run identically in any Kubernetes environment, with no Beanstalk-specific dependencies remaining.

We created a shared container base image with security hardening, structured logging and health endpoints baked in. Every service team adopted the same base, which meant operational consistency from day one on EKS.

Phase 2: EKS Cluster Design

The EKS cluster was designed around Visor.ai's actual workload characteristics, not generic best practices. Key architecture decisions included:

  • Node group segmentation: CPU-intensive AI inference workloads were placed on dedicated node groups with appropriate instance types, while API and web services shared general-purpose nodes
  • Horizontal Pod Autoscaler (HPA): every service was configured with HPA based on real traffic metrics, replacing Beanstalk's coarser-grained Auto Scaling
  • Shared ALB Ingress Controller: a single Application Load Balancer now serves all services, with path-based routing replacing the one-ALB-per-environment model
  • Namespace isolation: services are isolated by namespace with appropriate RBAC policies, providing security boundaries without the overhead of separate clusters

Target EKS architecture: shared ingress, per-service autoscaling, segmented node groups for AI and general workloads.

Phase 3: Controlled Cutover

The migration was not a big-bang event. We moved services to EKS one at a time, starting with the lowest-risk internal services and working towards the customer-facing AI engine. Each cutover followed the same pattern:

  • Deploy the containerised service to EKS alongside the existing Beanstalk environment
  • Run both in parallel with traffic mirroring to validate behaviour parity
  • Shift traffic to EKS via DNS-weighted routing, starting at 10% and increasing over 48 hours
  • Decommission the Beanstalk environment only after full traffic has been stable on EKS for a defined period

This approach meant that at no point was the production platform at risk. If any service showed unexpected behaviour on EKS, traffic could be routed back to Beanstalk within minutes.

Architecture Decisions That Mattered

Several architecture decisions during the migration had outsized impact on the final result. These were not default Kubernetes settings; they were choices made specifically for Visor.ai's workload profile.

Right-sizing for AI Workloads

Visor.ai's NLP inference services have very different resource characteristics from their API layer. On Beanstalk, both were running on the same instance types because each environment was managed independently. On EKS, we created dedicated node groups with GPU-optimised instances for inference workloads, while API and background processing services ran on cost-effective general-purpose nodes. This alone produced measurable cost savings.

Cluster Autoscaler with Karpenter

We deployed Karpenter for node provisioning instead of the standard Cluster Autoscaler. Karpenter's ability to provision the right instance type for each pending pod, and to consolidate underutilised nodes, gave Visor.ai a level of infrastructure efficiency that was impossible with Beanstalk's per-environment scaling model.

Observability from Day One

On Beanstalk, every environment had its own CloudWatch log group and its own set of dashboards. We replaced this with a unified observability stack: centralised logging, distributed tracing across all services, and a single set of operational dashboards covering the entire platform. For the first time, the Visor.ai team could see all their services in one place.

Outcomes After Migration

The migration delivered results across three dimensions that matter to a growing AI company: reliability, scalability and cost.

  • Increased Reliability: Applications running on EKS benefit from Kubernetes self-healing, automatic pod rescheduling and rolling deployments. Beanstalk's health management was adequate but coarser-grained, and recovery from failed deployments required manual intervention
  • Increased Scalability: EKS provides fine-grained control over scaling at both the pod and node level. Visor.ai can now scale individual services independently based on actual demand, rather than scaling entire environments
  • Cost Optimisation: The elimination of duplicated load balancers, the right-sizing of compute for different workload types, and Karpenter's bin-packing efficiency all contributed to measurable cost reduction. The exact savings depend on workload volume, but the architectural change from N isolated stacks to one shared cluster fundamentally changed the cost curve

"The migration was not just about moving to Kubernetes. It was about building a platform that could grow with us. On Beanstalk, every new service we launched made the estate harder to manage. On EKS, every new service benefits from the shared infrastructure we have already built."

Engineering Lead, Visor.ai

What This Means for Teams on Elastic Beanstalk

Elastic Beanstalk is a good tool for the stage of company it was designed for. But there is a point, and most growing engineering teams recognise it, where the per-application isolation model starts working against you. The duplication that was invisible at three services becomes a real cost at ten or twenty.

The lesson from Visor.ai is not "migrate to Kubernetes." The lesson is: when your infrastructure cost and complexity are growing linearly with your service count, you have an architecture problem that no amount of operational effort will solve. The fix is a shared platform layer that gives you economies of scale.

EKS happened to be the right answer for Visor.ai because they were already invested in AWS and needed a managed Kubernetes service that would not add operational burden. The specific tool matters less than the principle: consolidate your runtime, share your infrastructure, and let your platform team focus on making the shared layer excellent instead of maintaining N copies of the same thing.