
Introduction
For years, my default way of putting a side project online was dead simple:
- One VPS
- One project
- A bit of manual configuration and a couple of scripts
It worked, until it didn't.
As the number of projects grew, so did the number of servers to patch, backup, and babysit. Each VPS had its own little quirks and half-documented setup (if any hmhm). I slowly realized I wasn't just building apps anymore. I was running a small, fragile hosting platform entirely by hand.
This post tells the story of how I moved from that "one VPS per project" world to a standardized, Kubernetes-based PaaS. It's not a product pitch. It's about the ideas and trade-offs behind turning a pile of pets (servers) into something closer to cattle (a platform).
Embracing the DevOps philosophy
The "one VPS per project" approach fails for the same reasons that classic ops silos failed in larger organizations:
- Ops work doesn't scale. Every new project adds another box to patch, monitor, and keep alive.
- Improvements don't propagate. Fixing something on one server doesn't magically improve the others.
- Knowledge is scattered. How an app is deployed lives in shell history, random notes, and muscle memory.
I wanted something closer to a DevOps mindset, even for personal projects:
- Treat infrastructure and deployments as products, not afterthoughts.
- Prefer automation to heroics. The platform should do the boring work.
- Have a single source of truth for what is deployed, where, and in which state.
The result is a small PaaS: a backend that knows about users, projects, builds, and deployments, and a cluster that actually runs the workloads. My job shifts from "set up this app" to "send this app through the same pipeline as all the others."
Why kubernetes
I didn't rewrite everything around Kubernetes because it's fashionable. I got there by elimination.
Running a few apps on a single VPS or with simple Docker setups works great, until you want more:
- Isolation between different users and projects
- A place to run builds that isn't your laptop or a random CI box
- A consistent way to expose apps to the internet
Kubernetes gives me a set of building blocks that map nicely to those needs:
- Namespaces to isolate different tenants and ephemeral work like builds
- Deployments and Services to describe how apps run and how they are reached
- Ingress to handle routing and TLS in a unified way
In this setup, Kubernetes is the substrate. The PaaS backend's job is to translate high-level actions ("deploy this repo on this branch") into changes in the cluster.
The switch
The real switch wasn't "install Kubernetes." It was changing how I think about deployments.
Instead of "this project lives on that VPS," I now think in terms of records and state:
- A deployment is a description of what should run (repo, branch, port, owner).
- A build is the process of turning that description into something runnable.
Both of these live in a database. They have statuses, timestamps, and relationships. From there, the platform can decide what to do next:
- Queue a build
- Trigger a deployment
- Update status and expose a URL
Once everything is modeled explicitly, it's much easier to automate the flow than when deployments are just an SSH session and a git pull.
First deployment
The first time the new platform took a repository and turned it into a live URL without me SSHing anywhere felt like cheating.
From the outside, the flow is really simple:
- Choose a GitHub repository and branch.
- Tell the platform which port the app listens on.
- Click deploy and wait for a URL.
Behind the scenes, the platform:
- Builds a container image for that repository.
- Creates an isolated space in the cluster for that user.
- Starts the app and wires it to the outside world with a domain and TLS.
What matters is not each technical step, but that the same path is used for every deployment. Whether it's my personal project or someone else's, it goes through the same pipeline.
Automating things
Once the basic "build and deploy" loop worked, the rest was about raising the level of automation.
Some examples:
- The platform always knows the status of builds and deployments.
- Logs are streamed to a central place instead of scattered across servers.
- TLS, routing, and cleanup are handled by the platform, not by random scripts.
The important part is that these behaviors are built in, not something I have to remember to do for each project. Adding a new app becomes a matter of plugging it into the platform, not reinventing the deployment wheel.
Final words
Moving from "one VPS per project" to a Kubernetes-based PaaS wasn't a single big migration. It was a series of small decisions that all pointed in the same direction:
- Make deployments repeatable instead of unique.
- Capture them as data and workflows, not as tribal knowledge.
- Let a platform handle the plumbing so I can focus on the apps.
Kubernetes is only one piece of that story. The real shift is treating my own infrastructure as a product, something that should have a clear design, consistent user experience, and sensible defaults.
If you're still in the "one VPS per project" phase and starting to feel the drag, you don't have to jump to Kubernetes overnight. But you can start by asking: what would it look like if all my projects went through the same deployment pipeline? This PaaS is my answer to that question.
© 2025 Theo Poette. All rights reserved.