Every few months, I open the same question again: should this thing be running on Kubernetes?
I build security tooling as a solo founder. My stack is unglamorous on purpose: a FastAPI service with a deterministic Python scoring engine, a React frontend, PostgreSQL, and everything deployed on Cloud Run. It works. It scales when it needs to and scales to nothing when it does not. And yet the question keeps returning, because Kubernetes is the default answer in so many rooms, and "just use Cloud Run" can start to feel like a decision I made by accident rather than on purpose.
So I finally wrote the decision down — the framework I use, the five questions I ask, and when I would switch. If you are a solo builder choosing between GKE Autopilot and Cloud Run, this is the post I wish I had found earlier. Not because my answer is yours, but because the questions are portable.
What I actually run, and why it matters
Context first, because "which is better" is meaningless without it. My core service is a FastAPI API: requests come in, a deterministic scoring engine runs, a response goes out. Scoring is computed in plain Python — no model calls in the scoring path, ever. Language models only write the narrative explanation around a score, never the score itself. That separation is a product decision, but it is also an infrastructure decision: the hot path is stateless, CPU-bound, and easy to parallelize. It does not hold long-lived connections, it does not need a daemon on every node, and it does not care which machine runs it.
Around that core I have batch work — scheduled scans and verification jobs — which run as Cloud Run Jobs, triggered by Cloud Scheduler through Pub/Sub. The frontend is static, served separately. Firebase handles auth, with custom claims carrying tenant context that my FastAPI middleware checks on every request, fail-closed.
None of that needs Kubernetes. That is not a criticism of Kubernetes; it is the starting fact. The question is never "is Kubernetes powerful?" It obviously is. The question is "what does it buy this workload that Cloud Run does not, and what does it cost me — in toil, not just in money — to get it?"
Question 1: What shape is the traffic?
This is the question that decides most of it for me.
Cloud Run is built for request-shaped work: a request arrives, a container handles it, the container scales out under load and back down — to zero — when idle. My API is exactly that shape. My batch jobs are also that shape, just on a schedule instead of a request. The platform handles scaling, load balancing, and TLS, and I do not think about nodes at all.
GKE Autopilot starts to earn its keep when the shape stops looking like requests. Long-lived agents that maintain persistent connections to targets. DaemonSets that must run on every node. Stateful workloads that need stable storage and identity. Custom networking that Cloud Run's abstraction does not expose. A security scanner that needs to sit on a network and watch it continuously is a different shape from an API that answers questions about scan results — and the continuous watcher is the workload I would genuinely consider Kubernetes for.
For my current workloads, the traffic shape points one way. If I ever build a persistent agent that lives on a customer's network, it will point the other way, and I will not feel like I am betraying a principle by switching for that workload. Different shapes, different platforms. That is allowed.
Question 2: How much toil can one person afford?
This is the question people underweight, and it is the one that matters most when the team is one person.
Cloud Run's operational surface is small: a service, a job, an IAM binding, a revision. When something breaks, the list of things I could have misconfigured is short. GKE Autopilot removes a lot of traditional Kubernetes toil — Google manages the nodes, and you do not pay for idle node capacity in the same way — but it does not remove Kubernetes itself. You still own deployments, services, ingress, namespaces, PodDisruptionBudgets, resource requests, and the debugging that comes with them. Autopilot makes Kubernetes cheaper to run, not simpler to think about.
I have a rule for this: every platform I adopt must reduce the number of things I can get wrong at 2 AM. Cloud Run passes that test for my workloads. GKE, even Autopilot, adds a layer of concepts I would need to hold in my head for a payoff my current workloads do not use. That is not a permanent verdict — it is a budget. I have a fixed amount of operational attention, and I would rather spend it on detection logic and verification quality than on cluster hygiene.
Question 3: What does the cost curve actually look like?
I will not quote you a bill here, because a bill without a workload attached is meaningless, and anyone who gives you a precise number for a workload they have not seen is selling something. What I can say honestly is how the two cost shapes differ, because the shape is what bites you.
Cloud Run charges for what you use: requests served, time containers actually run. Idle costs trend toward zero, which is exactly what a bootstrapped builder wants for a service with bursty, uneven traffic. The risk is the opposite direction: a runaway workload — a retry storm, a scan loop that never terminates — can scale enthusiastically, which is why budgets, alerts, and concurrency limits are not optional. I wrote about cost guardrails before for exactly this reason: the bill is a function of your worst day, not your average day.
GKE Autopilot charges per Pod, for the resources you request, while the Pod runs. That shape is kinder to steady, always-on workloads and harsher to spiky ones if you over-request resources — and everyone over-requests at first, because under-requesting gets you throttled or evicted, and over-requesting just gets you a quiet bill. For a workload that is genuinely always on, Autopilot's shape can be the cheaper and more predictable one. For a workload that is idle most of the day, it is paying rent on an empty room.
My workloads are bursty. That is the whole answer to question three, for now.
Question 4: How much control do you actually need?
Cloud Run hides things: the nodes, the networking internals, some of the knobs. Most days, that is a gift. Some days, it is a wall. The honest version of this question is: "Name the specific control you need that Cloud Run does not give you." Not "more control" in the abstract — a named knob, for a named reason.
For my API, I cannot name one. VPC connectivity, private ingress, custom domains, IAM-based invocation, concurrency tuning, CPU allocation — Cloud Run exposes the controls my workload actually exercises. The day I can name a missing control — say, a requirement for a specific CNI behavior, or a sidecar pattern that genuinely needs Pod-level co-scheduling — that is the day the answer changes. Until then, "more control" is a feeling, not a requirement, and feelings are a bad reason to adopt a platform.
Security arguments cut the same way: Kubernetes is not inherently more secure, just more configurable — every knob gained is a knob you can misconfigure. Cloud Run's smaller surface means fewer misconfigurations to make, and SCC, VPC Service Controls, and Cloud Armor do perimeter work regardless of the compute behind them. The platform choice is not the security posture; the configuration is.
Question 5: What happens when you are not there?
Solo founder reality: there is no on-call rotation. There is me, and there is whatever the platform does without me.
Cloud Run's failure modes are ones I understand: a bad revision rolls back by shifting traffic to the previous one, a job fails and Scheduler retries it, a container crashes and a new one starts. Recovery is usually "the platform did it" or "I redeployed the last good revision." GKE's failure modes are richer — node issues, Pod evictions, ingress misconfigurations, a bad rollout that needs a manual rollback across namespaces — and richer failure modes need a richer mental model at the exact moment you are least able to supply one.
That is not an argument that Kubernetes is unreliable. It is an argument that its reliability depends on its operator, and its operator is currently one tired person. A platform that fails in ways I can fix quickly beats a platform that fails in ways I need to research first, at least until the team grows past one.
So what did I choose, and when would I switch?
I stayed on Cloud Run. For a stateless FastAPI API, scheduled batch jobs, and a static frontend, it is the right platform: request-shaped traffic, near-zero idle cost, a small operational surface, and failure modes one person can handle.
I would switch — for a specific workload, not for everything at once — when one of these becomes true:
- A persistent agent. A long-lived scanner or watcher that holds connections and cannot be expressed as requests or jobs.
- A named missing control. A specific networking or scheduling requirement Cloud Run cannot meet, not a general desire for "more control."
- A second operator. The day someone else shares on-call, the toil budget changes, and the calculus changes with it.
- Steady, always-on load. If a workload stops being bursty and becomes constant, Autopilot's per-Pod shape may beat scale-to-zero on cost and predictability.
Notice what is not on that list: "because everyone uses Kubernetes," "because it looks more serious," or "because I might need it someday." Those are reasons to learn Kubernetes — which I am doing, deliberately, on side projects where the stakes are learning, not production. They are not reasons to migrate a working production system.
The takeaway
The framework, stripped down, is five questions:
- What shape is the traffic — requests, jobs, or persistent agents?
- How much operational toil can your actual team afford?
- What does the cost curve look like for your shape, on your worst day?
- Can you name the specific control you need that your current platform lacks?
- What happens when you are not there to fix it?
Answer those honestly and the platform usually picks itself. For my security tooling, today, it picks Cloud Run. I expect that answer to change for some future workload, and when it does, I will write that post too — because the interesting story was never "I chose X." It is always "here is what changed, and here is what I learned from choosing wrong the first time."
Top comments (1)
Your second question is the one I would put first. I have learned that a platform I cannot reason about at 2am is not really mine yet, no matter how good it looks in a demo. I also like your switch triggers because they are specific: a persistent agent, a named missing control, a second operator, or steady load. That is a much better test than whether Kubernetes feels more serious. For my own backend work, I ask the same cost question differently: what does a bad day cost me in toil, not just in the bill?