A large machine lets you postpone questions. A small one asks them immediately, and the answers are the same ones you need at any size.
A single small server is an unforgiving place to run anything. That is its main virtue.
A large machine lets you postpone questions. A small one asks them immediately, and the answers turn out to be the same answers you need at any size, arriving earlier and far more cheaply. Here are the ones worth knowing before the machine teaches them to you.
Logs will take down your database
Container logging drivers commonly write files that never rotate. Nothing warns you. The files simply grow, for months, at whatever rate your traffic dictates.
The first thing to break is not logging. It is the database, which hits a full disk mid-write and stops. The outage presents as a database problem, the cause is your logs, and the logs are the first thing you would reach for while debugging.
The fix is a few lines of configuration: cap the size per file and the number of files retained, for every long running service. On a larger machine this failure takes a year to arrive instead of a month, which is worse rather than better, because by then nobody remembers that logging was never bounded.
Unbounded anything is a scheduled outage. The disk only decides when.
Your firewall may not be protecting your database
This one is worth checking on your own infrastructure right now, whatever size it runs at.
Container runtimes that publish a port commonly insert their own forwarding rules ahead of the ones you wrote by hand. Publishing a database port and then blocking that port in your firewall can leave you with a database reachable from the internet and a firewall confidently reporting it closed.
What actually keeps it private is binding the published port to the loopback address rather than to every interface. Host side tooling such as backups still reaches it, and nothing off the machine can. Your firewall rule becomes a second layer rather than the only one.
The general lesson is harder than the specific one: a control you have never tested from the outside is a belief, not a control. Scan yourself from another network. It takes a minute and it is the only version of this check that counts.
Give every service a ceiling
Set a memory limit on each service. The point is not efficiency, it is blast radius.
Without limits, the process that misbehaves decides who dies, and on a shared machine the casualty is usually whichever process next asks for memory. That is frequently the database, which is the one component holding state you cannot rebuild.
With limits, a runaway service hits its own ceiling and is killed on its own. One thing degrades instead of the whole machine. This is some of the cheapest reliability work available, and it stays correct at every scale.
Build where the memory is
Compiling an application on the same machine that is currently serving traffic puts the build and the application in competition, and the build is greedier. A naive deploy that builds in place can take a small site down while it works.
The answer is not a bigger machine. It is a build that happens somewhere with room for it, producing an artifact the server only has to run. That separation is also what makes a rollback instant, because the previous artifact still exists and does not need rebuilding.
Backups you have not restored are not backups
A backup job that exits zero proves a file was written. It does not prove the file contains a usable database, that the credentials still work, that the retention policy keeps anything useful, or that you can get it back inside an acceptable window.
Restore one, on purpose, into a scratch environment, before you need to. Then write down how long it took, because that number is your real recovery objective regardless of what any document claims.
What small infrastructure does not excuse
Running lean is not a reason to skip the things that are painful to retrofit. Even at the smallest scale, from day one:
- Structured logs with a request ID that follows a request across every component it touches.
- Timeouts and cancellation on every outbound call, so one slow dependency cannot exhaust your capacity.
- Connection pooling, with a pool ceiling your database can actually serve.
- Versioned, reversible migrations, tested against a table with data in it.
- Readiness endpoints and graceful shutdown, so a deploy does not drop in-flight requests.
- One real background job path, because the second one is easy and the first one is architectural.
None of those are scale features. They are the things that are nearly impossible to add later without touching every file, which makes them the opposite of premature.
Name the wall before you hit it
Being specific about limits beats pretending there are none. For any system, write down the number that means it is time to act, before you need it: sustained writes per second, working set size against available memory, the point at which a backup stops fitting in the quiet hours, the queue depth that means consumers are falling behind.
The value of writing them down is that you act on a measurement rather than a feeling, and nobody is surprised.
Small infrastructure is not a lesser version of the real thing. It is the real thing with the failure modes arriving early, while they are still cheap to fix.