Amazon S3
Replaces the web server
Object storage that answers HTTP directly. The built site is copied into a bucket and served from it. Nothing runs; files are returned.
Billed per GB stored and per request
Case studies Technical
Most explanations of serverless stop at the diagram. This one runs all the way to a system you can poke at: the website you are on. Every number, service and decision below is the one actually in production, including the parts we got wrong first.
01
There are still servers. They are just not yours. What changes is the unit you rent and the unit you think about: instead of a machine that runs all month, you rent one execution of one function, and you are billed for the milliseconds it was awake.
That one shift pulls a lot behind it. There is no operating system to patch, because you do not have one. There is no capacity to plan, because there is no fixed capacity — a thousand simultaneous requests start a thousand copies. And there is no idle cost, because between requests nothing of yours exists.
Worth saying before the good parts. You lose the machine, so anything that wants a long-lived process, a local disk or a fixed IP has to be rethought. The first request after a quiet spell is slower. Your architecture becomes harder to lift to another cloud. And debugging moves from “ssh in and look” to reading logs and traces, which is a different skill.
02
A serverless stack is assembled rather than installed. These are the pieces this site uses, what each one replaces, and what you pay for.
Replaces the web server
Object storage that answers HTTP directly. The built site is copied into a bucket and served from it. Nothing runs; files are returned.
Billed per GB stored and per request
Replaces the application server
Runs a function on demand, in a container AWS starts and destroys. You supply code and a memory size; AWS supplies everything under it.
Billed per millisecond × memory
Replaces the API gateway
One HTTPS endpoint attached straight to a function, with its own CORS settings. For a single endpoint it removes a whole service from the diagram.
No extra charge
Replaces the database server
A managed key-value store with no instance to size. Set a time-to-live on a row and it deletes itself when it expires.
Billed per read, write and GB
Replaces the mail server
Publish a message to a topic and everyone subscribed to it is notified. For an enquiry alert that is the entire requirement.
Billed per message
Replaces stored credentials
The deploy pipeline proves who it is with a short-lived token from GitHub and assumes a role. No AWS access key exists to leak.
No charge
Not used here, but part of the same family and worth knowing: API Gateway when you need many routes, authorisers or throttling; Step Functions when work spans several functions; EventBridge for scheduling and fan-out; CloudFront when you want a CDN and a custom certificate in front of S3.
03
This is what happens when you fill in the contact form on this site and press Send.
The site is built to static files and synced to an S3 bucket. S3 answers the request itself. There is no process in front of it to run out of memory, no connection pool, and nothing that falls over when a newsletter goes out.
The browser posts JSON to the Function URL. Until this moment there is no compute of ours running anywhere in the world — not idling, not scaled to zero, simply absent.
It pulls the function’s code, starts a runtime, and calls the handler. On a warm container it skips straight to the handler. Either way the function is one file of JavaScript with no framework under it.
Three cheap checks, in order: a hidden field that only an automated form-filler would populate, a comparison between when the page rendered and when it was submitted, and a per-IP counter kept in the same table. A bot fails at least one.
The enquiry goes into DynamoDB with an expiry stamped on the row, a notification goes to SNS, and a small JSON reply goes back to the browser, which sends you to the thank-you page. The container is then frozen, and shortly after, discarded.
04
The specifics, so you can judge the claims rather than take them.
We configured CORS on the Function URL and also returned the headers from the function. The response then carried the access-control-allow-origin header twice, browsers rejected it as malformed, and the form showed “Load failed” — while the enquiry was stored and the email sent perfectly well. The fix is to pick one place. We pick the URL.
The Function URL is visible in the page source, which is the usual first worry. It is deliberately public: it accepts a form post and nothing else, holds no credentials of its own, and its IAM role can write to exactly one table and publish to exactly one topic. The secret is not the address; it is what the address is allowed to do.
The first design used a third-party CAPTCHA. That meant another vendor in the request path, another privacy disclosure, and a puzzle for every genuine visitor. The honeypot-and-clock approach costs nothing, tells no one anything, and has held.
05
When no container is warm, AWS has to create one before your code runs. That is a cold start, and it is the most-cited objection to serverless. It is real, and it is also usually quoted by people describing a different runtime than the one you are using.
What actually governs it: the language, the size of the deployment package, and whether the function has to reach a private network. A small JavaScript function with no VPC attachment is at the fast end. A large Java application inside a VPC is at the slow end, and those two cases are often discussed as if they were the same thing.
It also matters where the cold start lands. On this site it can only ever land on a form submission, after a human has spent a minute typing. If it landed on every page view, the answer would be different — which is exactly why the pages come out of S3 instead.
Keep the package small, stay out of the VPC unless you need it, move work out of the module scope and into the handler, and if the path is genuinely latency-critical, use provisioned concurrency and pay for warm containers. That last option is a dial, not a defeat.
06
We are not going to print a monthly figure, because yours would be a different number and a made-up one helps nobody. The shape of the bill is the useful part.
A rented server is a rectangle: the same amount every month whether you had a thousand visitors or none. Serverless is a staircase that starts at the floor. For a marketing site with a contact form, the compute side of that staircase barely leaves the floor, because the compute only runs when somebody writes to you.
The honest counter-point: at sustained high volume, a constantly-busy function costs more than a constantly-busy server doing the same work. There is a crossover. Below it you are paying for idle; above it you are paying a premium for not having to operate anything. Knowing roughly where your traffic sits on that line is most of the decision.
07
A push to the main branch starts a GitHub Actions run. The run asks GitHub for a short-lived identity token, hands it to AWS, and AWS gives back temporary credentials for a role scoped to this one job. Then it builds the site and syncs the result to S3.
The point of that middle step is that no AWS access key exists anywhere in GitHub. There is no secret to rotate, no key to leak in a log, and nothing useful for someone who gains read access to the repository.
08
We would rather tell you this now than halfway through a migration.
We build and run this for banks, NBFCs, wealth managers and fintechs — including the parts that cannot be serverless. Tell us what you are running today and we will tell you honestly which parts are worth moving.