Impact / Economics

Drupflare vs a Traditional VPS

Latency, throughput and cost against nginx with PHP-FPM on the same Drupal tree and database.

Drupflare targets solo, enterprise, indie and budget-bound sites, where the cost is the hours and not the hosting bill.

Drupflare is faster than a single-region VPS for any visitor who is not sitting next to it, and the margin grows with distance. Traffic-weighted across the workload mix, against nginx and PHP-FPM 8.5 serving the same Drupal tree and the same database:

the visitor is ratio
in the VPS's own datacenter 1.50x
on the same continent 6.35x
one ocean away 11.32x
on the far side of the world 23.09x

Same-datacenter is the VPS at its theoretical best and nobody's real audience. The rest is Geography. Where Drupflare is slower is under Limitations, and a raw uncached render is one of them.

Every Drupflare figure marked M is measured, on deployed Cloudflare infrastructure or on this machine, and the column says which. D is derived from measured inputs and is arithmetic, not a reading. L is a vendor's published list price. n/m is not measured, and is stated as a range or omitted.

Against a VPS

docker/vps.yml runs nginx 1.29 and PHP 8.5 FPM with opcache and tracing JIT against the same Drupal tree and the same site database Drupflare serves, so the runtime is the only variable. bun run measure:vps drives both. The generator's own ceiling on the same machine is 8,308 req/s against nginx and 1,231 against the front worker, so it constrains neither arm.

workload VPS Drupflare
Anonymous cached, p50 3 ms 2 ms
Anonymous cached, p95 49 ms 4 ms
Anonymous cached, 32 clients 122 req/s, p50 10 ms 438 req/s, 62 ms
Re-render, tag-invalidated 24 ms 30 ms
Re-render, page cache hit 24 ms 22 ms

An authenticated session is a curve and not a median: the first three requests render and compile a plan, and everything after is answered in the isolate with no Durable Object hop. Converged p50 for one session driven sequentially, n=14 per path:

authenticated path VPS Drupflare
/ 11 ms 5 ms
/admin/content 71 ms 5 ms
/user/1 56 ms 5 ms
/admin/structure/types 18 ms 6 ms

Under concurrency Drupflare holds about 312 req/s flat from 4 to 32 clients on both authenticated workloads. The VPS peaks at 204 req/s on the front page and 67 on admin, then declines.

Both arms are on localhost, which is the VPS's best case. A VPS sits in one region; Drupflare answers from the visitor's own colo. The network term a real user pays on every VPS request is absent from the tables above. Geography measures it, and it is the largest term in the comparison.

A re-render is 1.21x on the pages a save invalidates, and Drupflare is faster on the rest. A save purges only the cache entries matching its tags, so every other page re-renders from a warm dynamic page cache. The larger ratios elsewhere in this file are a warm-kernel interpreter comparison and a both-bins-emptied edge render, which are different workloads on different instruments and do not divide into each other. The authenticated rows are not a runtime ratio either: they are a hop that does not happen.

The authenticated arm is one Durable Object with no replica lanes. A four-lane pool measured 3.29x on real authenticated renders and none of it is in the numbers above. Bodies agree to within 10 bytes on the anonymous arms; on the authenticated arms Drupflare serves the larger page (103,689 against 96,177 on the front page) and the difference is unexplained.

Drupflare Traditional VPS prov.
Setup, from zero to a rendering site one deploy, no server provision, webserver, PHP-FPM, MariaDB, TLS, cron, firewall, backups n/m
Local toolchain you must learn first none for deploy; bun to develop ddev or Lando, Composer, Drush, ssh, a database client n/m
Monthly infrastructure itemised in The Cost Model the same table L
What "scaling up" means nothing to do; the edge absorbs it resize the box, tune PHP-FPM workers, add a cache layer n/m
OS patching, PHP upgrades, kernel CVEs none exist to patch yours, forever n/m
Anonymous page served from edge cache, ~85% of traffic, 0 Durable Object requests your box, every request M edge
page_cache hit 1 ms of Durable Object CPU, 1 statement full LEMP round trip M edge
dynamic_page_cache hit a handful of ms and 6 statements n/m M edge
Full uncached render seconds of Durable Object CPU with both cache bins emptied; the technical report carries the figure tens of ms native PHP, warm kernel M edge
Wasm penalty, warm kernel only a same-machine interpreter ratio, not the edge cost above; the technical report carries it 1x by definition M local
Authenticated page, plan hit answered in the front worker with no Durable Object hop; see the curve above tens of ms on a warm PHP-FPM pool M edge
Authenticated read concurrency scales with the replica pool: 1.00 / 2.03 / 3.29x at 1 / 2 / 4 lanes, on real renders one box, until you resize it M edge
Cold start over a second; paid absorbs it and free amortises it off the request path ~0; the box is already running M edge
Free-plan capacity ~100,000 page views/day (~3M/month), saturated; every visit costs one Worker request, cached or not; static assets do not bill; with Workers Cache on, which the shipped config does not enable, 1,515 views/day whatever the box does before it swaps M edge
Worker bundle fits the 64 MiB Worker size limit n/a M local
First-run migration chunked, one per invocation on free, each sized to complete on its own drush si, then hope M local
When something breaks at 3am a self-repair ladder runs: L0 observe, L1 reset, L2 reconstruct, L3 reconfigure, L4 quarantine, L5 rollback you, or someone you pay M built
Failure detection 19 tripwires (12 host, 7 PHP) plus a mandatory boot self-test uptime ping, if configured M built

The Cost Model

The infrastructure saving is small. The labour saving is the large one.

Infrastructure, for one small content site. Vendor figures are list prices and are marked so. For the same comparison across traffic levels, see Prices at each traffic level.

line item Drupflare free Drupflare paid small VPS prov.
Compute $0.00 $5.00 Workers Paid $10-12 for 1-2 vCPU and 2 GiB L
Database $0.00 (the object's own SQLite) included on the box, or $15+ managed L
Backups $0.00 included in the plan's storage $0.50-2.50, snapshots or a plan surcharge L
CDN included included $0 with Cloudflare in front, which most do L
TLS included included $0 via Let's Encrypt, yours to renew L
Monthly total $0.00 ~$5.00 ~$10.50-14.50
Annual $0 ~$60 ~$126-174

Paid against a VPS is roughly $66-114 a year. The VPS is the smallest box that meets Drupal's documented memory minimum, priced from provider list prices in the impact report. The free column has no VPS equivalent: there is no $0 VPS, and free carries about 3.0M visits a month with the same feature set as paid. Shell assembly is on for both plans, and warming is on by default only on paid (Free vs Paid). At fleet scale the infrastructure works out near $0.028 per site per month.

The column that cannot be filled in is the difference:

recurring obligation Drupflare VPS
OS and kernel CVEs nothing to patch monthly, forever
PHP minor and major upgrades a binary swap, built in CI yours, with a compatibility risk each time
Webserver and PHP-FPM tuning does not exist yours
Drupal security releases detected on the cron chain, reported by /health yours, on the advisory's schedule
Backup verification export and restore chunked, resumable, rehearsed yours to configure, and to test
Uptime and alerting 19 tripwires plus a self-repair ladder uptime ping, if configured
Traffic spike nothing to do resize, tune, add caching

Price those at your own rate; this project states no hourly figure.

Performance Summary

Full provenance for each figure is in the report's executive summary.

Drupflare native PHP on a VPS prov.
Cached page (the ~99% case) 1 ms DO CPU, 1 statement full LEMP round trip M edge
Re-render, tag-invalidated 30 ms 24 ms M local
Re-render, page cache hit 22 ms 24 ms M local
Uncached render, cold bins 2,127 ms DO CPU (n=10); a different cache state n/m on this instrument M edge
Wasm penalty, warm kernel 3.57x warm / 3.94x cold, same-machine ratio 1x by definition M local
Isolate startup 5 ms, not billed to the request n/a; the box is running M edge
Cold boot 1,264 ms, amortised off the request path ~0 M edge
Authenticated page 208 ms p50 from a client, both levers on tens of ms, warm pool M edge

The architecture wins by not rendering. The tiers above answer without a render, and where a logged-in visitor forces one, shell assembly cuts the render and not the boot.

The two render rows are different workloads on different instruments and must not be divided. The 30/24 pair is one machine and one instrument, with Drupal's render and dynamic_page_cache bins warm on both sides, which is the state a content change produces. The 2,127 ms is the edge billing a render with both bins emptied. Dividing an edge cold figure by a local warm one is how an external review published a 225x that this project had to retract; the technical report keeps that correction at its Provenance section.

Authenticated traffic is the harder case. Shell assembly and a resident interpreter put a logged-in page at about 467 ms against 3,391, and a replica pool spreads authenticated reads across objects instead of queueing them behind one. A VPS with a warm PHP-FPM pool is still ahead on a single request. Drupflare answers the concurrency question with lanes: adding one is a config change and a provisioning call, not a resize (replica settings).

Write-heavy traffic is a narrow loss. A lane runs a write locally, discards its own effect and forwards the statement list to the primary, which sequences it, so the work spreads and only the commit serialises. The class of write that originates a value two objects would mint differently cannot spread; those are refused, because a retry cannot repair them.

Geography

Every table above gives the VPS a visitor standing in its own datacenter. A real visitor is somewhere else, and a single-region VPS answers all of them from one place while Cloudflare answers from the colo nearest each. That difference is the largest term in the comparison. It is measured: scripts/measure/delay-proxy.mjs puts a real delaying proxy in front of the VPS arm, so its connection pays the network instead of having a number added afterwards.

The injected delays are Azure's published P50 round-trip figures between regions. From US-East: West US 69 ms, West Europe 83 ms, Brazil 117 ms, India 198 ms, Australia 201 ms, Southeast Asia 224 ms.

injected round trip VPS weighted p50 Drupflare weighted p50 ratio
0 ms, same datacenter 14.8 ms 9.9 ms 1.50x
40 ms, same continent 56.5 ms 8.9 ms 6.35x
82 ms, transatlantic 100.2 ms 8.9 ms 11.32x
200 ms, antipodal 218.0 ms 9.4 ms 23.09x

Traffic-weighted across the workload mix, three replica lanes, viable: true with zero regressions on all three network arms. Drupflare's figure barely moves, because the term being added is one a single-region VPS pays and an edge network does not.

Cloudflare Containers accept a placement constraint, so the same comparison runs on real infrastructure. The VPS arm was pinned first to western Europe and then to eastern North America, same image and same instance type, driven from a US-East client, n=15 interleaved with the arm order alternating:

VPS placement VPS floor Drupflare floor
Western Europe 174.3 ms 82.6 ms
Eastern North America 111.7 ms 78.0 ms

These are minima and not medians, because the client's own round trip is in both and the floor is where it shows. Moving the container across the Atlantic costs 62.6 ms on an otherwise identical arm, and a same-continent VPS still sits about 34 ms above Drupflare, because a region is not the visitor's own colo. Subtracting the shared client round trip leaves figures that agree with the proxy table above, so two instruments reach the same answer.

The advantage is zero for a visitor in the VPS's own city and largest for one on the other side of the world, so a site whose audience sits beside its VPS gains nothing here. There is no single worldwide number to quote in place of the rows above: internet penetration is published by region, users per region are not, and averaging without that table would invent a figure. The proxy also understates the gap, since it delays data and not the TCP handshake, and Drupflare's own arm stays on localhost.