Monitoring latency: Cloudflare Workers vs Fly vs Koyeb vs Railway vs Render | openstatus

Monitoring latency: Cloudflare Workers vs Fly vs Koyeb vs Railway vs Render

Feb 19, 2024 | by Thibault Le Ouay Ducasse | [education]

⚠️ We are using the default settings for each provider and conducting datacenter to datacenter requests. A real-world application's results are going to be different. ⚠️

You want to know which cloud providers offer the lowest latency?

In this post, I compare the latency of Cloudflare Workers, Fly, Koyeb, Railway and Render using openstatus.

I deployed the application on the cheapest or free tier offered by each provider.

For this test, I used a basic Hono server that returns a simple text response.

const app = new Hono();
app.use("*", logger());

app.use("*", poweredBy());

app.get("/", (c) => {
  return c.text(
    "Just return the desired http status code, e.g. /404 🤯 \nhttps://www.openstatus.dev",
  );
});

You can find the code in the status-code repository, it’s open source 😉.

Openstatus monitored our endpoint every 10 minutes from 6 locations located in Amsterdam, Ashburn, Hong Kong, Johannesburg, Sao Paulo and Sydney.

It's a good way to test our own product and improve it.

Let's analyze the data from the past two weeks.

Cloudflare workers

Cloudflare Workers is a serverless platform by Cloudflare. It lets you build new applications using JavaScript/Typescript. You can deploy up to 100 worker scripts for free, running on more than 275 network locations.

Latency metrics

100% UPTIME

0# FAILS

10,956# PINGS

182 ms AVG

138 ms P75

695 ms P90

778 ms P95

991 ms P99

Region Trend P75 P95 P99
🇳🇱 ams 60ms 88ms 169ms
🇺🇸 iad 120ms 139ms 177ms
🇭🇰 hkg 92ms 123ms 158ms
🇿🇦 jnb 705ms 748ms 1110ms
🇦🇺 syd 60ms 287ms 310ms
🇧🇷 gru 60ms 219ms 296ms

Cloudflare avg. latency between 04. Feb and 18. Feb 2024 aggregated in a 1h window.

Timing metrics

Region DNS (ms) Connection (ms) TLS Handshake (ms) TTFB (ms) Transfert (ms)
AMS 17 2 17 27 0
GRU 38 2 13 28 0
HKG 19 2 13 29 0
IAD 24 1 14 30 0
JNB 123 168 182 185 0
SYD 51 1 11 25 0

I can notice that Johannesburg's latency is about ten times higher than that of the other monitors.

Headers

From the Cloudflare request I can get the location of the workers that handle the request, with Cf-ray in the headers response.

Checker region Workers region number of request
HKG HKG 1831
SYD SYD 1831
AMS AMS 1831
IAD IAD 1831
GRU GRU 1791
GRU GIG 40
JNB AMS 741
JNB MUC 4
JNB HKG 5
JNB SIN 6
JNB NRT 8
JNB EWR 10
JNB CDG 82
JNB FRA 276
JNB LHR 699
JNB AMS 741

I can see all the request from JNB is never routed to a nearby data-center.

Apart from the strange routing error in Johannesburg, Cloudflare workers are fast worldwide.

I have not experienced any cold start issues.

Fly.io

Fly.io simplifies deploying and running server-side applications globally. Developers can deploy their applications near users worldwide for low latency and high performance. It uses a lightweight Firecracker VM to easily deploy Docker images.

Latency metrics

100% UPTIME

0# FAILS

10,952# PINGS

1,471 ms AVG

1,514 ms P75

1,555 ms P90

1,626 ms P95

2,547 ms P99

Region Trend P75 P95 P99
🇳🇱 ams 1562ms 1606ms 1654ms
🇺🇸 iad 1572ms 1627ms 1716ms
🇭🇰 hkg 1556ms 1624ms 1975ms
🇿🇦 jnb 1530ms 1591ms 1689ms
🇦🇺 syd 1514ms 2560ms 2641ms
🇧🇷 gru 1510ms 1567ms 1878ms

Fly avg. latency between 04. Feb and 18. Feb 2024 aggregated in a 1h window.

Timing metrics

Region DNS (ms) Connection (ms) TLS Handshake (ms) TTFB (ms) Transfert (ms)
AMS 6 1 8 1469 0
GRU 5 0 4 1431 0
HKG 4 0 5 1473 0
IAD 3 0 5 1470 0
JNB 24 0 5 1423 0
SYD 3 0 3 1489 0

The DNS is fast, our checker is attempting to connect to a region in the same data center, but our machine's cold start is slowing us down, leading to the high TTFB.

Here’s our config for Fly.io:

app = 'statuscode'
primary_region = 'ams'

[build]
  dockerfile = "./Dockerfile"

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[vm]]
  cpu_kind = 'shared'
  cpus = 1
  memory_mb = 256

The primary region of our server is Amsterdam, and the fly instances is getting paused after a period of inactivity.

The machine starts slowly, as indicated by the logs showing a start time of 1.513643778s.

2024-02-14T11:24:16.107 proxy[286560ea703108] ams [info] Starting machine

2024-02-14T11:24:16.322 app[286560ea703108] ams [info] [ 0.035736] PCI: Fatal: No config space access function found

2024-02-14T11:24:16.533 app[286560ea703108] ams [info] INFO Starting init (commit: bfa79be)...

2024-02-14T11:24:16.546 app[286560ea703108] ams [info] INFO Preparing to run: `/usr/local/bin/docker-entrypoint.sh bun start` as root

2024-02-14T11:24:16.558 app[286560ea703108] ams [info] INFO [fly api proxy] listening at /.fly/api

2024-02-14T11:24:16.565 app[286560ea703108] ams [info] 2024/02/14 11:24:16 listening on [fdaa:3:2ef:a7b:10c:3c9a:5b4:2]:22 (DNS: [fdaa::3]:53)

2024-02-14T11:24:16.611 app[286560ea703108] ams [info] $ bun src/index.ts

2024-02-14T11:24:16.618 runner[286560ea703108] ams [info] Machine started in 460ms

2024-02-14T11:24:17.621 proxy[286560ea703108] ams [info] machine started in 1.513643778s

2024-02-14T11:24:17.628 proxy[286560ea703108] ams [info] machine became reachable in 7.03669ms

If you update your fly.toml file to include the following, you can get the zero cold start and achieve a better latency.

  min_machines_running = 1

This is our data for our production server deploy on Fly.io.

100% UPTIME

0# FAILS

12,076# PINGS

61 ms AVG

67 ms P75

164 ms P90

198 ms P95

327 ms P99

We use Fly.io in production, and the machine never sleeps, yielding much better results.

Koyeb

Koyeb is a developer-friendly serverless platform that allows for global app deployment without the need for operations, servers, or infrastructure management. Koyeb offers a free Starter plan that includes one Web Service, one Database service. The platform focuses on ease of deployment and scalability for developers

Latency metrics

100% UPTIME

0# FAILS

10,955# PINGS

539 ms AVG

738 ms P75

881 ms P90

1,013 ms P95

1,525 ms P99

Region Trend P75 P95 P99
🇳🇱 ams 158ms 225ms 250ms
🇺🇸 iad 173ms 195ms 213ms
🇭🇰 hkg 372ms 395ms 416ms
🇿🇦 jnb 1532ms 1744ms 1812ms
🇦🇺 syd 825ms 869ms 915ms
🇧🇷 gru 705ms 815ms 989ms

Koyeb avg. latency between 04. Feb and 18. Feb 2024.

Timing metrics

Region DNS (ms) Connection (ms) TLS Handshake (ms) TTFB (ms) Transfert (ms)
AMS 50 2 17 107 0
GRU 139 65 75 407 0
HKG 48 2 13 321 0
IAD 35 1 12 129 0
JNB 298 1 11 720 0
SYD 97 1 10 711 0

Headers

The request headers show that none of our requests are cached. They contain cf-cache-status: dynamic. Cloudflare handles the Koyeb edge layer. https://www.koyeb.com/blog/building-a-multi-region-service-mesh-with-kuma-envoy-anycast-bgp-and-mtls

Our requests follow this route:

Cf workers -> koyeb Global load balancer -> koyeb backend

Let's see where did we hit the cf workers

Checker region Workers region number of request
AMS AMS 1866
GRU GRU 504
GRU IAD 38
GRU MIA 688
GRU EWR 337
GRU CIG 299
HKG HKG 1866
IAD IAD 1866
JNB JNB 1861
JNB AMS 1
SYD SYD 1866

Koyeb Global Load Balancer region we hit:

Checker region Koyeb Global Load Balancer number of request
AMS FRA1 1866
GRU WAS1 1866
HKG SIN1 1866
IAD WAS1 1866
JNB PAR1 4
JNB SIN1 1864
JNB FRA1 1
JNB SIN1 1866

I have deployed our app in the Frankfurt data-center.

Railway

Railway is a cloud platform designed for building, shipping, and monitoring applications without the need for Platform Engineers. It simplifies the application development process by offering seamless deployment and monitoring capabilities.

Latency metrics

99.991% UPTIME

1# FAILS

10,955# PINGS

381 ms AVG

469 ms P75

653 ms P90

661 ms P95

850 ms P99

Region Trend P75 P95 P99
🇳🇱 ams 181ms 295ms 316ms
🇺🇸 iad 79ms 107ms 131ms
🇭🇰 hkg 348ms 443ms 467ms
🇿🇦 jnb 660ms 786ms 893ms
🇦🇺 syd 532ms 620ms 728ms
🇧🇷 gru 417ms 468ms 541ms

Railway avg. latency between 04. Feb and 18. Feb 2024 aggregated in a 1h window.

Timing metrics

Region DNS (ms) Connection (ms) TLS Handshake (ms) TTFB (ms) Transfert (ms)
AMS 9 21 18 158 0
GRU 14 115 127 178 0
HKG 8 45 54 225 0
IAD 7 2 14 65 0
JNB 18 193 178 319 0
SYD 21 108 105 280 0

Headers

The headers don't provide any information.

Railway is using Google Cloud Platform. It’s the only service that does not allow us to pick a specific region on the free plan. Our test app will be located to us-west1 Portland, Oregon. We can see that the latency is the lowest in IAD.

By default our app did not scale down to 0. It was always running. We don't have any cold start.

Render

Render is a platform that simplifies deploying and scaling web applications and services. It offers features like automated SSL, automatic scaling, native support for popular frameworks, and one-click deployments from Git. The platform focuses on simplicity and developer productivity.

Latency metrics

99.89% UPTIME

12# FAILS

10,946# PINGS

451 ms AVG

447 ms P75

591 ms P90

707 ms P95

902 ms P99

Region Trend P75 P95 P99
🇳🇱 ams 95ms 127ms 16675ms
🇺🇸 iad 165ms 343ms 16552ms
🇭🇰 hkg 384ms 746ms 16681ms
🇿🇦 jnb 587ms 730ms 16295ms
🇦🇺 syd 433ms 945ms 16696ms
🇧🇷 gru 370ms 753ms 15979ms

Render avg. latency between 04. Feb and 18. Feb 2024 aggregated in a 1h window.

Timing metrics

Region DNS (ms) Connection (ms) TLS Handshake (ms) TTFB (ms) Transfert (ms)
AMS 20 2 7 107 0
GRU 61 2 6 407 0
HKG 76 2 6 321 0
IAD 15 1 5 129 0
JNB 36 161 167 720 0
SYD 103 1 4 711 0

Headers

The headers don't provide any information.

I have deployed our app in the Frankfurt data-center.

According to the Render docs, the free tier will shut down the service after 15 minutes of inactivity. However, our app is being accessed by a monitor every 10 minutes. We should never scale down to 0.

Render spins down a Free web service that goes 15 minutes without receiving inbound traffic. Render spins the service back up whenever it next receives a request to process.

Conclusion

Here are the results of our test:

Provider Uptime Fails Ping Total Pings AVG latency (ms) P75 (ms) P90 (ms) P95 (ms) P99 (ms)
CF Workers 100 0 10 956 182 138 690 778
Fly.io 100 0 10 952 1 471 1 514
Koyeb 100 0 10 955 536 738 881 1
Railway 99.991 1 10 955 381 469 653 661
Render 99.89 12 10 946 451 447 591 707

If you value low latency, Cloudflare Workers are the best option for fast global performance without cold start issues. They deploy your app worldwide efficiently.

For multi-region deployment, check out Koyeb and Fly.io.

For specific region deployment, Railway and Render are good choices.

Choosing a cloud provider involves considering not just latency but also user experience and pricing.

We use Fly.io in production and are satisfied with it.

Frequently asked questions

Which cloud provider has the lowest latency?

In our benchmark, Cloudflare Workers had the lowest average latency of the providers left on their default configuration: 182ms across 6 global regions, with a P75 of 138ms. Fly.io was faster still when kept warm — 61ms with min_machines_running=1 — but averaged 1,471ms once cold starts were allowed.

Does Fly.io have cold start issues?

Yes. With auto_stop_machines enabled and min_machines_running=0, Fly.io averaged 1,471ms due to cold starts (~1.5s machine boot time). Setting min_machines_running=1 eliminates cold starts and brings the average down to 61ms.

How does Cloudflare Workers latency compare to Railway and Render?

Cloudflare Workers averaged 182ms with 100% uptime. Railway averaged 381ms with 99.991% uptime (1 failure). Render averaged 451ms with 99.89% uptime (12 failures). Cloudflare Workers deploy globally to 275+ locations, while Railway and Render run from a single region.

Which cloud provider had the most downtime in the benchmark?

Render had the most failures with 12 failed checks and 99.89% uptime over 2 weeks. Railway had 1 failure (99.991% uptime). Cloudflare Workers, Fly.io, and Koyeb all had 0 failures and 100% uptime.