...
Back

We Changed Hosting Platforms Three Times in One Week

Serverless functions, then a PaaS, then a container runtime. Each move was justified. What we should have measured first was not price or features, but how much of the platform we would have to model in our heads.

We Changed Hosting Platforms Three Times in One Week

We Changed Hosting Platforms Three Times in One Week 🚚

In one week our deployment target moved from cloud functions, to a PaaS, to a container runtime. The reasons were individually sound and collectively embarrassing, and the whole detour came from evaluating hosts on the wrong axis.


What each attempt actually taught us

Cloud functions. The appeal is real: no server to size, scale-to-zero, pay per request. The problem is that a server-rendered app with a routing layer is not a function. Getting it to work meant handing every request to a framework request handler inside the function, which is the framework's server, running inside something that was designed not to be one. Memory and timeout both had to be raised well past defaults before pages rendered reliably. At that point we were paying function prices for server behavior without server ergonomics.

The signal we ignored: when the fix is "raise the limits until it stops looking like a function," the abstraction is wrong.

The PaaS. Much better fit — it wanted a Dockerfile, and it built and ran one. We lost a day to build-cache behavior instead. chore: force rebuild without cache appears in our history more than once, which is a tell: we did not understand when the platform reused layers, so we started poking it. Convenience abstractions are pleasant until the moment you need to know exactly what happened, and then you are debugging someone else's heuristics through a log viewer.

The container runtime. Where we stayed. Not because it is objectively best, but because the mental model is small: here is an image, here is a port, here is how many instances. Every failure after the move was our failure, in our Dockerfile, reproducible locally.


The axis we should have used

We compared on price, region availability, and feature checklists. Those matter, but they are not what cost us the week. The thing that cost us the week is better stated as:

How much of this platform do I have to hold in my head to debug a bad deploy at 2am?

Call it model size. A container runtime has a small one: image, port, environment, concurrency, instance count. A PaaS has a larger one, because its conveniences — inferred build steps, cache heuristics, automatic service wiring — are behavior you did not write and cannot step through. Functions have a small model that is a bad fit for a stateful server, which is its own kind of expensive.

Small model beats feature count, for a specific reason: features are visible when you shop, and model size is only visible when something breaks. You feel it exactly when you can least afford to.


The questions that actually predict pain

If we ran the evaluation again, this is the list:

  1. Can I reproduce the production build on my laptop, byte for byte? If the platform builds differently than I do, every build failure becomes a remote debugging session. This one question would have ordered our three candidates correctly.
  2. What happens on a cold start, and can I pay to avoid it? Not "is it fast" — is it a dial I control. Scale-to-zero is a cost decision, and it should be a decision, not a property.
  3. Where do the logs go, and can I search them by request? Aggregate dashboards are for graphs. Debugging needs one request's story, start to finish.
  4. How do I roll back, and how long does it take? The honest version: how long between "this is broken" and "the previous version is live". If the answer involves rebuilding, it is not a rollback.
  5. What is the build context, and what is baked in at build time? Anything inlined into a client bundle is decided at build time. If the platform builds for you, you need to know exactly which environment it built with.

What we would keep from each

Nothing here is a verdict on the platforms. The functions runtime is excellent for actual functions — webhooks, scheduled jobs, image resizing. The PaaS genuinely gets a small service online faster than anything else, and for a project that will never need to know what the cache did, that is a real win. The container runtime asks more of you up front and gives back predictability.

The mistake was not choosing wrong three times. It was choosing on the shopping-page axis when the axis that mattered was how much of somebody else's behavior we would have to reverse-engineer when a deploy went bad.