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

Feb 19, 2024 | by [Thibault Le Ouay Ducasse](https://bsky.app/profile/thibaultleouay.dev) | [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](/content/blog/monitoring-latency-cf-workers-fly-koyeb-raylway-render#cloudflare-workers/index.html), [Fly](/content/blog/monitoring-latency-cf-workers-fly-koyeb-raylway-render#flyio/index.html), [Koyeb](/content/blog/monitoring-latency-cf-workers-fly-koyeb-raylway-render#koyeb/index.html),
[Railway](/content/blog/monitoring-latency-cf-workers-fly-koyeb-raylway-render#railway/index.html) and [Render](/content/blog/monitoring-latency-cf-workers-fly-koyeb-raylway-render#render/index.html) using
[openstatus](/content/site-root.html).

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

For this test, I used a basic [Hono](https://hono.dev/) server that returns a
simple text response.

```js
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](https://github.com/openstatusHQ/status-code), 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:

```toml
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](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.
