Kayaking Geek is one of our own sites — trip reports, gear reviews, and route notes for paddlers. It's low traffic by design, which made it a good candidate for a question worth answering for any small or low-traffic client site: how cheap can WordPress hosting actually get if you're willing to rebuild the parts of it that assume an always-on server?

Request path — no NAT Gateway, no database server Visitor browser request API Gateway HTTP API Lambda container WordPress · PHP-FPM (Bref) reserved concurrency: 1 EFS SQLite + uploads ✕ no NAT Gateway — no outbound internet
Kayaking Geek's full request path: API Gateway routes straight to a single WordPress Lambda, which reads and writes SQLite and media on EFS. There's no NAT Gateway and no database server running — which is most of why the hosting bill is $0.03/month.

Why "serverless WordPress" usually isn't actually cheap

Most write-ups on running WordPress on Lambda still wire up an RDS or Aurora Serverless database behind it, plus a NAT Gateway so the Lambda function (sitting in a VPC to reach that database) can still reach the internet. Both of those are metered, always-on-ish costs — a NAT Gateway alone runs roughly $32/month before any data transfer, and that's before the database. You've swapped an EC2 instance for Lambda, but the bill barely moves.

For Kayaking Geek, we cut both of those out entirely.

No database server: SQLite over EFS

WordPress's database layer gets swapped out using the official SQLite Database Integration plugin (a wp-content/db.php drop-in), so there's no MySQL, no RDS, no Aurora Serverless. The .sqlite file and the wp-content/uploads directory both live on an EFS filesystem, mounted into the Lambda function at /mnt/wp-content — that's what gives WordPress the persistent disk it expects, without running a persistent server.

The trade-off: SQLite can't safely handle concurrent writers. Reserved concurrency on the function is capped at 1, so a request that would otherwise run in parallel queues for a moment instead. For a low-traffic site, that's not a real cost — nobody notices a queue of one.

No NAT Gateway: the function has no internet access, on purpose

The Lambda function sits in a private VPC purely so it can reach the EFS mount targets — it has no route out to the public internet at all, which is exactly what a NAT Gateway would otherwise be there to provide. The trade-off is that WordPress can't phone out: plugin and theme update checks, Gravatar, Akismet, all silently fail. Since the container image gets rebuilt and redeployed for every real change anyway (core, plugins, and themes are pinned in composer.json, not installed from wp-admin), that trade-off doesn't actually cost anything here. DISALLOW_FILE_MODS is set for the same reason — the image filesystem is read-only at runtime outside the EFS mount, so a wp-admin plugin install would just fail anyway.

That "no outbound internet, ever" constraint isn't just theoretical — it's the reason the site's Garmin activity sync runs from GitHub Actions instead of from the site itself. The Lambda function can't call an external API on its own, so the outbound leg happens elsewhere and only calls back into the site's own REST API — a request the function can always answer, regardless of what it's blocked from initiating.

One container image, no CloudFront, no Function URL

The whole site is a single Lambda function, packaged as a container image running PHP-FPM via Bref. The entry point serves real static files directly — theme CSS/JS, wp-includes assets, EFS-backed media uploads — and only falls through to WordPress's own routing for actual WordPress requests. There's no separate CDN or static-asset bucket to run.

An API Gateway HTTP API sits in front of the function instead of a Lambda Function URL or CloudFront — both of those hit an account-level verification gate that returns a bare 403. API Gateway isn't affected, and its own endpoint is the real public URL until (or unless) a custom domain gets wired up later.

The bill

Month to date: three cents. Not "three cents plus a database tier" — three cents, total. At this traffic level, the cost basically is Lambda's per-request pricing plus a sliver of EFS storage; there's no fixed monthly floor left to pay, because nothing in the stack is provisioned to sit idle and billing anyway.

Where this pattern fits — and where it doesn't

This isn't a general-purpose WordPress hosting recipe. It's a good fit when traffic is low and spiky, updates go through a deploy pipeline rather than wp-admin, and "the site can't phone out or take concurrent writes" is an acceptable trade for "the site costs almost nothing when nobody's visiting." It's a bad fit for a high-traffic site, a store taking simultaneous orders, or a team that wants to install plugins from the WordPress dashboard.

Where it does fit — marketing sites, blogs, brochure sites, low-traffic client sites that still need to look and behave like real WordPress — it's a legitimate way to take the "hosting" line item down to essentially nothing. The pattern even extends to a real WooCommerce store taking Stripe payments — see how checkout works without a NAT Gateway — as long as the one piece that genuinely needs outbound internet gets carved out on its own. If you're paying for always-on WordPress hosting for a site that gets a trickle of traffic, tell us about it and we can tell you honestly whether this pattern would work for your case.

Joseph Rounds

Founder, Lighthouse Consulting

25+ years building enterprise software at McKesson (Fortune 10), Doctor On Demand, and IntelyCare. Now helping Boston-area businesses design and build custom software, AWS infrastructure, and AI integrations that fit how they actually operate.