Why I Still Choose Boring VPS Hosting Over AWS in 2026 (And How It Lets Me Ship Faster)

Wait 5 sec.

I want my hosting to leave me alone long enough to finish the feature. After more than two decades of running web projects, that remains a surprisingly useful architectural requirement.I still rent servers and put websites on them.Sometimes it is a VPS. Sometimes it is a dedicated machine. I install Linux, configure Apache or Nginx, run my application as a process, and arrange backups and monitoring. My production websites and services do not require AWS, GCP, or Azure.This is not a nostalgia project. I like new tools when they remove work. I am less enthusiastic when they rename the work, distribute it across six dashboards, and charge separately for each part.Over the years, I have noticed a change in the conversations around web development. I hear less about what a business needs and more about which platform components belong in the architecture.A perfectly ordinary application somehow acquires 25 managed services. Then I am discussing IAM policies, network boundaries, deployment manifests, queue permissions, and billing dimensions before the product has done anything useful.I can do that work. I would rather have a reason.I build for the workload in front of meMost of my projects have recognizable requirements.A marketing site needs to load quickly, accept inquiries, and stay editable. A SaaS application needs authentication, a database, background work, and a dependable release process. An internal tool needs to make somebody’s spreadsheet less miserable. An API needs to answer requests correctly within an acceptable time.My starting point is those requirements, not an infrastructure diagram.I am not preparing for 50 million users on launch day. I am preparing for the traffic I can reasonably expect, with enough headroom for a busy afternoon and a plan for growth.I care about peak concurrency, expensive queries, memory consumption, storage growth, and acceptable recovery time. A registered-user count tells me much less than the workload those users generate.Before I split an application into services, I want to know whether I have an architectural problem or an unnecessary query inside a loop. My experience has made me suspicious of solving the latter with additional computers.I also distinguish between keeping options open and implementing every option immediately. I can maintain clean application boundaries without turning each boundary into a network connection.My usual stack is small enough to explainFor a modest application, my starting arrangement is often Nginx in front of an application process, PostgreSQL, and a background worker.I may put them on one machine when the capacity and availability requirements allow it. I do not treat that arrangement as suitable for every workload. I do treat it as a legitimate starting point.For an HTTPS reverse proxy, my application-specific Nginx configuration can look like this, assuming I have already provisioned the certificate and configured renewal. Nginx supports both the TLS configuration and proxying directly, as described in the Nginx TLS configuration guide.server { listen 443 ssl; server_name app.example.com; ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; }}That is the HTTPS routing block, not my entire production checklist. I still configure HTTP redirection, appropriate request limits, firewall rules, and the application’s trusted-proxy settings. My application listens on loopback rather than exposing its backend port publicly.But I can read the routing arrangement in one sitting.I usually let systemd supervise the application, running it under an unprivileged account with an explicit restart policy as described in the systemd.service documentation. Automatic process restart is already available there; I do not need a cluster merely to restart a failed process.If containers make packaging easier, I am happy with a basic Docker Compose file. I do not object to YAML. I object to how much of my week it occupies.For an overnight report, I might use a cron job. For continuous background work, I might use a daemon. I do not begin with EventBridge and three Lambda functions simply because the job happens after another job.The simplicity still needs engineering. I make important work retryable, prevent overlapping executions, record completion, and alert on missed runs. I keep durable job state when losing work would matter.If those requirements justify a proper queue, I use one. A timer is not a delivery guarantee, and I do not pretend otherwise.I treat understandability as an architectural metricI have become much more interested in how quickly I can understand a broken system than how impressive its healthy-state diagram looks.When my application stops responding, I usually SSH in and start with a short set of checks:sudo journalctl -u myapp.service --since "15 minutes ago" --no-pagerhtopdf -hsudo tail -n 50 /var/log/nginx/error.logI use journalctl to inspect the service’s recent messages and htop to see what the processes are doing. Neither tool requires me to reconstruct a platform before I can begin investigating.I am looking for something specific. Did the process exit? Did a deployment introduce an exception? Is the disk full? Is a worker consuming the memory I intended for the database?I do not always find the answer immediately. Linux has never promised to make every mistake obvious. What I usually have, though, is a short path from the symptom to useful evidence.In a heavily managed architecture, my investigation may also involve VPC peering routes, security groups, network ACLs, role trust policies, and permission boundaries. Those are real troubleshooting surfaces, not imaginary complaints about the cloud, as you can see in AWS’s VPC peering troubleshooting guide.Then I may still need to inspect the application, its managed queue, and the provider’s service status.I do not consider those tools defective. I consider every additional boundary something I must understand during an incident.A green provider dashboard is not an explanation for my failed request.My preferred architecture is one whose request path I can describe without opening a console. That understanding is part of how I recover, and part of why I am comfortable changing it.I do not confuse cloud hosting with resilienceI take hyperscaler reliability seriously. I do not treat it as immunity.During AWS’s US-EAST-1 disruption on October 19 and 20, 2025, a race condition in DynamoDB’s DNS management automation produced an empty record for its regional endpoint. The failure affected dependent AWS services, with recovery problems extending into other systems, according to AWS’s post-event report. The same report says existing EC2 instances remained healthy, which is an important qualification. I would not describe the incident as every AWS server falling over.On November 18, 2025, Cloudflare experienced a major outage after a database permissions change caused a Bot Management feature file to grow beyond the size its traffic-handling software expected, as detailed in Cloudflare’s outage post-mortem. The file was distributed across its network, and the software failed. I include that example as a shared edge dependency, not because Cloudflare and AWS are interchangeable hosting products.My takeaway from those incidents is concentration risk. A shared dependency can turn one failure into problems for many otherwise unrelated applications. That remains possible even when the dependency is operated by a company with substantial engineering resources, which is exactly what the AWS report illustrates.None of this proves that my inexpensive VPS is more available than a properly designed multi-region system. It is not evidence for that claim.My VPS provider can lose a host. A network can fail. I can deploy something foolish.I choose based on the availability I actually need, the failure modes I can manage, and the recovery process I have tested. A provider’s size does not answer those questions for me.I prefer bills that do not need interpretationFor a modest project, I often start with a VPS budget in the USD 10 to USD 40 per month range.That is a server budget, not a promise that every application fits there. As a concrete reference, DigitalOcean lists regular Basic plans at USD 12 and USD 24 per month, with included transfer allowances and monthly caps on the bundled server pricing.I like knowing the ordinary monthly compute cost before I deploy.I do not want a 14-page metered invoice to become another debugging exercise. I especially dislike discovering that moving data between parts of an architecture is a meaningful part of its operating cost.A VPS does not exempt me from reading the terms. I check transfer allowances and overage rules. I budget for backups, monitoring, email delivery, and any additional storage. Those are not all magically included in the server price. DigitalOcean, for example, prices backups separately.My time also costs money.The advantage, for me, is not merely a smaller number. It is fewer billing behaviors to keep in my head while I make product decisions.I still do the operational workMy version of boring hosting includes maintenance. Otherwise, I have simply rented a future incident.I patch the operating system and application dependencies. I restrict access. I keep secrets out of the repository. I rotate logs and watch disk usage. I monitor the service from somewhere other than the machine serving it.I keep provisioning scripts and configuration under version control. I do not want a server whose recovery procedure begins with remembering what I typed two years ago.SSH is a diagnostic tool, not my deployment strategy.My releases are repeatable, and I retain a known-good application version. I also plan database migrations with care, because rolling back an executable does not necessarily undo a schema change.For backups, I keep encrypted copies outside the server and test restores into a separate environment. I do not count a successful backup command as proof that I can recover.I match database backup methods to the tolerated data-loss window. Periodic PostgreSQL dumps may meet one project’s requirements. A shorter recovery window may justify base backups and continuous WAL archiving for point-in-time recovery, which as the PostgreSQL backup documentation explains, is a different arrangement from pg_dump.I write down how long I can afford to be offline and how much data I can afford to lose.If those limits rule out a single machine, I stop using a single machine. I budget for redundancy, database recovery or failover, and the work needed to operate them.The low anxiety comes from knowing this routine and keeping it manageable. It does not come from ignoring it.I know when a larger platform earns its keepI would consider hyperscalers for requirements that genuinely benefit from their services: substantial geographic distribution, unusual capacity demands, specific enterprise controls, or a team already organized around that platform.I would consider Kubernetes when I have Kubernetes-shaped problems, such as a fleet of independently deployed workloads that needs a shared operating platform.I would also budget for operating it. Kubernetes’s own production environment guidance discusses replicated control-plane components, worker capacity, networking, security, and recovery. That is reasonable work when the problem justifies it. It is not work I automatically want for a small application.I also know this is not literally “VPS versus cloud.” AWS offers straightforward virtual-server bundles through Lightsail, including predictable pricing. I could run a small Linux stack there. My preference does not depend on pretending AWS is incapable of simplicity.I prefer a hosting arrangement that leaves me with a server, clear responsibilities, and few reasons to visit the provider’s console.That preference has limits. I am willing to change it when the requirements change.I ship faster because I have fewer unrelated decisionsOn my familiar stack, adding a feature mostly means changing application code, testing it, and deploying it.I am not choosing another managed service, creating another role, or learning another provider-specific failure model unless the feature actually requires it.The time savings are often small and ordinary. Less documentation to reread. Fewer credentials to arrange. A shorter investigation when something breaks. More of the afternoon left for the application.I have found that those small savings accumulate.There is room in my work for ambitious engineering. I would rather spend it on making the product useful than making its hosting elaborate.When a measured requirement changes, I will reconsider the infrastructure. Until then, I would rather finish the feature than finish the platform.