What a 3-Person Team Actually Needs for Infrastructure in 2026
Every time I read an infrastructure blog post from a company with a dedicated platform team of fifteen people, I have to mentally translate it for a team our size, which is three engineers, none of whom have DevOps in their title, all of whom would rather be shipping features than managing a service mesh. I want to write the version of that post for teams like ours, because the advice aimed at companies fifty times our size is not just unhelpful, it is actively harmful if you try to adopt it prematurely.
The rule we optimize for above all else
Every infrastructure decision we make gets filtered through one question: can one person understand and fix this at 11 PM without waking anyone else up. Kubernetes fails this test immediately for a team our size, not because it is bad technology, it is excellent technology, but because the operational surface area, networking, RBAC, ingress controllers, autoscaling configuration, is enormous, and none of us touch it often enough to keep that knowledge fresh. Complexity you do not use daily rots into a liability.
What we actually run
Our application, a Next.js app with a Postgres database and a Redis cache, deploys to Coolify running on a couple of Hetzner VPS instances, one for production and a smaller one for staging. Coolify gives us a self-hosted, Heroku-like deploy experience, git push, build, deploy, rollback, on infrastructure we control and pay for at roughly a tenth of what an equivalent managed platform would cost at our traffic level. I want to be clear this is not a knock on managed platforms like Vercel or Railway, we used Vercel for the first year and it was genuinely great, we moved off it specifically because our workload includes some longer-running background jobs that did not fit its serverless execution model cleanly, not because anything was broken.
Database-wise, we run Postgres directly on the VPS with automated daily backups pushed to S3-compatible object storage, plus point-in-time recovery enabled via WAL archiving. I was nervous about self-hosting the database at first, the managed database marketing has convinced a lot of people that running Postgres yourself is somehow exotic, but Postgres is remarkably boring and stable software, and for our scale, a few million rows and moderate write volume, the operational burden has been maybe two hours a month, mostly checking that backups actually restore correctly, which we test quarterly by doing an actual restore into a scratch environment rather than trusting the backup job's exit code.
Monitoring: less is more
We run a single Grafana instance backed by Prometheus for metrics and use Sentry for error tracking and performance monitoring on the application side. That is genuinely the entire monitoring stack. No dedicated logging platform beyond what Coolify captures and forwards, no distributed tracing setup, because we do not have a distributed system, we have a monolith talking to a database and a cache, and tracing a request through three services when you only have one service is solving a problem you do not have.
The alerting rules we actually kept, after pruning aggressively, are: error rate spike detected by Sentry, database connection pool exhaustion, disk usage above 80 percent, and HTTP 5xx rate above a threshold on our load balancer. Everything else we tried alerting on early, CPU usage, memory usage, request latency percentiles, generated so much noise that we started ignoring pages, which is the actual failure mode of over-alerting, not that you get too much information, but that you train yourself to stop trusting the pager.
CI/CD
GitHub Actions runs our test suite and type checks on every pull request, and merging to main triggers a webhook that Coolify picks up to build and deploy automatically. Total pipeline time is under five minutes, which matters more than people give it credit for, because a slow pipeline is a pipeline people start working around, batching changes into bigger, riskier deploys to avoid waiting through it repeatedly.
name: CI
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm run test
We do not run a separate staging deploy pipeline for every PR, we deploy preview environments are on our list but honestly have not been worth the setup time yet given our team size, three people can coordinate who is testing what on staging with a Slack message.
What we deliberately do not have
No Kubernetes, as mentioned. No service mesh, obviously, since that only makes sense once you have multiple services that need to discover and authenticate each other, and we have one. No dedicated secrets management platform beyond environment variables managed through Coolify's built-in encrypted secrets store, which is not as sophisticated as Vault but is more than sufficient for our threat model. No multi-region deployment, our users are concentrated enough geographically that a single well-chosen region with a CDN in front for static assets covers our latency needs, and multi-region active-active setups introduce data consistency problems that are simply not worth solving until you actually need the redundancy or the latency improvement.
The actual cost
Our entire infrastructure bill, two VPS instances, object storage for backups, and the managed services we do pay for, Sentry and a transactional email provider, comes to a little under 200 dollars a month. I have seen teams our size spend that much on a single managed database instance alone. The savings are nice, but the bigger win is cognitive: nobody on our team needs to hold a mental model of a system more complex than the one we actually have. When something breaks, and things do break, the blast radius and the debugging surface are both small enough that whoever is on call can genuinely reason about the whole system, not just their corner of it.
The advice I would give any team our size is to resist infrastructure decisions made by imagining the company you might become rather than the company you are. You can always add complexity later, when you actually hit the wall that justifies it. You cannot easily remove complexity you adopted prematurely, because by the time you realize it was a mistake, three other things depend on it.
I want to be fair to the other side of this too, because I have seen the opposite mistake happen just as often: a team stays deliberately simple past the point where simplicity is actually serving them, purely out of inertia or an aversion to change, and ends up firefighting outages that a modest infrastructure investment would have prevented. The discipline is not never add complexity, it is add complexity in response to a wall you have actually hit, not a wall you are imagining three years out. We revisit this stack explicitly every six months, not to add things for their own sake, but to ask honestly whether anything on our current setup is now causing more pain than it is preventing. So far the answer has mostly been no, but I expect that to change eventually as we grow, and when it does, I would rather make that call deliberately, with evidence, than drift into complexity the same way we drifted into eleven microservices the first time around.
Written by AIDToolStack Editorial
The AIDToolStack editorial team reviews developer tools, AI products, and MCP servers with hands-on testing and honest assessments. We use the tools we review every day in production.
References & Further Reading
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.