Serverless is often sold as the end of servers: upload a function, and the cloud provider takes care of the rest. There are still servers, of course. The difference is that you no longer rent, patch, or scale them yourself, and you pay only while your code is actually running. For a webhook that receives a few thousand calls a day, that can mean a monthly bill close to zero. For an API that serves steady traffic all day, the same model can cost more than a pair of ordinary servers. This article explains how serverless works in practice, using AWS Lambda as the main example, and where it does and does not make sense.
What Serverless Actually Means
A service counts as serverless when three things hold: you do not manage the operating system or the machines, capacity follows the traffic automatically, and the bill follows usage, ideally dropping to zero when nothing is happening. Under that umbrella there are three fairly different models.
| Model | Examples | What you deploy |
|---|---|---|
| Functions (FaaS) | AWS Lambda, Azure Functions, Google Cloud Run functions | A function that is called once per event: an HTTP request, a queue message, an uploaded file, a schedule. |
| Serverless containers | Google Cloud Run, Azure Container Apps | A regular container image with a web server inside. The platform starts and stops copies as traffic changes. |
| Edge functions | Cloudflare Workers, Vercel Functions | Small pieces of JavaScript or WebAssembly that run in data centers close to the user. |
Databases, queues, and storage can be serverless too, such as DynamoDB, SQS, S3, or Aurora Serverless. In practice a serverless application is usually a combination: functions for the logic and managed services for everything that holds state.
How AWS Lambda Works
A Lambda function is a handler plus its configuration: runtime, memory, timeout, and permissions. Something triggers it, such as API Gateway, an S3 upload, an SQS message, or an EventBridge schedule. Lambda then finds an execution environment to run the handler in. If no idle environment is available, it creates a new one: it downloads your code, starts the runtime, and runs your initialization code. That is the cold start. Once the request is finished, the environment stays around for a while and can serve the next request without the startup cost.
- One environment handles one request at a time. If 50 requests arrive at the same moment, Lambda runs 50 environments. This is called concurrency.
- CPU follows memory. According to the AWS documentation, a function gets the equivalent of one full vCPU at 1,769 MB. A function with 128 MB gets only a small fraction of a CPU, so raising memory often makes it both faster and not much more expensive.
- Billing is per millisecond of duration multiplied by the memory configured, plus a fee per request. Since August 2025, initialization time during a cold start is billed as well, so heavy startup code now costs money directly.
- Nothing is guaranteed to survive between requests. Data in memory or in /tmp may still be there for the next request, or may not. Anything that matters belongs in a database, cache, or S3.
The Limits You Should Know Before Designing
Most Lambda problems in production come from designs that ignore these limits. The values below come from the AWS Lambda quotas page.
| Item | Limit |
|---|---|
| Maximum run time per invocation | 15 minutes |
| Memory | 128 MB to 10,240 MB |
| /tmp storage | 512 MB to 10,240 MB |
| Request and response size, synchronous | 6 MB each, or up to 200 MB with response streaming |
| Event size, asynchronous | 1 MB |
| Deployment package | 50 MB zipped, 250 MB unzipped including layers, or a container image up to 10 GB |
| Concurrency per region | 1,000 by default, lower for new accounts, can be raised |
| Scaling speed per function | 1,000 new execution environments every 10 seconds |
The services around Lambda have limits of their own. An HTTP API in API Gateway waits at most 30 seconds for a response, so a slow report endpoint will fail there long before the 15 minute Lambda timeout. Heavy work should go through a queue and report back when it is done.
Comparing the Main Options
| Platform | Model | Region near Indonesia | How it charges |
|---|---|---|---|
| AWS Lambda | Functions, one request per environment | Jakarta (ap-southeast-3) | Requests plus GB-seconds of duration. Free tier of 1 million requests and 400,000 GB-seconds per month. |
| Google Cloud Run and Cloud Run functions | Containers or functions, up to 80 concurrent requests per instance by default | Jakarta (asia-southeast2), which uses the more expensive Tier 2 prices | vCPU-seconds, GiB-seconds, and requests while an instance is busy. Free tier of 180,000 vCPU-seconds, 360,000 GiB-seconds, and 2 million requests per month. |
| Azure Functions (Flex Consumption) | Functions | Check whether the plan is offered in Indonesia Central; Southeast Asia (Singapore) is the usual fallback | Executions plus GB-seconds of memory. |
| Cloudflare Workers | Edge functions in a V8 isolate, no full Node.js environment | Runs in the Cloudflare data center closest to the user | USD 5 per month includes 10 million requests and 30 million CPU milliseconds. Only CPU time counts, not time spent waiting on a database. |
Cloud Run is the easiest move for teams that already ship containers: the same image runs locally, on a VM, or on Cloud Run, and one instance can serve many requests at once. Lambda has the widest set of triggers and the closest integration with the rest of AWS. Cloudflare Workers has almost no cold start and is very cheap for light, latency-sensitive work, but its runtime is not a full Node.js environment and it is less suited to heavy processing. If personal data must stay in Indonesia under the UU PDP, check where the function runs and, more importantly, where it stores and sends the data.
What It Costs: A Calculation with Jakarta Prices
To make this concrete we calculated the monthly cost of an API on Lambda in the Jakarta region, using AWS public price list data in November 2026. Assumptions: Arm (Graviton) architecture at USD 0.0000133334 per GB-second, 512 MB memory, an average billed duration of 120 ms per request, USD 0.20 per million requests, an API Gateway HTTP API at USD 1.29 per million requests, and the Lambda free tier. For comparison we used Cloudflare Workers with 5 ms of CPU time per request. Data transfer, logs, and the database are not included.
| Requests per month | Average per second | Lambda | API Gateway | Lambda + API Gateway | Cloudflare Workers |
|---|---|---|---|---|---|
| 1 million | 0.4 | USD 0 | USD 1.29 | USD 1.29 | USD 5.00 |
| 10 million | 3.9 | USD 4.47 | USD 12.90 | USD 17.37 | USD 5.40 |
| 50 million | 19 | USD 44.47 | USD 64.50 | USD 108.97 | USD 21.40 |
| 100 million | 39 | USD 94.47 | USD 129.00 | USD 223.47 | USD 41.40 |
| 300 million | 116 | USD 294.47 | USD 387.00 | USD 681.47 | USD 121.40 |
For comparison, a c6g.xlarge instance (4 vCPU, 8 GB, Arm) in Jakarta costs about USD 0.157 per hour, or roughly USD 114 per month on demand. Two of them for redundancy come to about USD 229 per month before a load balancer, and for a typical business API they can handle far more than 39 requests per second. Three things stand out from the numbers:
- At low and irregular traffic, serverless is very hard to beat. Up to around 10 million requests per month the bill stays below twenty dollars, with no idle servers.
- API Gateway often costs more than Lambda itself. For simple endpoints, a Lambda function URL has no charge beyond the function, although you lose features such as built-in throttling per client and API keys.
- Somewhere around 50 to 100 million requests per month of steady traffic, ordinary servers or containers start to win on cost. That break-even point moves a lot with function duration and memory, so measure your own numbers rather than reusing ours.
Where Serverless Fits Well
- Webhooks from payment gateways, WhatsApp, or other third parties. Traffic is unpredictable and each call is short.
- File processing after upload: resizing images, generating thumbnails, extracting text from a PDF, or checking an Excel import.
- Scheduled jobs that used to need a server with cron: daily reports, data synchronization, cleanup.
- Queue workers that process messages from SQS or Kafka and scale with the backlog.
- APIs for internal tools, early stage products, or seasonal campaigns, where traffic is low most of the time with occasional spikes.
- Glue between services, such as forwarding events from one system to another or sending notifications.
Where It Usually Does Not
- APIs with high and steady traffic all day. As the calculation above shows, you pay a premium for elasticity you do not use.
- Work that runs longer than 15 minutes, such as large data migrations or video encoding, unless it is split into steps.
- Applications that need a long-lived connection or local state, such as game servers or a WebSocket server that keeps data in memory.
- Endpoints with strict latency requirements on every request, where an occasional cold start of several hundred milliseconds or more is not acceptable and provisioned concurrency would erase the savings.
- Teams that cannot yet invest in monitoring and infrastructure as code. Fifty small functions configured by hand in the console are harder to run than one application on one server.
Problems That Show Up in Production
Database connections run out. Every execution environment opens its own connection, so a traffic spike that starts 500 environments can open 500 connections to a PostgreSQL or MySQL server that only allows 100. Use RDS Proxy or another connection pooler, set reserved concurrency on the function to cap how far it scales, or use a database with an HTTP interface.
Cold starts are longer than expected. Large packages, many dependencies, and heavy frameworks all add startup time, and Java without SnapStart is the slowest. Keep the package small, create SDK clients and database connections outside the handler so warm requests reuse them, and only load what each function needs.
Events get processed twice. Asynchronous invocations are retried twice by default when they fail, and SQS delivers messages at least once. A payment webhook that runs twice must not credit a balance twice, so every handler that changes data needs to be idempotent, for example by storing the event ID and skipping duplicates. Configure a dead letter queue or failure destination so events that keep failing are not silently lost.
Costs grow in unexpected places. A function that writes to the same S3 bucket that triggers it can call itself in a loop. AWS detects some of these loops now, but not all. Debug logs at high volume can make CloudWatch Logs more expensive than Lambda itself. Set a log retention period, use log levels, and create an AWS Budgets alert before going live, not after the first invoice.
Newer Lambda Options Worth Knowing
AWS has added several variants that soften the classic limits. They are worth a look before deciding that Lambda does not fit:
- Lambda Managed Instances run functions on EC2 instances in your account, managed by Lambda. You pay the EC2 price plus a 15 percent management fee and USD 0.20 per million requests, without a duration charge, and Savings Plans or Reserved Instances apply. This targets exactly the steady, high traffic case where classic Lambda gets expensive.
- Durable functions let one function run a long workflow made of steps with checkpoints, and wait for an external event or a time without paying for compute while it waits.
- Lambda Web Functions run an ordinary Node.js web server, such as Express or Hono, with a built-in HTTPS endpoint, where one environment handles many requests at once. At the time of writing they support Node.js only, so check availability in your region.
Getting Started Without Making a Mess
- 1Pick one workload that clearly fits, such as a webhook, image processing, or a scheduled job, instead of moving the whole application at once.
- 2Define the functions, permissions, and triggers as code from the first day using AWS SAM, CDK, Terraform, or SST, and deploy through a CI/CD pipeline.
- 3Choose the Arm architecture unless a dependency requires x86. In Jakarta it is 20 percent cheaper per GB-second.
- 4Test several memory sizes with a tool such as AWS Lambda Power Tuning. The cheapest setting is often not the smallest one.
- 5Set reserved concurrency for functions that touch a database, and a dead letter queue or failure destination for asynchronous work.
- 6Set log retention, alarms on errors and throttles, and a monthly budget alert before the first production release.
- 7Review the bill after one or two months. If a function runs constantly at high volume, compare it with a container or Lambda Managed Instances.
Serverless does not remove operations work. It moves it: from patching servers to designing for retries, concurrency, and cost per request.
Key takeaways
- Serverless means no servers to manage and billing per use, delivered as functions (Lambda), serverless containers (Cloud Run), or edge functions (Cloudflare Workers).
- Lambda runs one request per environment, gives CPU in proportion to memory, bills cold start initialization since August 2025, and stops at 15 minutes per invocation.
- With Jakarta prices, an API with 10 million requests per month costs about USD 17 on Lambda with API Gateway, while steady traffic around 50 to 100 million requests per month starts to favor ordinary servers.
- API Gateway is often the largest part of the bill; function URLs or Cloudflare Workers can be much cheaper for simple endpoints.
- Plan for database connection limits, idempotent handlers, dead letter queues, log retention, and budget alerts before going live.
- Lambda Managed Instances, durable functions, and Web Functions cover several cases that classic Lambda handles poorly.


