You're staring at a requirements doc and the question comes up again: serverless, containers, or a good old VPS? I've seen teams pick serverless because it sounded modern, then rack up surprise bills. I've also seen teams stick with a traditional server when a few Lambda functions would have saved them weekends of maintenance.
The choice isn't about which architecture is "better." It's about matching your workload's actual behavior to the trade-offs each platform imposes. Let's walk through five decision points that matter in production.
Decision Point 1: Traffic Pattern and Predictability
Serverless shines when your traffic is spiky or inconsistent.
If you're handling webhook callbacks from third-party APIs, processing file uploads triggered by users, or running scheduled jobs that fire every hour but sit idle otherwise, serverless makes sense. You pay only for execution time. A function that runs for 200ms per invocation costs almost nothing during quiet periods.
Traditional servers charge you for uptime whether they're busy or not. A $20/month VPS burns that $20 even if it processes three requests all week. Containers on ECS or GKE fall somewhere in the middle—you can scale pods down to zero with tools like KEDA, but you're still managing orchestration overhead.
Here's the flip side: if your app serves steady traffic around the clock, a dedicated server often costs less. A single 2-core VPS can handle thousands of requests per minute with a well-optimized web stack. The math shifts when "steady" means you're already paying for compute that would run anyway.
Ask yourself: does my traffic drop to near-zero for hours or days at a time? If yes, serverless probably wins. If no, run the numbers on a reserved instance or a small VPS before committing to per-invocation pricing.
Decision Point 2: State and Execution Duration
Serverless functions are ephemeral.
Each invocation starts in a clean environment. You can't rely on in-memory state between requests. If your application needs to hold WebSocket connections open, maintain long-running database transactions, or stream large files through memory, serverless becomes painful fast.
Functions also have execution time limits. AWS Lambda caps you at 15 minutes. Google Cloud Functions allows 60 minutes for second-gen functions, but you'll pay more the longer you run. If your workload involves video transcoding, large ETL jobs, or any task that runs for tens of minutes, you're better off with a container or a VM where you control the runtime.
I handled a support case where a team tried to build a PDF generation service on Lambda. Each render took 8-12 minutes. They hit timeouts constantly, retried failed jobs, and ended up with duplicate invoices. Moving that workload to a small ECS task saved them days of debugging.
Stateless tasks are the sweet spot: process an image, validate a form submission, send an email, query a database and return JSON. Each of those finishes in seconds and doesn't care what happened in the previous invocation.
Do you need to hold state across requests or run for more than a few minutes? Pick containers or a server. Are your tasks quick and independent? Serverless fits.
Decision Point 3: Cold Start Tolerance
Cold starts are the tax you pay for not running a server 24/7.
When a serverless function hasn't been invoked recently, the platform has to spin up a new execution environment. That takes time—anywhere from 50ms for a minimal Node.js function to several seconds for a JVM or .NET runtime with heavy dependencies. If your function sits behind a user-facing API and that API needs to respond in under 200ms, cold starts will hurt.
You can mitigate this. Keep functions warm with scheduled pings, use provisioned concurrency (which costs money), or pick a runtime with faster cold starts. But you can't eliminate the problem entirely without paying to keep instances alive, which negates some of the serverless cost advantage.
For background jobs, cold starts don't matter. Processing a payment webhook or resizing an uploaded image can tolerate an extra second of latency. Users won't notice. For synchronous APIs where every millisecond counts, cold starts become a real constraint.
I've seen teams solve this by running a small always-on container for latency-sensitive endpoints and using serverless functions for everything else. Hybrid architectures work.
Is your workload user-facing and latency-critical? Cold starts might disqualify serverless. Is it asynchronous or can you absorb occasional delays? You're fine.
Decision Point 4: Cost Predictability vs. Cost Efficiency
Serverless pricing is usage-based. That's amazing when usage is low and terrible when you can't predict spikes.
A $5/month VPS costs $5 every month. You know the bill. A serverless function that suddenly gets hammered by a traffic surge or a misconfigured retry loop can balloon to hundreds of dollars overnight. I've reviewed bills where a single buggy function retrying a failed API call racked up 40 million invocations in six hours.
You can set budget alerts and concurrency limits to cap runaway costs, but those are guardrails you have to configure. Traditional hosting gives you a built-in spending ceiling—the server can only do so much, and once it's maxed out, new requests just fail or queue. That's actually a feature when it prevents surprise bills.
For low-traffic projects, serverless is cheaper. A side project with 10,000 requests per month might cost you $0.20 on Lambda. Spinning up even the smallest VPS would cost more. For high-traffic predictable workloads, a dedicated server wins. For unpredictable workloads where you're willing to monitor and optimize, serverless can scale efficiently if you watch it.
Can you stomach a variable monthly bill and commit to monitoring it? Serverless is viable. Do you need a fixed cost or does your finance team require predictable line items? Go with traditional hosting or reserved capacity.
Decision Point 5: Team Skills and Operational Complexity
Serverless trades one kind of complexity for another.
You don't patch servers, configure load balancers, or manage SSH keys. The platform handles scaling, OS updates, and hardware failures. That's a real win for small teams. But you do have to learn IAM roles, API Gateway configurations, CloudWatch Logs, environment variable injection, and the quirks of your cloud provider's tooling.
If your team already knows how to deploy a Docker container or SSH into a VPS and restart nginx, those skills transfer across providers. Serverless architectures are more opinionated. AWS Lambda works differently than Google Cloud Functions, which works differently than Azure Functions. Debugging is harder—no SSH, no persistent logs on disk, just whatever your function managed to emit to stdout before it died.
I've worked with teams that loved serverless because they didn't have to think about infrastructure. I've also worked with teams that found it frustrating because they couldn't just tail a log file or attach a debugger. Your mileage depends on your team's background.
Distributed tracing becomes important fast. When a single user request triggers four Lambda functions, an SQS queue, and a DynamoDB write, finding out where it failed requires proper instrumentation. Traditional monoliths on a single server are easier to reason about.
Does your team have experience with cloud-native architectures and observability tools? Serverless might be a smooth fit. Are they more comfortable with Linux sysadmin work and server-based deployments? Containers or VMs will feel more natural.
When Hybrid Makes Sense
You don't have to pick just one.
Run your main API on a container or VPS for predictable latency and cost. Use serverless functions for background jobs like sending emails, processing uploads, or running scheduled reports. Let each workload live where it fits best.
A typical pattern I see: a Django or Rails app on a $20/month VPS handling web traffic, with Lambda functions triggered by S3 events to generate thumbnails and CloudWatch Events to run nightly cleanup tasks. The web app stays simple and fast. The batch jobs don't waste server resources sitting idle.
Another pattern: a Next.js app deployed to Vercel (serverless functions under the hood) for the frontend, with a separate containerized API on ECS for complex business logic that needs longer execution times. The frontend scales automatically with traffic. The backend runs on predictable infrastructure.
Don't force everything into one architecture just for consistency. Match the workload.
What You Should Measure Before Deciding
Run some numbers before you commit.
Traffic volume: How many requests per day? What's your peak vs. average?
Execution time: How long does a typical request take to process?
Concurrency: How many requests happen at the same time during peak load?
Idle periods: Does traffic drop to zero overnight or on weekends?
Budget: What's your monthly hosting budget, and can it flex?
Plug those numbers into a serverless pricing calculator and compare it to the cost of a VPS or container setup that could handle the same load. Don't guess. If you're processing 10 million requests per month at 200ms each, you can calculate exactly what Lambda would cost versus a $40/month Linode.
Test your actual workload on both platforms if you can. Deploy a prototype, send it realistic traffic, and measure latency, error rates, and cost. Real data beats assumptions.
FAQ
Can I run a database on serverless?
You can run serverless functions that query a database, but the database itself should live on a persistent server or use a managed service like RDS or Cloud SQL. Databases need stable connections and persistent storage. Serverless functions are stateless and ephemeral.
What happens if my serverless function needs to call another API that's slow?
You'll wait. If the external API takes 10 seconds to respond, your function runs for 10 seconds and you pay for that time. If that happens often, you're paying for idle waiting. Consider using a queue and a background worker instead.
Are serverless functions secure?
They're as secure as you configure them. Each function should have minimal IAM permissions—only access to the specific resources it needs. Validate all inputs. Don't log sensitive data. The platform handles OS patching, but application security is still your responsibility.
Can I migrate from serverless back to containers later?
Yes, but it takes work. If you've built a highly distributed serverless architecture with dozens of functions, API Gateway integrations, and event triggers, consolidating that into a monolith or a few containers requires refactoring. Keep your business logic decoupled from platform-specific code to make it easier.
Does serverless support custom runtimes or compiled languages?
Yes. AWS Lambda supports custom runtimes via Lambda Layers or container images. You can run Go, Rust, or any compiled binary. Cold start times will vary based on runtime size and initialization overhead.
Choosing What Fits Your Workload
Serverless isn't a magic bullet. It's a tool that works well for specific workload patterns: spiky traffic, short-lived tasks, event-driven architectures, and teams that want to avoid infrastructure management.
Traditional servers still win for steady traffic, long-running processes, and teams that need cost predictability. Containers split the difference—you get portability and orchestration without giving up control.
Evaluate your traffic, measure your execution times, check your team's skills, and run the cost math. Don't default to serverless because it's trendy. Don't avoid it because it's unfamiliar. Pick the platform that fits the work you're actually doing.
