yanncabral.dev/writing/Why I used Uncloud, not Kubernetes
Back
Infra

Why I used Uncloud, not Kubernetes

YCYann CabralApr 20266 min readuncloud

I had a product with a web app, API, worker, engine, Redis, Temporal, QuestDB, Meilisearch, ClickHouse, Signoz, and a few internal tools. The default internet answer was Kubernetes. I used Uncloud on one VPS instead. That was the right call.

Kubernetes would have made this setup look more serious. Uncloud made it ship.

That's the whole thesis.

I had a real system to run: public web surfaces, internal dashboards, background workers, stateful infra, observability, auth in front of private tools, and enough moving parts that "just run Docker Compose" starts feeling a little flimsy. The temptation was obvious: install Kubernetes, wrap everything in Helm charts, and call myself a platform engineer.

I didn't. I used Uncloud.

The shape of the system

This wasn't a toy app. The deployment had:

  • public services
  • internal services behind auth
  • private network-only services
  • stateful components like Redis, Postgres-compatible stores, search, and telemetry

The useful part is that I could describe all of it in one uncloud.compose.yml file and deploy slices of the system with one command.

uc deploy --connect "$UNCLOUD_CONNECT" -f deploy/uncloud.compose.yml \
  temporal-db temporal temporal-ui redis questdb meilisearch clickhouse signoz nocodb --yes

uc deploy --connect "$UNCLOUD_CONNECT" -f deploy/uncloud.compose.yml \
  web api engine worker --yes

That split was deliberate: infra first, app second. No charts. No operators. No "why is my ingress controller talking to cert-manager through a CRD that changed last month?"

Why I didn't want Kubernetes here

Because Kubernetes solves a different problem.

Kubernetes is great when you actually need cluster scheduling, multi-node elasticity, team-level standardization, hard isolation, and all the ceremony that comes with those things. I had one VPS.

On one VPS, Kubernetes mostly gives you a second infrastructure project.

Now you are not shipping your product. You are debugging:

  • ingress
  • persistent volume behavior
  • secrets wiring
  • rollout strategy
  • Helm drift
  • random YAML archaeology

All to end up with the same containers on the same machine.

If the topology is one box, I want software that respects that fact.

That was the whole decision.

What Uncloud gave me instead

The sweet spot was: more structure than raw Docker Compose, much less operational tax than Kubernetes.

I still got:

  • service discovery by service name
  • public host routing
  • private internal networking
  • deploy orchestration
  • a clean CLI story

And I got it without pretending I was running a small cloud.

A service could talk to Redis at redis:6379, to Temporal at temporal:7233, and to other internal services by name. Public routes lived next to the service definition.

services:
  web:
    build:
      context: ..
      dockerfile: deploy/docker/web.Dockerfile
    x-caddy: |
      app.example.com {
        reverse_proxy {{upstreams 3000}}
      }

  api:
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=redis://redis:6379
      - TEMPORAL_ADDRESS=temporal:7233

That matters more than people admit. Infra gets easier when the topology is legible.

The underrated win: one source of truth

The best part of this setup was not the deploy command. It was the fact that the compose file became an actual contract.

I wrote a small script to parse the environment schemas for each runtime and compare them against the env vars declared in uncloud.compose.yml. If a service was missing a required env var, the script failed fast.

That killed an entire class of production mistakes:

  • env added in code, forgotten in deploy
  • optional vs required drift
  • service config silently diverging from runtime expectations

This is the kind of thing Kubernetes fans often attribute to "platform maturity." You can get a lot of that maturity without signing up for the whole stack.

What changed in practice

Three benefits showed up immediately.

1. Faster deploy decisions

I could reason about the whole system in one file. Public hosts, internal tools, service dependencies, stateful volumes, app services. No hunting across manifests.

2. Lower debugging cost

When something broke, I was debugging the app or the container. Not the app, the container, the ingress controller, the storage class, and the chart values.

3. Much less infrastructure theater

This matters. A lot of teams adopt Kubernetes because it signals scale, not because it solves a present problem. For a single-machine deployment, the extra abstraction is often just prestige debt.

The real thresholdI'd reach for Kubernetes when the machine boundary stops matching the product boundary: multi-node scheduling, stronger isolation, serious scaling pressure, or a team large enough to need that control plane. Before that, simpler wins.

What I gave up

A few real things:

  • a more standardized path if I were handing the system to a Kubernetes-native infra team
  • stronger primitives for horizontal scaling later
  • access to the broader Kubernetes ecosystem

Those are real tradeoffs. I just didn't need them yet.

And that's the part people skip. The question isn't "is Kubernetes more powerful?" Obviously yes. The question is whether you need that power now, on this topology, for this team.

For me, the answer was no.

I'd do it again

If I have one VPS and a serious product, I will take Uncloud over Kubernetes again without hesitation.

Not because Kubernetes is bad. Because unnecessary control planes are bad.

The more I build, the more I like infrastructure that matches the size of the problem. On one machine, I want one-machine tools. Uncloud gave me enough structure to run a real system without paying the Kubernetes tax early.

More posts