# Moving an app from Fly.io to Cloud Run

> Fly.io runs your Dockerfile on VMs you size and count; Cloud Run runs the same image and counts for you. What each part of fly.toml becomes, secrets, volumes, Postgres, the domain, and both bills.

Published 2026-10-11 · Updated 2026-10-13 · [By Exodus team](https://exodus.elim.io/about) · From Fly
<https://exodus.elim.io/blog/fly-to-cloud-run>

Moving an app from Fly.io to Cloud Run keeps the Dockerfile and changes who decides how many copies run: Fly starts and stops the Machines you created, Cloud Run starts instances as traffic needs, down to none. The work is turning `fly.toml` into deploy flags, copying secrets Fly will not display, moving volumes and Postgres, and handling `SIGTERM`. At list prices Fly stays cheaper for the same app.

## In short

- Fly has no free tier: a `shared-cpu-1x` Machine with 512 MB is $3.69 for 30 days in its cheapest regions and traffic $0.02 per GB. Cloud Run's free tier is 180,000 vCPU-seconds and 2 million requests a month, then traffic to users in Europe is $0.105 per GiB.
- One app at 1 million requests and 50 GB a month costs $4.84 on Fly and $5.37 on Cloud Run; at 10 million requests and 1,000 GB, $35.35 and $146.27.
- Cloud Run sends `SIGTERM` and kills the container 10 seconds later; Fly sends `SIGINT` and waits 5 seconds by default.
- Cloud Run runs in 43 regions to Fly's 17, with a 99.95% SLA on every service; Fly's 99.9% applies to Enterprise customers.

The model app runs on Fly in Amsterdam and on Cloud Run in `europe-west1`, at list prices of October 11, 2026, for a 30-day month.

## When moving pays, and when it does not

Fly has no platform fee and, for organizations created since October 2024, no free tier: a trial of 2 hours of Machine runtime or 7 days, then you pay for what runs. A `shared-cpu-1x` Machine with 512 MB costs $3.69 for 30 days of continuous running in Ashburn or Secaucus, Fly's cheapest regions, and about 4% more in Amsterdam. A stopped Machine costs only its root file system, $0.15 per GB a month. Traffic to the internet is $0.02 per GB in North America and Europe. Managed Postgres starts at $38 a month.

Cloud Run has a [free tier](https://exodus.elim.io/blog/the-cloud-run-free-tier-explained) per billing account: 180,000 vCPU-seconds, 360,000 GiB-seconds and 2 million requests a month. After that a service with request-based billing pays $0.000024 per vCPU-second and $0.0000025 per GiB-second while it handles requests, and $0.40 per million requests (Tier 1 regions, prices of October 11, 2026). Traffic to users is where the two differ most: $0.105 per GiB within Europe on Cloud Run, five times Fly's rate, as the [egress guide](https://exodus.elim.io/blog/egress-on-google-cloud-explained) explains.

![Six bars: at 1 million requests and 50 GB, Fly costs $4.84 and Cloud Run $5.37; with a small Postgres with a standby, Fly $43.12 and Cloud Run with Cloud SQL $59.12; at 10 million requests and 1,000 GB, Fly $35.35 and Cloud Run $146.27](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-5cad628be0267b97c770effe01f72f08f24011e0986689ecf4e5c6be2991990b-680x360-svg/bytes "One app on Fly in Amsterdam and on Cloud Run in europe-west1, list prices of October 11, 2026, a 30-day month. Cloud Run is an upper bound: no request shares an instance with another")

The model is one web app. Every request takes 200 ms, of which 50 ms is CPU. On Cloud Run the service has 1 vCPU and 512 MiB; on Fly the Machines are sized so that their CPU baseline covers the load, because a shared vCPU on Fly may run only 6.25% of the time, plus a burst balance it saves while idle.

- **1 million requests and 50 GB a month.** Fly: one `shared-cpu-1x` 512 MB Machine always on, $3.84, plus $1.00 of traffic: $4.84. Cloud Run: $0.48 of CPU above the free tier and $4.89 for 46.6 GiB of egress: $5.37.
- **The same, with Postgres that has a standby.** Fly's Managed Postgres Basic (shared-2x, 1 GB, high availability, backups) is $38 plus $0.28 for 1 GB of data: $43.12 in all. Cloud SQL's nearest match, a high-availability `db-g1-small` with 1.7 GB and 10 GB of SSD, is $53.75: $59.12 in all. A single-zone `db-f1-micro` is about $9 a month, without the standby and outside the SLA; the [Cloud SQL guide](https://exodus.elim.io/blog/cloud-sql-smallest-instances-and-what-they-cost) has every size.
- **10 million requests and 1,000 GB.** Fly: two `shared-cpu-2x` 1 GB Machines, $15.35, plus $20 of traffic: $35.35. Cloud Run: $43.68 of CPU, $1.60 of memory, $3.20 of requests and $97.79 of egress: $146.27. If requests overlap on an instance, Cloud Run's CPU line falls; the egress line does not.

At list prices Fly is cheaper in all three. People move anyway for reasons that are not on that chart: the rest of their system is on Google Cloud; they want Google's managed services next to the app (Cloud SQL with point-in-time recovery, Pub/Sub, BigQuery, IAM for every service); they want autoscaling that adds instances rather than starting ones they created; their Postgres is the unmanaged Fly Postgres, which Fly lists as unsupported; or they need a region Fly does not have, since Fly runs in 17 regions and Cloud Run in 43. Cloud Run's 99.95% SLA applies to every service, while Fly's 99.9% applies to Enterprise customers. If none of these is your reason, the move costs time and saves nothing, and Exodus says so in its verdict. The same map for other platforms is in [moving from Heroku to Cloud Run](https://exodus.elim.io/blog/heroku-to-cloud-run) and [moving from DigitalOcean App Platform](https://exodus.elim.io/blog/digitalocean-app-platform-to-cloud-run).

## What Fly did for you

Start with an inventory, because each piece needs a home. `fly.toml` holds most of it; if it is not in the repository, `fly config save -a APP` writes the live one. Then list what lives outside it:

```shell
fly scale show -a APP
fly secrets list -a APP
fly volumes list -a APP
fly ips list -a APP
fly certs list -a APP
fly mpg list
```

![Nine pairs: http_service becomes a Cloud Run service; auto_stop and min_machines_running become scale to zero and --min-instances; concurrency becomes --concurrency; vm becomes --cpu and --memory; processes become one service per process or a worker pool; release_command becomes a Cloud Run job run before the deploy; mounts become Cloud Storage or Cloud SQL; secrets become Secret Manager; .internal and certs become service URLs, ID tokens and a load balancer](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-979dabcc1152c8d86832485c80df5381b1785a87cdbe49cf6f0cb4bd34ee93c8-680x604-svg/bytes "Where each part of a Fly app goes on Google Cloud")

## Step 1: the container

The Dockerfile Fly built is the Dockerfile Cloud Run builds; nothing Fly-specific lives in it. Three runtime details differ.

**The port and the address.** Fly sends traffic to `internal_port`, 8080 by default. Cloud Run sends it to 8080 by default too and sets `PORT` in the container; if your app listens elsewhere, deploy with `--port=3000`. The app must listen on `0.0.0.0`. Fly's private network is IPv6, so many Fly apps bind to `[::]`, which on Linux also accepts IPv4 and works on Cloud Run. An app bound only to its Fly private address, or to `127.0.0.1`, never answers Cloud Run's startup check.

**The stop signal.** Fly sends `SIGINT` when it stops a Machine, waits `kill_timeout` seconds (5 by default, up to 300), then sends `SIGTERM`. Cloud Run sends `SIGTERM` and kills the container 10 seconds later. An app that only handles `SIGINT`, or none at all, is killed mid-request on every scale-in. Handle both:

```ts
for (const signal of ["SIGTERM", "SIGINT"]) {
  process.on(signal, () => {
    console.log(`${signal}: finishing open requests`);
    server.close(() => process.exit(0));
  });
}
```

![A terminal: curl to the sample returns "hello from web, PORT=8080"; docker stop on the server with a handler takes 0.271 s and exits 0 after logging "SIGTERM: finishing open requests"; docker stop on the same server without a handler takes 10.234 s and exits 137](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-6bf697c923c4837acb583b4af736243d03e3320a59d39d6e8068de154c94fa57-680x400-svg/bytes "The sample on our machine, October 11, 2026. Docker, like Cloud Run, sends SIGTERM and kills the container after 10 seconds")

We ran a 15-line Node server with that handler in `node:24-slim`, and the same image with a server that has none. With the handler the container stops in a quarter of a second with exit code 0; without it Docker waits its 10 seconds and kills it, exit code 137. Cloud Run behaves the same way, and bills the CPU during those 10 seconds.

**The disk.** A Fly Machine's root file system is thrown away on restart and deploy unless you ask otherwise, and anything durable sits on a volume. On Cloud Run the writable file system lives in memory: what the app writes counts against the instance's memory and disappears with the instance. Step 5 deals with volumes.

## Step 2: secrets

Fly does not give secret values back. `fly secrets list` shows each name and a digest; Fly's documentation says it does not allow read access to the plain-text values, and that they are available only to the application, as environment variables. So the values come either from where they were made (the payment provider's dashboard, the OAuth app, the database) or from inside a running Machine.

Create a service account for the app, then copy each secret from a Machine straight into Secret Manager, so the value never appears on your screen or in your shell history:

```shell
gcloud iam service-accounts create web
fly ssh console -a APP -q -C "printenv STRIPE_KEY" | tr -d '\n' \
  | gcloud secrets create STRIPE_KEY --data-file=-
gcloud secrets add-iam-policy-binding STRIPE_KEY \
  --member=serviceAccount:web@PROJECT_ID.iam.gserviceaccount.com \
  --role=roles/secretmanager.secretAccessor
gcloud secrets versions access latest --secret=STRIPE_KEY | wc -c
```

`tr -d '\n'` removes the newline `printenv` adds; it would remove newlines inside a value too, so take multi-line values such as PEM keys from their source. The last command prints only the length, so you can compare it with what you expect without showing the value. The copy is as sensitive as the original: anyone who can SSH into the app or deploy to it could read these values all along, and a move is a good moment to rotate the ones that matter.

Plain settings from `[env]` in `fly.toml` go on the service with `--set-env-vars`. `PRIMARY_REGION`, `FLY_REGION`, `FLY_APP_NAME` and the other `FLY_` variables do not exist on Cloud Run; search the code for `FLY_` and give anything that reads them its own variable.

## Step 3: deploy and scaling

Pick the region next to the database, not next to you. For a Fly app in `ams` or `fra`, `europe-west1` (Belgium) is a Tier 1 region and `europe-west3` is Frankfurt; the [region guide](https://exodus.elim.io/blog/choosing-a-cloud-run-region) covers the trade-offs.

```shell
gcloud run deploy web --source . --region europe-west1 \
  --service-account=web@PROJECT_ID.iam.gserviceaccount.com \
  --allow-unauthenticated \
  --cpu=1 --memory=512Mi \
  --concurrency=80 --max-instances=10 \
  --set-env-vars=LOG_LEVEL=info \
  --set-secrets=STRIPE_KEY=STRIPE_KEY:latest
```

`--source .` builds the Dockerfile in Cloud Build and deploys it, as the [Vercel guide](https://exodus.elim.io/blog/vercel-to-cloud-run) describes. The output ends with a `run.app` URL; test the app there before the domain moves.

The scaling settings map, but the model underneath changes:

- **Stopping when idle.** `auto_stop_machines = "stop"` and `min_machines_running = 0`, which `fly launch` writes by default, are Cloud Run's default behaviour: no traffic, no instances. `min_machines_running = 1` becomes `--min-instances=1`, billed at the idle rate while it waits.
- **The number of Machines** was your ceiling: Fly's proxy never creates or destroys Machines, it only starts the ones you made. Cloud Run creates instances as traffic rises, up to 100 per service by default. Set `--max-instances` to the most you are willing to pay for; it is also how you protect a database with a fixed number of connections.
- **Concurrency.** `hard_limit` in `[http_service.concurrency]` becomes `--concurrency`, the most requests one instance takes at once (80 by default, 1,000 at most). `soft_limit` has no equivalent; Cloud Run decides when to add an instance.
- **Machine size**, the `vm` section, becomes `--cpu` and `--memory`. A Cloud Run vCPU is a whole vCPU while it serves a request, while a Fly shared vCPU has a 6.25% baseline, so an app that was throttled on `shared-cpu-1x` is not throttled at `--cpu=1`.

![A table comparing Fly.io and Cloud Run: who adds instances, CPU, the stop signal, the disk, protocols, regions, SLA and traffic price](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-d6d4a2f84e9b45eb1c6a3e7480de2f2aa662644bfa1d1c6722cacba428bb8483-680x528-svg/bytes "What changes in a move, from Fly's and Google's pages of October 11, 2026")

Cold starts change too. When a stopped app gets a request, Fly boots a Machine and Cloud Run starts an instance. A TU Berlin study measured a median of 1.6 s for Fly Machines with `fly launch` defaults and 0.53 s for Cloud Run functions with startup CPU boost turned off; the [cold start guide](https://exodus.elim.io/blog/cloud-run-cold-start-measured) has the details and the caveats.

## Step 4: release command, processes and cron

**The release command.** `release_command` runs a migration in a temporary Machine with the new image before Fly updates the app, and stops the deploy if it fails. On Cloud Run the same command becomes a job, run between the build and the deploy. Build the image once, run the job, then deploy that image:

```shell
gcloud artifacts repositories create apps --repository-format=docker --location=europe-west1
IMAGE=europe-west1-docker.pkg.dev/PROJECT_ID/apps/web:v42
gcloud builds submit --tag "$IMAGE"
gcloud run jobs deploy migrate --image "$IMAGE" --region europe-west1 \
  --service-account=web@PROJECT_ID.iam.gserviceaccount.com \
  --set-secrets=DATABASE_URL=DATABASE_URL:latest \
  --command=bin/rails --args=db:prepare \
  --execute-now --wait
gcloud run deploy web --image "$IMAGE" --region europe-west1
```

`--wait` makes the command fail when the migration fails, so a script or CI step stops there, as Fly's deploy did. The repository is created once; later deploys repeat the last four commands with a new tag.

**Process groups.** The `[processes]` section ran several commands from one image on separate Machines. On Cloud Run each process that serves HTTP becomes its own service, deployed from the same image with `--command`. A queue worker that only pulls work is a [worker pool](https://exodus.elim.io/blog/cloud-run-services-jobs-and-worker-pools), or a service with instance-based billing and `--min-instances=1`; a request-billed service gets no CPU between requests, so a worker there stalls.

**Cron.** On Fly, scheduled work is usually Supercronic in a `cron` process scaled to one Machine, or a scheduled Machine that runs about once an hour or a day. On Google Cloud the schedule moves out of the container: a Cloud Run job does the work (created like `migrate` above, here named `report`) and a Cloud Scheduler job starts it. The console's job page has a Triggers tab for this, or:

```shell
gcloud scheduler jobs create http nightly-report \
  --location=europe-west1 --schedule="0 5 * * *" \
  --uri=https://run.googleapis.com/v2/projects/PROJECT_ID/locations/europe-west1/jobs/report:run \
  --http-method=POST \
  --oauth-service-account-email=web@PROJECT_ID.iam.gserviceaccount.com
```

The service account needs permission to run the job (`roles/run.invoker` on it). Scheduler costs $0.10 a job a month with 3 free per billing account, and the time zone defaults to UTC.

## Step 5: volumes and the database

Cloud Run has no disk that outlives an instance, so every `[mounts]` entry needs a decision based on what is on it.

- **User uploads and generated files** go to a Cloud Storage bucket, written through the client library. Cloud Run can also mount a bucket as a directory (`--add-volume=name=files,type=cloud-storage,bucket=BUCKET` and `--add-volume-mount=volume=files,mount-path=/data`), which suits files that are written once and read often. The mount has no file locking: when two instances write the same file, the last write wins.
- **SQLite or LiteFS** cannot move as is. A database file on a bucket mount will be corrupted by two writers, and Cloud Run's other volumes vanish with the instance. Move the data to Cloud SQL, or keep that app on Fly.
- **Postgres**, managed or not, moves to Cloud SQL with `pg_dump` and `pg_restore`.

For Postgres, create the Cloud SQL instance and database first, then plan a short write freeze. Stop the app on Fly so nothing writes during the copy, open a tunnel to the Fly database, and dump it in the custom format:

```shell
fly scale count 0 -a APP --yes
fly mpg proxy CLUSTER_ID --local-port 15432
pg_dump -Fc --no-owner --no-acl -h localhost -p 15432 -U fly-user -d fly-db -f app.dump
```

Run the proxy in a second terminal; `pg_dump` asks for the password, so it never sits in a command line. For the unmanaged Fly Postgres the tunnel is `fly proxy 15432:5432 -a PG_APP`. Then restore through the Cloud SQL Auth Proxy, also in its own terminal:

```shell
cloud-sql-proxy PROJECT_ID:europe-west1:INSTANCE --port 5433
pg_restore --no-owner --no-acl -h 127.0.0.1 -p 5433 -U postgres -d app app.dump
```

If the restore stops on a `CREATE EXTENSION`, Google's import page shows how to list the archive's contents, comment the extension statements out, and restore with that list. Check row counts on a few tables on both sides before the app writes again.

Redis from Upstash and object storage from Tigris were bought through Fly but belong to those providers, with their own connection strings. Point the Cloud Run service at them first, and decide later whether to move them to Memorystore or Cloud Storage. Traffic from Google to them is internet egress on your Google bill.

## Step 6: private network and regions

On Fly, apps in one organization reach each other over a private IPv6 network at names such as `api.internal`, with no authentication. Cloud Run has no such network between services. A private service gets `--ingress=internal` and no `--allow-unauthenticated`; the caller fetches a Google-signed ID token with the receiving service's URL as its audience and sends it as a Bearer token, and the caller's service account gets `roles/run.invoker` on the receiver. Traffic between Cloud Run services in the same region is free. For a database or another server on private addresses, Cloud Run reaches a VPC network through Direct VPC egress (`--network` and `--vpc-egress`).

A Fly app with Machines in several regions behind one anycast address has a counterpart, with more parts. `gcloud run deploy web --regions=europe-west1,us-east4 --image "$IMAGE"` deploys one service to several regions, and a global external Application Load Balancer with a serverless network endpoint group in each region sends users to the nearest one. The load balancer costs $0.025 an hour for its forwarding rule, about $18 a month. If the database lives in one region, app instances far from it wait on every query, so check whether the extra regions helped before you rebuild them.

## Step 7: the domain and the switch

Fly issued the certificate (`fly certs add`) for A and AAAA records pointing at the app's addresses, or a CNAME to its `fly.dev` name. Cloud Run offers three ways to put your domain on a service. Google recommends a global external Application Load Balancer; Firebase Hosting can front the service; Cloud Run's own domain mapping is in Preview, which Google says is not production-ready. The [Vercel guide](https://exodus.elim.io/blog/vercel-to-cloud-run) walks through the same choice.

The switch follows the order in the [Vercel guide](https://exodus.elim.io/blog/vercel-to-cloud-run): lower the TTL a day before, test on the `run.app` URL, add the new OAuth callback, make the certificate active, change DNS, keep the old app for a week. Fly adds three things:

1. Test the parts Fly had on the `run.app` URL: the migration job, an upload, and a scheduled job run by hand with `gcloud run jobs execute report --region europe-west1 --wait`.
1. Before the DNS change, freeze writes, copy the database (Step 5), point the service at Cloud SQL and deploy.
1. Leave the Fly app scaled to zero for the week: stopped Machines cost only their root file system, and volumes still cost $0.15 per GB. Then destroy the app, its volumes and the database, which Fly bills separately from the app.

## What breaks most often

- The app listens on its Fly private address or on `127.0.0.1`, and the first deploy fails its startup check.
- Shutdown code waits for `SIGINT`, so instances are killed mid-request when Cloud Run scales in.
- A secret copied with its trailing newline breaks an API key comparison.
- Files written to a volume are written to memory, and vanish when the instance stops; uploads disappear a few minutes later.
- A worker or Supercronic process copied into the web service runs on every instance, or gets no CPU between requests.
- Calls to `other-app.internal` fail, because that name does not exist on Google Cloud.
- No `--max-instances`, and a traffic spike opens more database connections than Postgres allows.

All of them show up when you test on the `run.app` URL, before users see them. The [patterns that break on Cloud Run](https://exodus.elim.io/blog/patterns-that-break-on-cloud-run) guide covers the rest.

## Where Exodus comes in

Exodus's CLI reads `fly.toml`, the Dockerfile and the code on your machine, with your coding agent reading the parts that need judgement. The report lists every piece from the picture above with what it becomes: the release command as a job, each process group as a service or worker pool, the mounts and what is on them, the `FLY_` variables the code reads, the shutdown handling, the calls to `.internal` names. It estimates the month on Cloud Run for your traffic next to what Fly charges now, egress and database included, and says plainly when staying on Fly is cheaper.

## FAQ

### Is Cloud Run cheaper than Fly.io?

Usually not at list prices. One app with 1 million requests and 50 GB of traffic a month costs $4.84 on Fly and $5.37 on Cloud Run, and at 10 million requests and 1,000 GB, $35.35 against $146.27. Most of the gap is traffic: $0.02 per GB on Fly, $0.105 per GiB on Cloud Run within Europe.

### How do I get my secret values out of Fly.io?

Fly does not show them: fly secrets list prints each name and a digest. Take each value from where it was made, or pipe it from a running Machine with fly ssh console and printenv straight into Secret Manager, so it never appears on screen. Rotate the ones that matter after the move.

### Can I run SQLite or LiteFS on Cloud Run?

Not as is. A database file on a Cloud Storage mount is corrupted by two writers, because the mount has no file locking, and Cloud Run's other volumes vanish with the instance. Move the data to Cloud SQL, or keep that app on Fly.

### What replaces Fly's release_command on Cloud Run?

A Cloud Run job with the same image, run between the build and the deploy. With --execute-now --wait the command fails when the migration fails, so the deploy stops there, as it did on Fly.

## Sources

1. [Fly.io resource pricing (no platform fee and no free tier for new organizations; shared-cpu-1x 512MB $3.69 per 30 days in iad and ewr, per-second prices and region markups in the page's price table; stopped Machine $0.15/GB of rootfs; volumes $0.15/GB; outbound $0.02/GB in North America and Europe; Managed Postgres from $38; unmanaged Fly Postgres listed as unsupported; read Oct 11, 2026)](https://docs.fly.io/about/pricing)
2. [Fly.io free trial (2 hours of Machine runtime or 7 days; read Oct 11, 2026)](https://docs.fly.io/about/free-trial)
3. [Fly.io Managed Postgres (Basic: shared-2x, 1GB, $38 a month with high availability and backups; storage $0.28/GB; version upgrades not yet available; read Oct 11, 2026)](https://docs.fly.io/postgres)
4. [Fly.io CPU performance (a shared vCPU may run 5 ms of every 80 ms, 6.25%, plus a burst balance; read Oct 11, 2026)](https://docs.fly.io/machines/cpu-performance)
5. [Fly.io app configuration, fly.toml (kill_signal SIGINT and kill_timeout 5 s by default; release_command in a temporary Machine; http_service, concurrency soft_limit and hard_limit; services over tcp or udp; mounts; vm; processes; statics; read Oct 11, 2026)](https://docs.fly.io/reference/configuration)
6. [Fly.io autostop and autostart (fly launch defaults; Fly Proxy never creates or destroys Machines; read Oct 11, 2026)](https://docs.fly.io/launch/autostop-autostart)
7. [Fly.io secrets (injected as environment variables at boot; no read access to plain-text values; read Oct 11, 2026)](https://docs.fly.io/apps/secrets)
8. [fly secrets list (name and digest; the value is only available to the application; read Oct 11, 2026)](https://fly.io/docs/flyctl/secrets-list)
9. [Fly.io private networking (6PN over WireGuard, .internal DNS returns started Machines only; read Oct 11, 2026)](https://docs.fly.io/networking/private-networking)
10. [Fly.io custom domains (fly certs add; A and AAAA records or a CNAME to the .fly.dev name; read Oct 11, 2026)](https://docs.fly.io/networking/custom-domain)
11. [Fly.io, migrate from Heroku (fly mpg proxy to reach a Managed Postgres cluster from your machine; read Oct 11, 2026)](https://docs.fly.io/getting-started/migrate-from-heroku)
12. [Fly.io task scheduling (Cron Manager, Supercronic in a cron process, scheduled Machines; read Oct 11, 2026)](https://docs.fly.io/blueprints/task-scheduling)
13. [Fly.io regions (17 regions; read Oct 11, 2026)](https://docs.fly.io/reference/regions)
14. [Fly.io uptime SLA (99.9% for customers on an Enterprise plan; read Oct 11, 2026)](https://fly.io/legal/sla-uptime)
15. [Cloud Run pricing, Google (request-based billing in Tier 1: $0.000024 per vCPU-second, $0.0000025 per GiB-second, $0.40 per million requests; free 180,000 vCPU-seconds, 360,000 GiB-seconds and 2 million requests a month per billing account; read Oct 11, 2026)](https://cloud.google.com/run/pricing)
16. [Cloud SQL pricing, Google (HA db-g1-small $0.07 an hour; HA SSD storage $0.000465753 per GiB-hour; shared-core machines outside the SLA; read Oct 11, 2026)](https://cloud.google.com/sql/pricing)
17. [Container runtime contract, Cloud Run (listen on 0.0.0.0 and PORT, 8080 by default; in-memory file system; SIGTERM, then SIGKILL 10 seconds later; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/container-contract)
18. [Cloud Run quotas and limits (100 instances by default; 80 concurrent requests per instance by default, 1,000 at most; 60 minute request timeout; read Oct 11, 2026)](https://docs.cloud.google.com/run/quotas)
19. [What is Cloud Run, Google (WebSockets, HTTP/2 and gRPC; disposable file system; worker pools for work that is not HTTP; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/overview/what-is-cloud-run)
20. [Cloud Storage volume mounts, Cloud Run (Cloud Storage FUSE; no file locking, last write wins; not fully POSIX; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/services/cloud-storage-volume-mounts)
21. [Running database migrations with Cloud Run jobs, Google Cloud blog (a job per migration; build, run the job, then deploy; read Oct 11, 2026)](https://cloud.google.com/blog/topics/developers-practitioners/running-database-migrations-cloud-run-jobs/)
22. [Execute jobs on a schedule, Cloud Run (a Cloud Scheduler job that calls the job's run endpoint; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/execute/jobs-on-schedule)
23. [Cloud Scheduler pricing, Google ($0.10 a job per 31 days; 3 jobs free per billing account; read Oct 11, 2026)](https://cloud.google.com/scheduler/pricing)
24. [Direct VPC egress, Cloud Run (--network and --vpc-egress; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/vpc-direct-vpc)
25. [Connect using the Cloud SQL Auth Proxy, Cloud SQL for PostgreSQL (cloud-sql-proxy --port on 127.0.0.1; read Oct 11, 2026)](https://docs.cloud.google.com/sql/docs/postgres/connect-auth-proxy)
26. [Map custom domains, Cloud Run (global external Application Load Balancer recommended; Firebase Hosting; Cloud Run domain mapping in Preview, not production-ready; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/mapping-custom-domains)
27. [Cloud Load Balancing pricing, Google (first 5 forwarding rules $0.025 an hour; read Oct 11, 2026)](https://cloud.google.com/load-balancing/pricing)
28. [Serve traffic from multiple regions, Cloud Run (gcloud run deploy --regions, then a global external Application Load Balancer with serverless NEGs; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/multiple-regions)
29. [Authenticating service-to-service, Cloud Run (a Google-signed ID token with the receiving service's URL as audience; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/authenticating/service-to-service)
30. [Export and import using pg_dump and pg_restore, Cloud SQL for PostgreSQL (custom-format archives; extension statements commented out; read Oct 11, 2026)](https://docs.cloud.google.com/sql/docs/postgres/import-export/import-export-dmp)
31. [Cloud Run SLA, Google (99.95% a month in most regions, no plan required; read Oct 11, 2026)](https://cloud.google.com/run/sla)
32. [Cloud Run locations, Google (43 regions in gcloud run regions list, Oct 11, 2026)](https://docs.cloud.google.com/run/docs/locations)
33. [Schirmer et al., New Kids: An Architecture and Performance Investigation of Second-Generation Serverless Platforms, TU Berlin, ACM TOIT 2026 (Cloud Run functions with startup CPU boost off against Fly Machines with fly launch defaults; read Oct 11, 2026)](https://arxiv.org/abs/2604.15916)
34. Exodus team: a Node.js sample with the signal handler from this guide, run with Docker on Oct 11, 2026; flyctl v0.4.115 and gcloud 587.0.0 help output; the cost model and its arithmetic
