Technical Deep DiveInfrastructure

    From AWS ECS to OVH Kubernetes:
    A Startup's Migration Story

    When your AWS bill starts rivaling your engineering salaries, it's time to reevaluate. This article documents our migration from a multi-tenant AWS ECS architecture to a self-managed Kubernetes cluster on OVH bare-metal nodes.

    By Zachary Philips-Gary

    70% Cost Reduction
    Without sacrificing reliability + newfound flexibility

    We cover the cost drivers that forced the decision, the technical migration process, and the tradeoffs every engineering team should consider before making a similar move.

    Who This Is For

    This guide is for early-stage startups and small technical teams who need to run open-source tooling (workflow automation, documentation systems, analytics dashboards) for internal operations or multi-tenant client deployments. If you're a seed-to-Series A company watching your AWS bill climb toward five figures monthly while serving fewer than fifty clients, this is likely familiar, and the details below may help you decide what to do next.

    The Cost Trap of Per-Service Cloud Pricing

    Startups building internal tooling or client-facing platforms face a familiar crossroads: pay SaaS subscription fees that scale linearly with users, or self-host open-source alternatives and manage the infrastructure yourself. The AWS Well-Architected Framework's Cost Optimization Pillar recommends adopting a consumption model where you "pay only for the computing resources you consume." In theory, this aligns perfectly with startup economics. In practice, AWS's per-service pricing creates fixed costs that don't scale down with low utilization.

    Why Not Just Use SaaS Subscriptions?

    The obvious question: why not skip the infrastructure complexity entirely and pay for hosted SaaS versions of these tools? The math often argues against it for multi-tenant deployments.

    n8n Cloud

    Starts at €20/month but includes only 2,500 workflow executions. A single workflow with five steps running once counts as five executions. For even moderate automation needs, costs escalate quickly, and the Pro tier at €50/month with 10,000 executions still imposes execution-based billing that becomes unpredictable during traffic spikes.

    Metabase Cloud

    Starter plan runs $85/month for five users, with $5/month for each additional user. Scale to twenty users across multiple clients, and you're looking at $160/month for a single BI tool. The Enterprise tier with proper multi-tenant support and white-labeling starts at $15,000/year.

    When you multiply these costs across multiple tools and multiple tenants, SaaS subscriptions can exceed the infrastructure costs of self-hosting, while offering less control over data residency, customization, and integration. For startups serving clients with data sovereignty requirements or those needing deep customization, self-hosting becomes the only viable option.

    The Discount Gap Between Startups and Enterprises

    Large enterprises operate under different economics. They negotiate Enterprise Discount Programs with AWS, commit to multi-year Savings Plans that reduce per-service costs by up to 72%, and treat infrastructure as a rounding error against revenue. Their finance teams have dedicated FinOps practitioners parsing Cost Explorer dashboards. For them, the AWS Well-Architected Framework's guidance on right-sizing instances and implementing auto-scaling delivers meaningful savings.

    Startups don't have that luxury. We're optimizing burn rate, not margin. Every dollar saved on infrastructure is a dollar that extends runway or funds another engineering hire. When your monthly AWS bill approaches what you'd pay a junior developer, the calculus changes entirely.

    Understanding the Legacy Architecture

    Our original architecture followed AWS best practices to the letter. Each tenant workload ran on its own isolated stack: n8n for workflow orchestration, Docmost for documentation, and Metabase for analytics. Every stack included:

    AWS Stack per Tenant

    ECS Fargate

    Container orchestration

    ALB + ACM

    Traffic routing & SSL

    RDS PostgreSQL

    Persistent data

    EFS

    Shared file storage

    Secrets Manager

    Credentials

    CloudWatch

    Logging & monitoring

    + VPC with private subnets, requiring NAT gateways for outbound traffic

    On paper, this architecture was bulletproof. In practice, it was financially unsustainable for a startup.

    The Real Cost Breakdown

    The architecture's costs weren't driven by actual usage. They were driven by AWS's pricing model, which charges independently for every service regardless of utilization. Here's what a single idle environment actually cost:

    Compute (ECS Fargate)

    $100+/mo

    Each task was sized for worst-case scenarios at 1 vCPU and 2-4 GB of memory. Even during off-hours with zero traffic, each minor workload burned through $35-50 per month in pure compute charges. With three primary applications per tenant, that's over $100 monthly before a single request hits the system.

    Database (RDS PostgreSQL)

    $25-30+/mo

    We ran multiple always-on RDS instances with gp2 storage and provisioned IOPS. The smallest production-ready instance starts around $25-30 per month, but real-world requirements pushed us toward larger instances. Database snapshots added to the bill, and we couldn't share instances across tenants without compromising isolation.

    Load Balancing (ALB + ACM)

    $16-22+/mo

    Here's where costs became absurd. Each service needed its own ALB for proper routing and SSL termination. ALBs charge approximately $16-22 per month just for existing, plus additional fees for connection hours and processed bytes. Across multiple services and tenants, our load balancer line item rivaled our compute charges.

    Storage (EFS)

    We sized EFS volumes for peak capacity, but most data sat cold. AWS's tiering should have helped, but metadata operations and file locking kept pushing data back to the standard tier, eliminating any savings from Infrequent Access pricing.

    Networking

    HIDDEN KILLER

    NAT gateway charges ($0.045 per GB processed plus hourly fees) accumulated silently as containers in private subnets made outbound requests. Cross-AZ data transfer added another layer of surprise costs during our monthly invoice review.

    A single environment burned hundreds of dollars monthly before real traffic appeared. Scaling horizontally by adding tenants simply multiplied these fixed costs linearly.

    When Architecture Becomes a Liability

    The architecture wasn't wrong in the traditional sense. It was reliable, secure, and followed AWS Well-Architected Framework principles. However, those principles were designed for enterprises where infrastructure costs represent a rounding error. For startups optimizing burn rate, the approach was economically irrational.

    Every time we onboarded a new tenant, we duplicated the same AWS primitives: another RDS instance, another set of ECS services, another ALB, another CloudWatch log group with retention policies. The marginal cost of each new customer approached the per-service baseline rather than approaching zero.

    The Decision: Why Kubernetes on OVH

    Evaluating the Alternatives

    Before committing to migration, we evaluated several alternatives:

    AWS Cost Optimization

    We tried right-sizing instances, implementing auto-scaling, and purchasing reserved capacity. The results were marginal. The fundamental problem wasn't inefficiency; it was AWS's pricing model for services that charge regardless of utilization.

    Switching to EKS

    Amazon's managed Kubernetes service would have consolidated some costs, but EKS charges $0.10 per hour for the control plane ($73/month), and we'd still face the same ALB, NAT gateway, and data transfer costs. The economics didn't improve enough to justify the migration effort.

    Serverless Approaches

    Lambda and API Gateway offered pay-per-use pricing, but our workloads weren't suited for serverless. n8n, Docmost, and Metabase all require persistent connections and long-running processes that serverless architectures penalize.

    Alternative Cloud Providers

    We evaluated GCP, Azure, and smaller providers. GCP offered better container pricing through Cloud Run, but networking costs remained problematic. Azure had similar pricing structures to AWS.

    Bare-Metal + Self-Managed K8s : OVHcloud

    OVH offered dedicated servers at predictable monthly rates with generous bandwidth allocations. A single server capable of running our entire workload cost less than our previous per-tenant AWS bill.

    What is OVHcloud?

    OVHcloud is a European cloud provider best known for its dedicated bare-metal servers and straightforward, fixed-price infrastructure. Unlike hyperscalers like AWS, OVH focuses on predictable monthly pricing, bundled bandwidth, and direct access to physical hardware. It operates its own data centers, primarily in Europe, which makes it a common choice for teams with cost sensitivity, data sovereignty requirements, or a preference for self-managed infrastructure.

    Why OVH Specifically

    OVH emerged as the winner for several reasons:

    Predictable Pricing

    Flat monthly rates for dedicated servers eliminated usage-based surprises. We could model costs accurately without complex AWS pricing calculator gymnastics.

    Bandwidth Inclusion

    OVH includes substantial bandwidth with their servers. Our data transfer concerns evaporated.

    European Data Centers

    For some of our clients, GDPR compliance and data sovereignty mattered. OVH's European presence simplified these conversations.

    No Vendor Lock-in

    Running self-managed Kubernetes meant we could migrate again if needed. Our workloads would be portable across any Kubernetes-capable platform.

    Accepting the Tradeoffs

    We weren't naive about the downsides. Self-managed Kubernetes means:

    • No AWS SLA when nodes fail
    • Responsibility for control plane maintenance, patching, and security
    • Need for in-house expertise in CNPG backups, ingress controllers, and MetalLB
    • Higher initial migration effort compared to staying put

    We decided the monthly savings justified the operational investment. The key insight was that we'd need Kubernetes expertise regardless, since AWS abstractions were already leaking into our daily operations. We might as well own the full stack.

    Technical Architecture: The Target State

    Cluster Design Principles

    Our target architecture centered on resource consolidation. Instead of duplicating infrastructure per tenant, we'd share a single Kubernetes cluster with strong namespace isolation:

    Architecture Comparison

    AWS ECS (Before)
    × Separate stack per tenant
    × Multiple RDS instances
    × ALB per service
    × NAT gateway costs
    × Linear cost scaling
    K8s on OVH (After)
    Single cluster, namespaces
    CloudNativePG (in-cluster)
    Shared ingress-nginx
    Included bandwidth
    Flat monthly rate

    Single Cluster, Multiple Namespaces

    Each application (n8n, Docmost, Metabase) gets its own namespace with appropriate RBAC policies. Tenants are isolated at the application layer rather than the infrastructure layer.

    CloudNativePG for Databases

    CNPG replaces RDS with PostgreSQL clusters running inside Kubernetes. We gain the same HA capabilities through native Postgres streaming replication without per-instance AWS charges.

    Shared Ingress Layer

    A single ingress-nginx controller with MetalLB replaces multiple ALBs. cert-manager handles certificate provisioning through Let's Encrypt, eliminating ACM dependency.

    Hybrid Storage Strategy

    Rook/Ceph provides persistent volumes within the cluster, while AWS S3 remains our backup destination. We leverage AWS's durability without running workloads there.

    Component Selection

    Container Runtime

    Standard containerd on Ubuntu 24.04 LTS. We avoided exotic runtimes to maximize compatibility and simplify debugging.

    Networking

    Calico for pod networking with standard Kubernetes network policies. MetalLB provides LoadBalancer service types on bare-metal, assigning external IPs from a configured pool.

    Ingress

    ingress-nginx with cert-manager for automatic TLS certificate provisioning. All applications share the same ingress controller, with routing based on hostname and path rules.

    Storage

    • Rook/Ceph for persistent volumes requiring high availability
    • OVH block storage for simpler use cases
    • AWS S3 for backups (retained from the legacy architecture)

    Database

    CloudNativePG manages PostgreSQL instances as native Kubernetes resources. CNPG handles:

    Automated failover and high availability
    Point-in-time recovery to S3
    Connection pooling via built-in PgBouncer
    Rolling updates without downtime

    Secrets Management

    We retained AWS Secrets Manager for credential storage. The data path stays off AWS, but secrets are fetched at deployment time through External Secrets Operator.

    Architectural Insights

    Per-Service Pricing Is Dangerous at Scale

    AWS's model works well when you have a few high-value services. It breaks down when you need many small services. Kubernetes lets you amortize infrastructure across unlimited workloads.

    Managed Services Have Hidden Costs

    RDS isn't just the instance price. It's snapshots, PIOPS, storage, and multi-AZ replicas. EFS isn't just storage. It's throughput provisioning and access tier gymnastics. These hidden costs only become visible when the bill arrives.

    Operational Complexity Is Transferable

    We traded AWS operational complexity for Kubernetes operational complexity. The total complexity didn't decrease; it shifted to skills that are more portable and in-demand.

    Hybrid Approaches Work

    Keeping S3 for backups while running workloads elsewhere gives us the best of both worlds. AWS's durability for critical data, OVH's pricing for compute.

    When This Migration Doesn't Make Sense

    We wouldn't recommend this approach for every team:

    • If You Lack Kubernetes Expertise:The learning curve is real. Budget 3-6 months of investment before expecting operational maturity.
    • If Compliance Is Non-Negotiable:Some regulated industries require specific cloud certifications. OVH has many, but not all that AWS offers.
    • If You're Already at Scale:Large organizations often get AWS discounts that change the math. Enterprise agreements can reduce the per-service overhead that hurt us.
    • If Time-to-Market Is Critical:Our migration took weeks of engineering effort. If you're racing to ship features, infrastructure optimization can wait.

    Conclusion

    Moving from AWS ECS to self-managed Kubernetes on OVH was one of the most impactful infrastructure decisions we've made. The 70% cost reduction funds additional engineering headcount. The operational knowledge we've built makes our team more valuable and our architecture more portable.

    The migration wasn't easy, and it's not right for everyone. However, for startups where AWS's per-service pricing model conflicts with economic reality, this path offers a viable alternative.

    Struggling with cloud costs? Our infrastructure engineers can help you evaluate your options and execute migrations that make financial sense.