DEV Community

Cover image for You've outgrown one server. You don't need Kubernetes.
Sharang Parnerkar
Sharang Parnerkar

Posted on

You've outgrown one server. You don't need Kubernetes.

There's a specific kind of pain that hits when your side project stops being a side project.

One box was fine. You docker compose up, point a domain at it, add Caddy for TLS, and life is good. Then you add a second server - a GPU box, a cheaper region, a machine that's just for the database - and suddenly nothing is simple. Which container runs where? How does the app on box A reach the database on box B? What happens at 3am when box A reboots and the containers don't come back?

The industry's answer to that question is Kubernetes. And Kubernetes is a genuinely great answer - to a problem most of us don't have. The moment you adopt it you've also adopted etcd, a CNI plugin, an ingress controller, cert-manager, a service mesh you'll pretend you understand, and a deployment.yaml that's 120 lines to run one container. You wanted to deploy an app; you got a second full-time job.

At the other end there's Coolify, Dokku, CapRover - lovely tools that make one server feel like Heroku. But they're built around one server. The moment you have two, the model starts to strain.

So there's this gap. Bigger than one box, smaller than a platform team. That gap is where I've been living, and it's why I built Orca.

What Orca is

Orca is a single binary. You run it on your master node, orca join on the others, and you have a cluster. It schedules containers and WebAssembly modules as first-class workloads, and it ships with the things you'd otherwise bolt on: a reverse proxy, automatic TLS via Let's Encrypt, encrypted secrets, health checks, and an AI ops assistant that can actually explain why a service is down.

The config fits on a screen. Here's a real service:

[[service]]
name = "api"
image = "myorg/api:latest"
replicas = 3
port = 8080
domain = "api.myapp.com"        # TLS is provisioned automatically
health = "/healthz"

[service.env]
DATABASE_URL = "${secrets.db_url}"

[service.placement]
node = "worker-1"               # or match by labels
Enter fullscreen mode Exit fullscreen mode

That's the whole thing. No YAML anchors, no Helm chart, no kustomize overlay. You commit it to git, and if you turn on the reconciler, dropping a new service.toml in the repo is the deploy.

The design bets

Three opinions shaped it:

One binary, not a control plane. Everything - API server, scheduler, proxy, reconciler - is in one process written in Rust. There's no external database to operate. State lives in an embedded store, and the cluster survives the master restarting.

TOML you can hold in your head. If a config file needs a tutorial, the tool failed. Every field maps to something obvious. The mental model is small on purpose.

No hidden dependencies. An orchestrator that requires you to also run a secret manager, a service discovery layer, and a message bus has just moved the complexity, not removed it. Orca's secrets, for example, live encrypted in your git repo - no Vault, no sidecar. (That's the next post.)

Who it's for

If you're on Kubernetes and happy, stay there. Orca isn't trying to replace it - it's for the people who bounced off it, or who never should have been there. Two to a dozen nodes. A homelab that got serious. A small team running a real product on hardware they actually understand.

Over the next few posts I'm going to go deep on the parts I'm proud of and the bugs that taught me the most - including a WebSocket session that was silently dead for ten days while every dashboard insisted the node was healthy. That one rewired how I think about "liveness."

Orca is open source (AGPL-3.0) and on GitHub. If the gap I described sounds familiar, I'd love to hear how you're handling it in the comments.

Top comments (0)