Infrastructure Sanity with a Central Config: One File to Rule Them All

Before deploying apps or spinning up Kubernetes, you need something solid underneath — the server layer. That’s where this piece fits in: the base infrastructure stack that everything else depends on.
The goal? Build a minimal, isolated, and reproducible environment on Hetzner Cloud — one that doesn’t burn €10/month per project just to exist. Kubernetes (kreativarc k3s) and the actual applications (poc
, infra tools
, app1
, app2
) come later — but none of that works without a clean, affordable foundation.
The first version of this Pulumi-based infrastructure was… verbose. Not in the literary sense — just in the number of files. Each server had its own config scattered across multiple files, with subtle differences that were easy to miss and even easier to break. Adding a new machine felt like assembling IKEA furniture from schematics written in Morse code.
So I did what any developer does after a few rounds of self-inflicted chaos: I centralized.
The new setup revolves around a single inputConfig
object — a plain TypeScript file that defines everything needed to bootstrap a secure, reproducible infrastructure stack on Hetzner. One file. One format. No guesswork.
What It Does
The stack provisions an isolated cloud environment — private network, subnets, firewalls, and servers — all invisible to the outside world. There’s no public SSH access, no exposed ports. Cloudflare Tunnel will eventually be the only ingress point.
It automatically:
sets up a
192.168.x.x
internal networkgenerates SSH keys locally (and only locally — they never leave your machine)
creates firewalls that allow only trusted IPs
provisions servers, each with the correct network, SSH key, and firewall rules
Here’s a stripped-down version of the config example:
{
projectName,
stackName,
adminPublicIp,
network: {
type: "cloud",
ipRange: "192.168.100.0/24",
networkZone: "eu-central"
},
servers: [
{
name: "control-plane",
serverType: "cx22",
image: "ubuntu-22.04",
location: "nbg1",
firewallRules: [
{
direction: "in",
protocol: "tcp",
port: "22",
sourceIps: [`${adminPublicIp}/32`],
description: "SSH access"
}
]
},
{
name: "worker-poc",
serverType: "cx22",
image: "ubuntu-22.04",
location: "nbg1",
firewallRules: [
{
direction: "in",
protocol: "tcp",
port: "22",
sourceIps: [`${adminPublicIp}/32`],
description: "SSH access from admin"
}
]
}
]
}
Why It Matters
This config isn’t just for readability. It makes the entire system scalable.
Want to add a new server? Append it to the array.
Want a new environment — say, dev
in parallel to prod
? Just switch the stack and reuse the config structure. Since everything's parametrized and the naming is stack-aware, resources stay isolated and reproducible.
Clean Infra, Fewer Surprises
By keeping the input structure minimal and declarative, the actual resource logic becomes dead simple. Each Pulumi component (network, firewall, server, SSH key) just reads from this object and builds itself accordingly. There’s no hand-written state, no hardcoded IPs, and no shared secrets.
The only thing you need to provide is your current public IP — passed in securely as a Pulumi secret — to allow SSH access during provisioning.
pulumi config set adminPublicIp $(curl -s ifconfig.me) --secret
Once that’s in, the infra is yours. Clean. Predictable. And, for once, not screaming at you in YAML.
Planned VPS Scaling
While the long-term plan may involve abstracting away VPS management entirely, for now, each node is still a separate, explicitly defined server. That said — scaling the cluster is already trivial.
Thanks to the declarative inputConfig
, provisioning a multi-node setup (control plane + multiple workers) takes just a few lines. Need a new worker? Add an entry to the config, pulumi up
, and it’s done.
The goal is flexibility: you can spin up a POC node today, and production-grade workers tomorrow — without refactoring the entire infrastructure.
Next up: testing. Because we don’t just trust infra — we verify it.
Subscribe to my newsletter
Read articles from Arnold Lovas directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Arnold Lovas
Arnold Lovas
Senior full-stack dev with an AI twist. I build weirdly useful things on my own infrastructure — often before coffee.