How We Chose Infrastructure for 80+ News Apps (And What It Actually Costs)

We run over 80 news apps from a single Rails backend. Each app pulls articles, sends push notifications, and serves content to thousands of users daily. The backend itself is a recent rebuild—we converted a legacy Java application to Rails, modernizing the entire stack in the process.
The old Java setup ran on six dedicated servers at Hivelocity with CDN services from Ray Networks—Sandy Bridge Xeons from 2012, Ubuntu 15 and 16, self-managed. Our finance team pulled the last 12 months: $12,200 for Hivelocity and $10,900 for Ray Networks. That's $23,100 per year—roughly $1,925/month to host a legacy Java app.
With the rebuild complete, we had a clean opportunity to evaluate hosting from scratch. No legacy infrastructure tying us down, no assumptions carried over. Just a fresh Rails app deployed with Kamal, ready to go wherever the best value was.
This is the story of how we evaluated five different hosting approaches, ran the numbers on all of them, and landed on a stack that costs $358/month—saving over $18,000 per year while upgrading from 14-year-old hardware to modern dedicated CPUs with managed services.

Where We Started

If you've read my earlier posts, you know the journey. We were on AWS spending $135/day before I started cutting across our entire portfolio. Kamal freed us from Kubernetes and AWS lock-in. We moved to AmericanCloud and Cloudflare—simple, flat pricing, no Terraform required.
That setup worked. But as our app portfolio grew and the NewsFusion rebuild wrapped up, I started asking: are we getting the best deal for this new backend? We use Kamal for deployments, which means our entire stack is Docker containers deployed via SSH. We're not locked into anyone. So I did what any cost-conscious CTO should do—I shopped around.

The Five Options We Evaluated

I put together an apples-to-apples comparison for equivalent infrastructure: web servers, managed PostgreSQL, load balancing, and bandwidth for a Rails app serving 80+ news apps via Kamal.
Here's what I found:
Cloudflare + AmericanCloud: ~$474/month
Our baseline. Cloudflare LB at $5, AmericanCloud networking at $12, managed Postgres at $217, and 4 compute servers at $240. Simple and it works—but that managed Postgres price is 3–5x what other providers charge for the same specs.
AWS (on-demand, Graviton): ~$565/month realistic
I went deep on this one because AWS is always the default comparison. Using Graviton ARM instances in us-east-1, the sticker price looks competitive—until you add up everything AWS doesn't include. Here's the real line-by-line:
ComponentAWS InstanceSpecsMonthly Web servers (2×)c6g.large2 vCPU, 4GB ARM each$99.28 Backend worker (1×)c6g.2xlarge8 vCPU, 16GB ARM$198.56 DatabaseRDS db.m6g.large, Single-AZ2 vCPU, 8GB$116.07 DB storage60GB gp3$4.80 EBS (web servers)2× 80GB gp3$12.80 EBS (worker)1× 280GB gp3$22.40 Load balancerALB + ~1-2 LCUs$25.00 Public IPv4 addresses3 × $0.005/hrSince Feb 2024$10.95 Data transfer~300GB egress$0.09/GB after 100GB free$18.00 NAT GatewayBest practice for private subnets$0.045/hr + $0.045/GB$46.00 Route 53 + CloudWatchDNS, alarms, logs$12.00 Total (realistic)$565.86
That's the on-demand price with no commitments. If you lock into a 1-year reserved instance plan, you can cut the compute portion by about 35–40%, bringing it down to roughly $400/month—but now you're committed for a year with no flexibility to resize or switch providers.
The real killer isn't the EC2 instances—it's everything else. Public IPv4 addresses cost money now (AWS started charging $0.005/hr per address in February 2024). NAT Gateways are practically mandatory if you follow AWS security best practices, and they're $46/month before you even process a single byte. Data transfer is $0.09/GB for everything over 100GB. EBS storage is separate from your instances. And you're still managing VPCs, security groups, IAM roles, and Terraform configs.
Compare that to Linode: $358/month, flat, all-in. 16TB of pooled bandwidth included. No per-IP charges. No NAT Gateway tax. No EBS line items. No Terraform. Even AWS with a 1-year commitment can't match it.
Hetzner (EU): ~$34/month
Jaw-droppingly cheap. A full stack for the price of a nice lunch. But there's no managed PostgreSQL—you're the DBA. And the cheapest servers are EU-only, which means latency for US users. They also just announced a 30–37% price hike starting April 2026. Still incredible value if you have the ops capacity and European users.
Linode/Akamai: ~$358/month
The sweet spot. Managed Postgres with automated backups, dedicated CPU instances, flat pricing, 16TB of pooled bandwidth included. No surprise egress charges. No Terraform. This is what we chose.
Owned Hardware + Colocation: ~$130–175/month recurring
The DHH approach. 37signals saved millions by buying Dell servers and colocating them. A refurbished Dell R640 with 24 cores and 128GB RAM goes for $700–$1,200 on eBay. Colocation runs $79–$150/month. The math works, but you're the DBA, the sysadmin, and the on-call engineer. We're keeping this in our back pocket for when we're running 50+ app backends on shared infrastructure.

Why We Chose Linode

Three reasons: managed Postgres, predictable pricing, and operational simplicity.
We're a small team. Every hour spent managing database backups or debugging cloud networking is an hour not spent building features or growing revenue. Linode gives us a managed database with automated daily backups, dedicated CPU instances where performance doesn't depend on what our neighbors are doing, and enough bandwidth that we'll never see a surprise bill.
And since we deploy with Kamal, switching providers is a kamal deploy command pointed at different IP addresses. If Linode ever stops making sense, we leave. No lock-in.

The Exact Stack

Here's what we're running, with real prices:
Web Servers: 2× Linode Dedicated 4 GB — $136/month
Two dedicated CPU instances, each with 2 vCPUs and 4GB RAM. These run our Rails app via Kamal, serving API requests and content feeds to 80+ news apps. We went with dedicated over shared CPU because 80 apps creating overlapping, consistent request load isn't the bursty pattern where shared shines. The NodeBalancer distributes traffic between them, and if one goes down, the other handles everything while we spin up a replacement.
Backend Worker: 1× Linode Dedicated 16 GB — $144/month
This is the workhorse. 8 dedicated CPUs and 16GB RAM running two things: Solid Queue processing article ingestion, push notifications, and background jobs—plus a local Ollama instance running a quantized LLM for article classification and clustering.
Why dedicated and why 16GB? Running a local LLM is sustained, heavy CPU work. On shared instances, you'd get throttled because you're the noisy neighbor everyone hates. And a quantized 8B parameter model eats about 5–6GB of RAM, leaving ~10GB for Ruby processes and the OS. We're starting with one worker and adding a second when we see queue latency creep up.
Database: Linode Managed PostgreSQL, Dedicated 4 GB, 1 Node — $68/month
PostgreSQL v18, 2 CPUs, 4GB RAM, 58GB storage. Our workload is read-heavy—80+ apps reading articles, with batch writes from daily background jobs pulling in new content. That's the ideal Postgres pattern. The 4GB of RAM gives Postgres enough shared_buffers to keep recent articles cached in memory, so most reads never hit disk.
We're on a single node, not the recommended 3-node HA cluster. Why? The HA cluster is 3× the price ($204/month vs $68/month), and at our stage, a few minutes of database recovery time during a rare node failure is acceptable. The managed service includes automated daily backups regardless, so our data is safe. We'll upgrade to 3 nodes when downtime has a real dollar cost.
Load Balancer: Linode NodeBalancer — $10/month
HTTP-level load balancing with instant health-check failover between our two web servers. SSL termination included.
Edge Layer: Cloudflare Free Tier — $0/month
We layer Cloudflare in front of everything. All domains point to Cloudflare, which proxies to our NodeBalancer. We get global CDN caching for article images and static assets, DDoS protection, and SSL at the edge—all on the free tier. The NodeBalancer handles the actual HTTP distribution between servers, while Cloudflare handles everything before the request reaches our infrastructure. This replaces the $10,900/year we were paying Ray Networks for CDN services on the legacy stack.
Total: $358/month. $4,296/year.
Down from $1,925/month on the legacy stack. That's an 81% reduction—$18,804 in annual savings—while gaining managed backups, modern hardware, a local LLM for content intelligence, and a deployment pipeline that takes minutes instead of hours.

The Full Comparison

Here's every option side by side, with real numbers:
StackMonthlyAnnualLock-in Legacy (Hivelocity + Ray Networks)$1,925$23,100Monthly AWS On-Demand (realistic)$565$6,780None AmericanCloud + Cloudflare$474$5,688None AWS 1-Year Reserved~$400~$4,8001 year Linode + Cloudflare$358$4,296None Hetzner (EU only)$34$408None Colocation (after hardware)$130–175$1,560–2,100Hardware purchase
Linode beats AWS on-demand by $207/month—$2,484/year—with zero lock-in. It even beats AWS with a 1-year reserved commitment, which requires paying upfront for the privilege of being locked to a specific instance family in a specific region. And Linode includes 16TB of pooled bandwidth where AWS charges $0.09/GB for everything over 100GB.

How Far Can We Scale Before Hitting the Legacy Budget?

Since we're rebuilding in Rails and don't yet know exactly how the new app scales compared to the old Java algorithms, I wanted to see how much infrastructure we can throw at this before we even approach the old $1,925/month budget.
For context, the legacy stack had 84 Sandy Bridge cores across six servers with 280GB of RAM. But Sandy Bridge cores from 2012 are roughly 3–4× slower per core than modern EPYC or Ice Lake processors. So those 84 legacy cores are equivalent to about 21–28 modern cores in real-world throughput.
Here's every scaling tier on Linode, all under the legacy budget:
TierWebWorkersDatabaseMonthlyCPUsRAMvs Legacy 1 — Base2× Ded 4GB1× Ded 16GB4GB, 1 node$3581224GB81% savings 2 — Add worker2× Ded 4GB2× Ded 16GB4GB, 1 node$5022040GB74% savings 3 — Bigger web2× Ded 8GB2× Ded 16GB4GB, 1 node$6382448GB67% savings 4 — 3rd web + bigger DB3× Ded 8GB2× Ded 16GB8GB, 1 node$8422864GB56% savings 5 — 3rd worker3× Ded 8GB3× Ded 16GB8GB, 1 node$9863680GB49% savings 6 — HA database4× Ded 8GB3× Ded 16GB8GB, 3 nodes$1,3944096GB28% savings 7 — Add GPU4× Ded 8GB2× Ded 16GB + 1× GPU Med8GB, 3 nodes$1,69636 + GPU88GB12% savings 8 — Max under $2K4× Ded 8GB3× Ded 16GB + 1× GPU Med8GB, 1 node$1,56848 + GPU112GB19% savings
All tiers include NodeBalancer ($10) and Cloudflare free tier. CPUs are modern dedicated cores—each one roughly 3–4× faster than the Sandy Bridge cores in our legacy stack.
The takeaway: at Tier 5, we'd have 36 modern dedicated cores, 80GB of RAM, and six servers—for under $1,000/month, half the legacy budget. That's more real-world compute than all 84 Sandy Bridge cores combined. Even fully maxed out at Tier 8 with a GPU instance, four web servers, three CPU workers, and managed Postgres, we're still $357/month under the legacy spend with dramatically more capacity.
We have a lot of room to grow before this new stack costs what the old one did.

What About GPUs?

Linode offers RTX 4000 Ada GPU instances starting at $350/month. A single GPU does LLM inference 5–10× faster than our 8-core CPU setup. At our current volume, CPU inference handles the load fine—a few thousand articles per day processes in hours, not minutes, and that's acceptable when it runs in the background overnight.
When we cross 5,000+ articles per day and the queues start backing up, we'll evaluate moving the LLM workload to a GPU instance. At $350–$446/month for a single GPU box that replaces two $144 CPU workers, the economics get interesting at scale. For now, CPU keeps things simple.

The Colocation Card We're Holding

I'm a huge fan of what DHH did at 37signals—spending $600K on Dell servers to save $2M/year in cloud costs. The math is compelling: a refurbished Dell R640 with 24 cores and 128GB RAM costs $700–$1,200, and colocation runs $79–$150/month including power and unmetered bandwidth.
At our current scale, the absolute dollar savings between colocation and Linode are modest—maybe $800/year. That's not transformative. But when we're consolidating 50+ app backends onto shared infrastructure and the monthly cloud bill is $500–$700+, the math tilts dramatically. A single colocated server would have 6× the compute of our entire current cloud setup, and the 3-year total cost would be lower.
We're not there yet. But Kamal makes this migration trivial when the time comes. Same deployment commands, different IP addresses.

The Bottom Line

The architecture is simple on purpose. Two web servers behind a load balancer. One worker server doing background processing. One managed database. Cloudflare in front. If something breaks at 2am, there's only four things it could be.
Kamal + Linode + Cloudflare. $358/month. 80+ apps. Zero lock-in.
Ship your product, not your infrastructure.
Still getting AWS bills with line items you can barely explain? Email me at jonathan@rubygrowthlabs.com — put "Stack Review" in the subject, tell me what you're running, and I'll tell you exactly what I'd change.