Yashveer Singh
Connect
<- All posts
Backend, APIs, and System Design6 min read

Serverless vs Containers vs Bare Metal: A Cost and Flexibility Map

Compute models for web applications describe how application code is executed and billed. Serverless functions (AWS Lambda, Vercel Functions, Cloudflare Workers) execute code on-demand and bill per invocation and execution time, with no always-running infrastructure to manage. Containers (Docker on ECS, Kubernetes, Railway, Render) package application code with its runtime and run as always-on or auto-scaled processes. Bare metal or VPS hosting runs application code on dedicated or virtual machines where the team manages the operating system and runtime environment.

Written by Yashveer Singh, founder of Yashveer Labs.

What you need to know

  • Serverless is cost-effective for spiky or low-baseline traffic. It becomes expensive at consistently high request rates with meaningful execution duration.
  • Containers provide predictable cost at consistent traffic levels and are appropriate for stateful connections, continuous workloads, and AI inference.
  • Bare metal or VPS is the most cost-efficient for CPU/memory-intensive continuous workloads when the team has DevOps capacity to manage the infrastructure.
  • Cold start latency is a real trade-off for serverless. Use provisioned concurrency or containers for latency-sensitive user-facing operations.
  • The operational overhead increases from serverless to containers to bare metal. Match the compute model to the team's operational capacity.

The core argument

The compute model decision is inseparable from the team's operational capacity. Serverless provides the lowest operational overhead at the cost of flexibility and potentially higher cost at scale. Bare metal provides the highest flexibility and lowest cost-per-compute-unit at the cost of significant operational overhead. Containers sit in the middle: more flexibility than serverless, less operational overhead than bare metal.

The mistake is choosing a compute model based on performance or cost in isolation without accounting for the operational capacity required to run it. A team of two engineers without a DevOps background running their application on bare metal spends a disproportionate fraction of their time on infrastructure management rather than product work. A team of 15 engineers with a dedicated DevOps function running everything on Lambda at high traffic volumes pays three times the container cost without clear benefit.

The decision framework: start with what the team can operate reliably today, and migrate toward more cost-efficient models as operational capacity grows. For most early-stage SaaS products, a managed container platform (Railway, Render, or Fly.io) provides the right balance: more control than pure serverless, less operational overhead than self-managed Kubernetes or bare metal. Migrate to AWS ECS or self-managed infrastructure when the cost savings justify the operational investment.

Common mistakes

  1. Choosing serverless for a product that requires persistent connections. WebSocket-based real-time features, long-polling, and streaming responses are difficult or impossible to implement correctly in serverless functions, which have execution time limits and no persistent process state. If the product requires real-time bidirectional communication, containers with a persistent process are the appropriate compute model.
  1. Not modeling cold start impact on the user experience. Teams choosing serverless for user-facing APIs without analyzing cold start frequency and impact may discover the problem only in production, when users report intermittent slowness. Use AWS X-Ray or Datadog Lambda tracing to measure cold start frequency and duration. If cold starts occur regularly in the user-facing critical path, configure provisioned concurrency or migrate the affected functions to containers.
  1. Running databases on serverless-scaled containers without connection pooling. A serverless function that opens a new database connection for every invocation can exhaust the database's connection limit at high concurrency. Each Lambda invocation creates a new connection; 100 concurrent invocations create 100 connections. Use PgBouncer, RDS Proxy, or a database with a serverless-compatible connection model (PlanetScale, Neon) when running database-connected code in serverless functions.
  1. Over-provisioning bare metal for variable workloads. Bare metal servers are always-on; they do not scale down when traffic is low. Provisioning bare metal for peak traffic means paying for peak capacity continuously. Variable workloads (products with daily or weekly traffic cycles) should use auto-scaling containers or serverless, not bare metal. Bare metal is efficient for workloads with consistently high and predictable resource utilization.
  1. Not accounting for egress costs in the compute model comparison. Cloud provider egress (data transferred out of the cloud) can be a significant cost line, especially for products that return large payloads or serve media. Egress costs are roughly similar across serverless, containers, and VMs on the same cloud provider. Switching to Hetzner reduces compute cost dramatically but does not reduce egress cost for European regions to the same extent.

Where to start

  1. Categorize workloads by traffic pattern and execution characteristics. List the main workload types in the product: user-facing API requests (traffic pattern, average execution time, memory needs), background jobs (continuous vs triggered, execution duration), and heavy processing tasks (ML inference, document processing, report generation). Each category may warrant a different compute model.
  1. Compare the monthly cost of the current compute model against alternatives for the primary API workload. Pull the current monthly compute cost per request for the main API. Calculate the equivalent cost on Lambda (invocation cost + execution time cost), on a container platform (hourly or monthly rate divided by expected request capacity), and on a Hetzner VPS (monthly instance cost divided by expected capacity). The comparison reveals whether the current model is cost-optimal at current scale.
  1. Migrate one low-risk workload to test operational requirements of the target model. Before migrating the main application, migrate one background job or side service. This pilot reveals the operational challenges of the new compute model in a low-risk context and builds the team's experience before the main migration.

Related reading

FAQ

Frequently asked

Author

Closing note from the author

I keep these closing notes short on purpose. Most engineers writing about this topic are not the engineer you want to hire. I might be. Yashveer Singh, founder of Yashveer Labs. The contact channel is Instagram. The proof is the portfolio. The standard is in the work. If we are aligned, you will know within five minutes of the first message.

Related reading