In defence of the single small server
I have a box with two shared vCPUs and under two gigabytes of memory. It runs a git host, a push notification relay, a reverse proxy and a handful of static sites. It has been doing this for years, and the most interesting thing about it is how uninteresting it has been.
This is not a claim that scale doesn't exist. It's a claim that most side projects, internal tools and personal infrastructure never approach the point where a second machine earns its keep.
What one box actually gets you
The obvious win is cost, but that's the least of it. The real win is that every failure has one place to look. No service mesh, no cross-AZ latency, no question about which replica served the bad response. When something breaks you SSH in and read a log file.
The second win is that state is easy. A SQLite file on local disk is a perfectly good database for something with a handful of users. Backing it up is cp. Restoring it is cp. There is no replication lag to reason about because there is no replication.
Where it stops
The honest limits, roughly in the order you hit them:
- Memory. Long before CPU. Anything JVM- or Node-shaped will eat the machine. Add swap and set
vm.swappinesslow; it buys real headroom for services that allocate once and then idle. - Deploys with downtime. Fine until someone else depends on it.
- The upgrade you keep postponing. This is the one that actually kills these machines.
The failure mode nobody plans for
A single server doesn't die from load. It dies from neglect. The distribution goes end-of-life, the package mirrors get pulled, and one day you discover you can't install a security patch even if you want to. By then the machine has accumulated four years of undocumented configuration that exists only in your memory and in /etc.
The certificate had been expired for six years. Nobody noticed because a CDN sat in front of it and never checked.
The fix isn't more servers. It's treating the machine as disposable: write the setup down as a script, keep the data small and separable, and rebuild rather than upgrade. If reprovisioning takes an afternoon instead of a weekend, you'll actually do it.
A checklist I now use
- Everything the machine does lives in one
docker-compose.ymland one setup script, both in version control. - Data is under a single directory, and it is small enough to
rsyncin under a minute. - Unattended security upgrades are on.
- Something tells me when a certificate is close to expiring — and it has been tested at least once.
- There is a second way in that does not depend on the public IP still working.
That last one is easy to skip and expensive to skip.