← Back to list

The $250 WordPress Mistake: A Postmortem on a Slow AWS Architecture

Read Here For Free

Vikas Arora in AWS in Plain English · 2025-11-19 11:34 · 3 claps · 7.0 min read paywalled
#aws #wordpress #cloud-architecture #slow-wordpress-site #kinsta
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 📰 · Journalism & News 🏛️ · Architecture

The $250 WordPress Mistake: A Postmortem on a Slow AWS Architecture

***Read Here For Free***

I’m an architect. I design resilient, secure, and scalable systems on AWS. It’s what I do. So, when a client needed a new WordPress site, I built them the “right” solution.

It was a text-book, best-practice architecture. Secure, redundant, and built to scale. It also cost around $300 a month.

And it was slow. Humiliatingly slow.

The developer deployed the site, and the Time to First Byte (TTFB) was agonizing. We threw a caching plugin at it (WP Super Cache), and it was still sluggish.

Frustrated, the client moved the exact same site to a “managed WordPress host (Kinsta)” as a test. It took the developer 15 minutes. The site was instantly, blazingly fast. And the plan cost $50.

I was baffled. And humbled. How did a “proper” $300 stack get so thoroughly beaten by a standard subscription plan?

This post is the autopsy of that $300 architecture and the blueprint for how to actually build a high-performance WordPress stack on AWS.

It’s a lesson in caching, instance types, and the massive, invisible cost of expert labor.

The “Perfect” $300 AWS Stack That Failed

First, let’s look at what I built. If you’re an AWS pro, this will look familiar.

  • Compute: An Auto Scaling Group (ASG) with a minimum of two t3a.medium EC2 instances. This ensures high availability.
  • Database: An RDS instance, specifically a db.t4g.small, in a separate private subnet.
  • Networking: An Application Load Balancer (ALB) to distribute traffic.
  • Security: A NAT Gateway, allowing the private EC2 instances to fetch updates (like WordPress plugin downloads) without being exposed to the internet.
  • Caching: The developer added the “WP Super Cache” plugin inside WordPress.

It was secure, it was redundant, it followed all the AWS Well-Architected guidelines. And it failed. Here’s why:

The Autopsy: Where It All Went Wrong

My architecture was solving for infrastructure resilience, not application performance.

For WordPress, those are two very different things.

1. The “Burstable” T-Series Trap

This was the single biggest flaw. The entire stack, from the t3a.medium web servers to the db.t4g.small database, was built on AWS’s "burstable" T-series instances.

Here’s the rub: T-series instances are cheap because they give you a low “baseline” CPU performance (e.g., 20–40%) and a finite number of “CPU credits” to “burst” above that baseline.

I assumed WordPress was a “low-traffic” workload. I was wrong.

WordPress is constantly busy. Even with no visitors, it’s running background cron jobs, getting pinged by search engine crawlers, and processing admin tasks. These sustained loads exhausted our CPU credits fast.

Once the credits are gone, the instances are “throttled” down to their low baseline performance. That was the source of the terrible slowness.

Worse, the $300/month bill was likely a penalty. By default, T-series instances are in “Unlimited Mode,” which means when you run out of credits, AWS just sells you more at a premium. I was paying a premium price for throttled hardware.

2. The Caching Misunderstanding (PHP vs. Server)

I thought “WP Super Cache” would solve it. But a PHP-based caching plugin is dramatically slower than a true server-level cache.

Why? Because every single request — even for a “cached” page — still has to load the Nginx/Apache web server, which then has to load the entire PHP interpreter, which then loads WordPress, which then loads the caching plugin, just to fetch a static HTML file from the disk.

The managed host, by contrast, uses Nginx fastcgi_cache. This intercepts the request at the webserver before PHP ever loads. It serves the cached file directly from memory/disk, which is orders of magnitude faster.

3. The “Best Practice” Performance Tax

Two of my “best practice” security decisions actively hurt performance:

  1. NAT Gateway: For security, my instances were in a private subnet. To get out to the internet (for plugin updates, etc.), they had to go through a NAT Gateway. This service costs a fixed ~$33/month plus a per-GB data processing fee. This was pure cost overhead that provided zero performance value.
  2. RDS Latency: Separating the database to RDS is great for security and management. But for WordPress, which is notoriously “chatty” and can make 100+ database queries on a single uncached page load, I introduced network latency on every single query. The managed host typically runs containerized solutions where the database proximity is highly optimized.

4. The Iceberg: Total Cost of Ownership (TCO)

This was the real “Aha!” moment.

I was comparing a $300 infrastructure bill to a $50 service bill.

The $50/month Kinsta plan wasn’t just for a server. It was for the server plus the 24/7/365 amortized salary of an entire team of expert WordPress sysadmins. Their full-time job is to tune Nginx, optimize databases, manage server-level caching, and secure the platform.

My $300 AWS stack had $0 budgeted for that expert, application-specific labor. I had handed the client a box of high-end car parts, but the $50 host was selling them a fully-tuned “Formula 1” car with a pit crew.

The Right WordPress Deployment for the Right Situation

This experience didn’t mean “AWS is bad.” It meant I used the wrong tool for the job.

Here’s my new playbook.

Profile 1: The 95% of Users (Businesses, Blogs, E-commerce)

Recommendation: A Premium Managed WordPress Host (e.g., Kinsta, WP Engine).

Why: It’s faster, cheaper, and safer for almost everyone. You aren’t just buying a server; you’re buying a fully managed platform. They provide superior, sustained-performance compute (like Google’s C2/C3D VMs), premium networks, and a pre-tuned stack (Nginx fastcgi_cache, Edge Caching, optimized DBs) out of the box. You are outsourcing the expert labor to someone who does it better and cheaper than you ever could.

Profile 2: The Enterprise Integrator or High-Compliance Client

Recommendation: The “DIY” AWS Stack (but built correctly).

Why: Use this only if you have a non-negotiable reason.

  1. Deep Integration: You need WordPress to live in a private VPC and talk to other AWS services like Lambda, S3, or internal APIs that aren’t public.
  2. Extreme Compliance: You are in healthcare and need a HIPAA-compliant host that will sign a Business Associate Agreement (BAA), which many managed hosts will not do on standard plans.

But here is the critical part: You must sell this with a mandatory, high-value Managed Services contract. You are now the expert sysadmin, and that labor must be budgeted.

A Note on AWS Lightsail: The Worst of Both Worlds

You might think AWS Lightsail is the happy medium. It’s not. Lightsail is just a “leaky abstraction” over EC2. It still uses burstable T-series instances, so you’ll have the exact same CPU credit/throttling problem. And you are still 100% responsible for all the sysadmin work (security, patches, tuning). It combines the performance limitations of a cheap VPS with the management burden of IaaS.

The Blueprint: A Truly High-Performance WordPress AWS Stack

So, if you must build on AWS for that “Enterprise” client, here is the blueprint that actually works.

1. The Caching Hierarchy (Nginx + Redis)

This is the most important concept. It’s not “Nginx or Redis.” You need both.

  • Layer 1: Server-Level Page Cache (Nginx fastcgi_cache) This is your frontline. It stores the fully-rendered HTML of your pages in memory or on disk. This cache serves 90% of your (anonymous) traffic without ever touching PHP or WordPress, making it incredibly fast. Use the Nginx Helper plugin to automatically purge this cache when you update a post.
  • Layer 2: Object Cache (AWS ElastiCache for Redis) This is for everything that can’t be page-cached: wp-admin, logged-in users, and e-commerce carts. WordPress is "chatty," constantly asking the database for the same data (like site options). An object cache sits between PHP and your RDS database, storing the results of those queries in high-speed RAM.

2. The Compute Tier (EC2): No More T-Series

  • Instance Type: Use a C-Series (Compute Optimized) family, like the c7g.large (Graviton/ARM).
  • Why: The web server’s job is running Nginx and PHP. This is a CPU-bound workload. C-series instances give you high, sustained CPU performance. Never use T-series for a production workload you expect to be fast.

3. The Database Tier (RDS): Feed It RAM

  • Instance Type: Use an R-Series (Memory Optimized) family, like the db.r7g.large.
  • Why: MySQL performance relies on RAM (specifically the innodb_buffer_pool). An R-series instance gives you a massive amount of RAM, allowing the entire database to live in memory, which all but eliminates disk I/O latency.

4. The Auto-Scaling Problem: Stateless WordPress

  • The Challenge: WordPress stores user uploads locally. In an Auto Scaling Group, instances are destroyed and recreated, meaning you lose those files.
  • The Solution: Use a plugin like WP Offload Media. This plugin intercepts all media uploads and saves them directly to an S3 bucket. Your EC2 instances are now truly “stateless” (disposable), and your uploads are stored securely and cheaply on S3. Do not try to mount EFS to /var/www/html; the latency will kill your site speed unless you are an expert at tuning PHP OPcache.

5. The CDN (CloudFront): Caching HTML

Set up CloudFront with two behaviors:

  • Static Assets: Pull images/CSS directly from your S3 bucket.
  • Dynamic HTML: Configure CloudFront to cache the HTML response from your Load Balancer, but bypass the cache if the user has a wordpress_logged_in cookie. This gives you global "Edge Caching" for anonymous visitors.

6. The Engine Room (PHP-FPM Tuning)

  • Setting: In your PHP-FPM pool config, set pm = static.
  • Why: The default, pm = dynamic, tries to save RAM by killing and spawning "worker" processes as needed. This adds latency. pm = static forces a fixed number of workers (e.g., 30) to be always on and waiting. It provides the absolute lowest latency because there is zero process-management overhead.

The Final Lesson

The $300 vs. $50 debate was never about the price of an instance. It was about the invisible, non-negotiable cost of expert, application-specific labor.

Managed hosts productize this labor. Building on IaaS like AWS means you are now that labor. My initial $300 stack failed because it was just a box of parts with no expert mechanic.

You can absolutely build a world-class WordPress site on AWS, but it requires a different architecture, a different budget, and a deep understanding of what you’re actually optimizing for. Otherwise, just save yourself the headache and pay the $50. Your client (and your ego) will thank you.


메타데이터
post_id
9bc951a26f44
slug
the-250-wordpress-mistake-a-postmortem-on-a-slow-aws-architecture-9bc951a26f44
url
https://aws.plainenglish.io/the-250-wordpress-mistake-a-postmortem-on-a-slow-aws-architecture-9bc951a26f44
canonical_url
https://aws.plainenglish.io/the-250-wordpress-mistake-a-postmortem-on-a-slow-aws-architecture-9bc951a26f44
author_url
https://medium.com/@devopsulting.com
status
ok
fetched_at
2026-07-15 06:19:17