# Moving a Docker Compose app to Cloud Run

> gcloud run compose up puts a whole compose.yaml in one Cloud Run instance, fine for a demo and wrong for production. How to split it into a service, a worker pool, Cloud SQL and Cloud Storage.

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

A Docker Compose app moves to Cloud Run by splitting the stack: the web container becomes a Cloud Run service, the worker a worker pool or Cloud Tasks, Postgres Cloud SQL, named volumes a Cloud Storage bucket, and `.env` secrets Secret Manager. `gcloud run compose up` deploys the whole file as one service with one instance at most, which fits a demo, not production.

## In short

- `gcloud run compose up`, generally available since March 25, 2026, deploys the file as one Cloud Run service with 1 maximum instance and 1 vCPU and 1 GiB per container.
- For the model in this guide, a Hetzner VPS costs $8.39 a month, the split stack $17.24 to $83.24, and `compose up` kept running $209.91.
- Memorystore starts at $25.55 a month without an SLA and $37.23 with Basic Redis, the largest line of the split bill.
- A worker pool with one instance of 1 vCPU and 512 MiB is $31.17 a month; Cloud Tasks is free for the first million operations.
- Cloud Run stops an instance 10 seconds after `SIGTERM`, and Cloud SQL's `db-f1-micro` allows 25 connections.

## What it costs

The model is a typical compose stack for a small app (web, worker, Postgres with its data in a named volume, Redis): 1 million requests a month of 200 ms each, 50 GB of traffic out, and 100,000 background tasks of about a second. Prices are list prices of October 11, 2026, before VAT, in europe-west1 (Belgium) for Google and Germany for Hetzner.

![Five bars: the compose stack on a Hetzner CX23 costs $8.39 a month; gcloud run compose up kept running costs $209.91; split into Cloud Run, a worker pool, Cloud SQL and Memorystore $83.24; the same without Redis $46.01; with the worker replaced by Cloud Tasks $17.24](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-67a1a4d62416ce8b68498bc8845e7ce2261d995dd4ed33370df776b0826aabfc-680x360-svg/bytes "One compose stack (web, worker, Postgres, Redis), five ways to run it. Hetzner in Germany, Google in europe-west1, list prices of October 11, 2026, before VAT")

- **On a VPS:** the whole stack fits a Hetzner CX23 (2 vCPUs, 4 GB) at $8.39 a month with its IPv4 address and backups, and 20 TB of traffic included. The [VPS guide](https://exodus.elim.io/blog/a-vps-to-cloud-run) has the prices.
- **compose up, kept running: $209.91.** The command gives each of the four containers 1 vCPU and 1 GiB, so the one instance is 4 vCPUs and 4 GiB. For the worker and the database to run between requests, the instance needs instance-based billing and a minimum of one instance: 4 vCPUs at $0.000018 a second for 730 hours is $189.22, the memory $21.02, minus the free tier's $5.22, plus $4.89 of traffic.
- **Split, with Redis on Memorystore: $83.24.** The web service on request-based billing costs $0.48 of CPU past the free tier (an upper bound, with no two requests sharing an instance). Traffic is $4.89 at $0.105 per GiB. A worker pool with one instance of 1 vCPU and 512 MiB runs all month for $31.17. Cloud SQL's `db-f1-micro` is $9.37, 5 GiB of uploads in Cloud Storage $0.10, and Memorystore for Redis Basic with 1 GiB $37.23.
- **Split, without Redis: $46.01.** The same without Memorystore, when Redis was only a cache or can move into Postgres.
- **Split, with the worker replaced by Cloud Tasks: $17.24.** Cloud Tasks sends each task to the web service as an HTTP request, so the worker pool goes away. The tasks add $2.40 of CPU to the service, and the first million task operations a month are free.

So the VPS is cheaper at every size of this model, and the lift-and-shift is the most expensive way to run the stack on Google Cloud. A move pays for the work it takes off you (patching, backups, a machine that can fail, spikes), not on the bill; the [VPS guide](https://exodus.elim.io/blog/a-vps-to-cloud-run) makes that case in full. If you move, the shape of the stack decides the bill more than any price: an always-on worker and a managed Redis are the two largest lines after the lift itself.

## What `gcloud run compose up` does

The command reads a `compose.yaml`, builds the services that have a `build:` section with Cloud Build into an Artifact Registry repository named `cloud-run-source-deploy`, creates a Secret Manager secret for each compose secret and one Cloud Storage bucket for the volumes, and deploys **one** Cloud Run service. Every compose service becomes a container in that one service. One container is the ingress container that receives requests; the others are sidecars in the same instance, sharing its network, so they reach each other on localhost. Cloud Run adds each compose service name to `/etc/hosts`, which is why `db:5432` keeps working.

```shell
gcloud run compose up compose.yaml --region europe-west1
```

It has been generally available since March 25, 2026. Google's page says what it is for: development, and the step from a laptop to the cloud. For production it recommends Terraform, and it lists the limits: one service, a subset of Compose, and 1 maximum instance by default.

![One Cloud Run service named shop with at most one instance; inside the instance four containers of 1 vCPU and 1 GiB each: web as the ingress container on port 3000, worker, db running postgres:17, and redis; the browser reaches web over HTTPS; compose secrets go to Secret Manager; the pgdata and uploads volumes go to a Cloud Storage bucket over FUSE, so Postgres keeps its data files on a file system without file locking](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-1486f17056ae5a3a1f98aa6bf89c145bd10306e8726dcfb07614e3478ba0bc57-680x452-svg/bytes "What gcloud run compose up builds from a four-service compose file, from the YAML it generated for our sample, October 11, 2026")

To see what it builds, we took a sample stack (web and worker built from one Dockerfile, `postgres:17` with a named volume, `redis:7`) and ran the translation step on our machine. gcloud installs it as a component named `run-compose`, and its `translate` command works offline:

![A terminal: run-compose translate on the original compose file fails because web depends on db, which has no ports; after adding expose to db and redis and marking web as the ingress container, it writes one service file in which maxScale is 1, CPU throttling is left on, each of the four containers gets 1000m of CPU, and both volumes point to the same bucket](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-f310c2de673cdbde335111e346ae07d327bc07be89e892ce5563f1e5669ac526-680x536-svg/bytes "The translation of our sample compose file by the run-compose component of gcloud 587.0.0, run offline on October 11, 2026. The middle of the output is cut")

What came out, and what it means:

- **A dependency needs a port.** `depends_on: db` failed until `db` had `expose: ["5432"]`, because Cloud Run starts dependencies first and checks them with a TCP probe on their port. Our file had no ports on Postgres or Redis, like most compose files.
- **The ingress container needs marking.** Without `x-google-cloudrun: ingress-container: true` on `web`, the translator gave the public port to `redis`, the only other service with a port. Mark it.
- **Every container gets 1 vCPU and 1 GiB.** The `cpus:` key is the only size hint, and `cpus: 0.5` still came out as 1 vCPU. Four services make a 4 vCPU instance.
- **One instance at most, with CPU throttled.** `maxScale` is 1, so traffic cannot add instances. CPU throttling is left on, so the service uses request-based billing: Cloud Run gives CPU to all containers only while a request is being handled, and the worker barely runs between requests.
- **Postgres on a bucket.** The named volume `pgdata` became a folder in a Cloud Storage bucket, mounted with Cloud Storage FUSE. Google's FUSE page says it is not POSIX compliant and has no file locking, and a database expects both. When the instance scales to zero, Postgres stops, and during a deploy the new revision's instance can start a second Postgres on the same files while the old one still runs.
- **Values from env_file in plain text.** The `STRIPE_KEY` from `.env` went into the service as a plain environment variable, visible to anyone who can read the service's settings. Only a compose `secrets:` entry goes to Secret Manager.
- **PORT is reserved.** We validated the generated file with `gcloud run services replace --dry-run` against our project, and Cloud Run refused it: "The following reserved env names were provided: PORT". Our `web` set `PORT: "3000"` in `environment`, as many compose files do. Remove it; Cloud Run sets `PORT` itself.

One more thing we found in the source: in gcloud 587.0.0, `gcloud run compose up --dry-run` is accepted but never used. The command still creates the repository, builds, creates the bucket and secrets, and deploys. To preview, read the translation as above, or deploy into a scratch project.

So `compose up` fits a demo or a disposable staging copy. For production, split the stack.

## The split: what each compose service becomes

The app and the worker are already images and move as containers. The rest of the file describes a machine (a disk that keeps the database, a network where names resolve), and those parts become Google services.

![Ten pairs: web with build and ports becomes a Cloud Run service; the worker becomes a worker pool or Cloud Tasks; db with postgres:17 becomes Cloud SQL; redis becomes Memorystore or nothing; the uploads volume becomes Cloud Storage; environment and env_file become environment variables; compose secrets become Secret Manager at the same path; depends_on and healthcheck become startup probes and retries; service names become socket paths and URLs; restart: unless-stopped needs no line](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-69c242443870387a77c1753b118a7e36df2740f3d1b1ad73806f0fbdc98b9f95-680x676-svg/bytes "Where each part of a compose file goes when the stack is split")

### Step 1: read the compose file

`docker compose config` prints the file as Docker sees it, with every override and variable applied. These lines list the services, their images, ports, volumes and dependencies without printing the environment, which holds the passwords:

```shell
docker compose config --services
docker compose config --volumes
docker compose config --format json | jq -r '.services | to_entries[]
  | "\(.key)\t\(.value.image // "build")\tports=\([.value.ports[]?.target] | join(","))\tvolumes=\([.value.volumes[]?.source] | join(","))\tdepends=\((.value.depends_on // {}) | keys | join(","))"'
```

Plain `docker compose config` prints the values from `.env`, so do not paste it anywhere. Our sample printed:

```
db      postgres:17  ports=      volumes=pgdata   depends=
redis   redis:7      ports=      volumes=         depends=
web     build        ports=3000  volumes=uploads  depends=db,redis
worker  build        ports=      volumes=         depends=db,redis
```

Services you built are your code and move as containers. Services from public images (`postgres`, `redis`, `nginx`) are infrastructure, and each becomes a managed service or goes away. Volumes are data that needs a new home. A crontab or nginx outside compose is covered in the [VPS guide](https://exodus.elim.io/blog/a-vps-to-cloud-run).

### Step 2: the images

Cloud Run pulls images from Artifact Registry or Docker Hub. Create a repository once and push the image your `web` and `worker` services build. Cloud Run runs Linux x86_64 images, so on an ARM Mac build with `--platform linux/amd64`:

```shell
gcloud artifacts repositories create shop --repository-format=docker --location=europe-west1
gcloud auth configure-docker europe-west1-docker.pkg.dev
docker build --platform linux/amd64 -t europe-west1-docker.pkg.dev/PROJECT_ID/shop/shop:1 .
docker push europe-west1-docker.pkg.dev/PROJECT_ID/shop/shop:1
```

Or use `gcloud builds submit --tag` with the same name. When `web` and `worker` differ only by `command:`, one image serves both.

Check four things in the code. The app must listen on `0.0.0.0` and on the port Cloud Run sends requests to: set it with `--port=3000` at deploy time and drop `PORT` from the environment. It must handle `SIGTERM`, because Cloud Run stops an instance 10 seconds after sending it. It must log to stdout. And it must not keep files on local disk, since every instance starts from the image; run it with `docker run --read-only --tmpfs /tmp` to find the writes, as the VPS guide shows.

### Step 3: the database

The `db` service and its `pgdata` volume become Cloud SQL, a Postgres that Google runs and backs up daily. Create the instance as the [Cloud SQL guide](https://exodus.elim.io/blog/cloud-sql-smallest-instances-and-what-they-cost) describes; the smallest, `db-f1-micro`, is $9.37 a month with 10 GiB of disk.

Dump the data through compose, with the app stopped so nothing writes:

```shell
docker compose stop web worker
docker compose exec -T db pg_dump -Fc --no-owner --no-acl -U shop shop > shop.dump
```

`-T` matters: without it compose attaches a terminal, which corrupts a binary dump. We ran this against the sample's `postgres:17` with 1,234 rows, and `pg_restore --list` read the dump back. Restore it into Cloud SQL through the Auth Proxy as the [App Platform guide](https://exodus.elim.io/blog/digitalocean-app-platform-to-cloud-run) shows, and compare row counts. For a large database that cannot stop, the VPS guide describes Database Migration Service.

`DATABASE_URL` changes from `postgres://shop:...@db:5432/shop` to a socket:

```
postgresql://shop:PASSWORD@/shop?host=/cloudsql/PROJECT_ID:europe-west1:shop-db
```

The service gets `--add-cloudsql-instances` and its service account the Cloud SQL Client role. In compose the app had one connection pool to a database on the same machine; on Cloud Run each instance opens its own pool, and the micro allows 25 connections, so set `--max-instances` with the pool size in mind.

### Step 4: Redis

Ask what Redis did before you move it, because the managed version is the largest line of the split bill; [do you still need Redis on Cloud Run](https://exodus.elim.io/blog/do-you-still-need-redis-on-cloud-run) finds each job and its replacement.

- **A cache.** Each Cloud Run instance can keep a small cache in memory, or the cache can go. Many small apps lose nothing measurable.
- **Sessions.** Signed cookies or a table in Postgres; the [patterns guide](https://exodus.elim.io/blog/patterns-that-break-on-cloud-run) has the options.
- **A job queue** (Sidekiq, BullMQ, Celery, RQ). Either keep Redis, or move the jobs to Cloud Tasks (Step 5).

If it stays, Memorystore for Redis Basic with 1 GiB costs $0.051 per GiB-hour in Belgium, $37.23 a month; Basic has no replica. Memorystore for Valkey, which speaks the same protocol, starts with the `shared-core-nano` node at $0.035 an hour, $25.55 a month, but Google says that node has no SLA and recommends it only for development and testing. Either one lives in a VPC network, so the service reaches it with Direct VPC egress, which Google recommends for this:

```shell
gcloud redis instances create shop-cache --region=europe-west1 --tier=basic --size=1 --redis-version=redis_7_2
gcloud run services update shop --region europe-west1 \
  --network=default --subnet=default --vpc-egress=private-ranges-only
```

`REDIS_URL` then points to the instance's private IP, which this prints:

```shell
gcloud redis instances describe shop-cache --region=europe-west1 --format='value(host)'
```

### Step 5: the worker

In compose the worker was a container that ran forever next to the app. On Cloud Run that is a **worker pool**: instances with no URL that run all the time, billed at $0.000011244 per vCPU-second, which is $31.17 a month for one instance of 1 vCPU and 512 MiB.

```shell
gcloud run worker-pools deploy shop-worker --region europe-west1 \
  --image europe-west1-docker.pkg.dev/PROJECT_ID/shop/shop:1 \
  --command=node --args=worker.mjs --instances=1 \
  --cpu=1 --memory=512Mi \
  --service-account=shop@PROJECT_ID.iam.gserviceaccount.com \
  --set-secrets=DATABASE_URL=DATABASE_URL:latest
```

A worker reading a Redis queue also needs the network flags from Step 4. The [services, jobs and worker pools guide](https://exodus.elim.io/blog/cloud-run-services-jobs-and-worker-pools) explains how worker pools scale.

The cheaper shape is to stop running a worker at all. With Cloud Tasks, the app adds a task to a queue and Cloud Tasks calls an HTTP endpoint of the app for each one, retrying on failure. The work then runs inside the web service and is billed like any request. A task may run 30 minutes at most, so longer jobs still need a worker pool or a Cloud Run job. A queue is one command:

```shell
gcloud tasks queues create shop-jobs --location=europe-west1 --max-attempts=5
```

The job code changes from "pull from Redis" to "handle a POST". The [background workers guide](https://exodus.elim.io/blog/background-workers-with-cloud-tasks) has the producer, a handler that checks the token, the retry settings and the volume at which a worker pool becomes cheaper. Scheduled work that compose ran with a cron container becomes a Cloud Scheduler job; the [cron and timers guide](https://exodus.elim.io/blog/cron-and-timers-with-cloud-scheduler) has the commands.

### Step 6: volumes, settings and secrets

**Named volumes with user files** (`uploads`) go to a Cloud Storage bucket. The clean way is to write files with Google's client library, as the [files guide](https://exodus.elim.io/blog/files-and-uploads-with-cloud-storage) shows. The quick way is a FUSE mount at the old path, with the limits above: fine for uploads with unique names, wrong for anything that is edited in place or locked. Copy the old files out of the volume first:

```shell
docker compose cp web:/srv/app/uploads ./uploads
gcloud storage rsync ./uploads gs://PROJECT_ID-uploads --recursive
```

**Volumes for scratch files** become in-memory volumes, which use the containers' memory, or nothing.

**Bind-mounted config files** go into the image with a `COPY` line; there is no host to mount from.

**Environment variables and env_file** split in two. Settings that are not secret go to `--set-env-vars`. Passwords and keys go to Secret Manager, piped from `.env` so they never show on screen; the [VPS guide](https://exodus.elim.io/blog/a-vps-to-cloud-run) has the exact pipe.

**Compose secrets** were files at `/run/secrets/<name>`. Cloud Run can mount a Secret Manager secret at the same path, so the code does not change:

```shell
tr -d '\n' < stripe_key.txt | gcloud secrets create STRIPE_KEY --data-file=-
```

Then deploy with the mount in `--set-secrets`, as in Step 7: a key that starts with `/` is a mount path, any other key an environment variable.

### Step 7: deploy the service

```shell
gcloud iam service-accounts create shop
gcloud run deploy shop --region europe-west1 \
  --image europe-west1-docker.pkg.dev/PROJECT_ID/shop/shop:1 \
  --service-account=shop@PROJECT_ID.iam.gserviceaccount.com \
  --allow-unauthenticated --port=3000 \
  --cpu=1 --memory=512Mi --max-instances=5 \
  --add-cloudsql-instances=PROJECT_ID:europe-west1:shop-db \
  --set-env-vars=NODE_ENV=production,UPLOAD_BUCKET=PROJECT_ID-uploads \
  --set-secrets=DATABASE_URL=DATABASE_URL:latest,/run/secrets/stripe_key=STRIPE_KEY:latest
```

The service account needs the Cloud SQL Client role, `roles/secretmanager.secretAccessor` on each secret, and access to the bucket. Test everything on the `run.app` URL before DNS changes.

**depends_on and healthcheck** have no equivalent across services. Compose waited for `pg_isready` before starting `web`; on Cloud Run the database is already running, but retry the first connection in code anyway, and give the service a startup probe on an endpoint that checks the database, as [health checks and startup probes](https://exodus.elim.io/blog/health-checks-and-startup-probes) describes. Cloud Run probes are HTTP, TCP or gRPC; a `healthcheck` that runs a command inside the container has to become an endpoint.

**Service names** stop resolving: there is no `db` or `redis` host any more. The database is a socket path, Redis a private IP, and another web service in the stack is its own Cloud Run service with its own URL. If only your services should call it, deploy it with `--ingress=internal` and `--no-allow-unauthenticated`, and have the caller send an ID token, as Google's service-to-service page describes.

**An nginx service** in front of the app usually goes: Cloud Run serves HTTPS with a managed certificate. If you depend on its rules, it can run as a sidecar in the same service, the one place where Cloud Run's multi-container model matches compose. Sidecars are deployed with `--container` flags, one per container, with `--depends-on` for the start order and `--port` on the one that receives requests.

**restart: unless-stopped** goes: Cloud Run replaces crashed instances.

## The switch

Lower the DNS TTL a day ahead, then:

1. `docker compose stop web worker`, so nothing writes.
1. Take the final dump and restore it.
1. `docker compose cp` and `gcloud storage rsync` the uploads one last time.
1. Change DNS to Cloud Run, as the [Vercel guide](https://exodus.elim.io/blog/vercel-to-cloud-run) describes, and stop any cron container or host crontab so nightly jobs do not run twice.
1. Keep the machine with `docker compose down` (no `-v`, so the volumes stay) for a week as the way back, then back up the volumes and delete it.

## Keep `compose.yaml` for development

The compose file stays the way to run the stack on a laptop. Production is now described by the deploy commands above, in a script or a CI workflow, or by Terraform as Google recommends. Keep the environment variable names the same in both, so only the values differ.

## What breaks most often

- `PORT` set in the compose environment, which Cloud Run refuses.
- Code that still connects to `db`, `redis` or another service by name.
- Postgres or SQLite left on a volume, whether a FUSE bucket or memory, losing data on the next deploy or scale-down.
- A worker deployed as a service, which gets almost no CPU between requests.
- `env_file` passwords that ended up as plain environment variables.
- A `healthcheck` command with no HTTP endpoint behind it.
- One connection pool per instance against a database that allows 25 connections.
- Images built on an ARM Mac without `--platform linux/amd64`.

Test a sign-in, an upload and a background job on the `run.app` URL first.

## Where Exodus comes in

A compose file is the most complete description of a self-hosted stack, and Exodus's CLI reads it along with the Dockerfile and the code. Your coding agent maps each service to its Cloud Run shape, finds the service names, `PORT` settings, volumes and secrets in `env_file`, and the report shows the bill of each shape, like the five bars above, before anything moves.

## FAQ

### Can Cloud Run run a docker-compose.yml file?

Yes. Since March 25, 2026, gcloud run compose up deploys a compose file as one Cloud Run service, each compose service a container in it. Google describes it as a step from a laptop to the cloud and recommends Terraform for production; it supports a subset of Compose and 1 maximum instance by default.

### Can I run Postgres in a container on Cloud Run?

Not for data you need. A named volume becomes a Cloud Storage bucket mounted with FUSE, which is not POSIX compliant and has no file locking. Postgres stops when the instance scales to zero, and a deploy can start a second Postgres on the same files. Move it to Cloud SQL, from $9.37 a month.

### How do compose services reach each other on Cloud Run?

Service names stop resolving after the split. The database becomes a Cloud SQL socket path, Redis a private IP reached with Direct VPC egress, and another web service its own Cloud Run service with its own URL. A private one gets internal ingress, and the caller sends an ID token.

### What replaces depends_on and healthcheck on Cloud Run?

Nothing across services. Retry the first database connection in code, and give the service a startup probe on an endpoint that checks the database. Cloud Run probes are HTTP, TCP or gRPC, so a healthcheck that runs a command inside the container has to become an endpoint.

### Is Cloud Run cheaper than Docker Compose on a VPS?

Not for the small app in this guide: a Hetzner CX23 runs the whole stack for $8.39 a month, against $17.24 to $83.24 for the split stack on Google Cloud. A move pays for the work it takes off you, such as patching, backups and a machine that can fail, not on the bill.

## Sources

1. [Deploy services using Compose, Cloud Run (one Cloud Run service with multiple containers; a subset of Compose features; 1 maximum instance by default; not a replacement for infrastructure as code in production, Terraform recommended; the supported fields table, cpus mapping, x-google-cloudrun extensions, secrets named SERVICE_NAME-REGION-SECRET_NAME, one bucket per deployment, /etc/hosts entries for service names; page of Oct 9, read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/deploy-run-compose)
2. [Cloud Run release notes, Google (deploying with a Compose file in Preview Nov 13, 2025; generally available Mar 25, 2026; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/release-notes)
3. [Deploy container images to Cloud Run services, Google (sidecars: one ingress container, up to 10 containers per instance, containers share the network namespace and reach each other on localhost; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/deploying)
4. [Configure containers for services, Cloud Run (--container and --depends-on; with sidecars the ingress container needs an explicit port and only one container may expose one; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/services/containers)
5. [Container runtime contract, Cloud Run (the ingress container listens on 0.0.0.0 at PORT, which Cloud Run injects; CPU is allocated to all containers while the revision processes a request; worker pools always allocate CPU; SIGTERM, then SIGKILL 10 seconds later; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/container-contract)
6. [Cloud Run pricing, Google (Tier 1: request-based $0.000024 per vCPU-second, $0.0000025 per GiB-second, $0.40 per million requests; instance-based $0.000018 and $0.000002; worker pools $0.000011244 and $0.000001235; free tiers per billing account; read Oct 11, 2026)](https://cloud.google.com/run/pricing)
7. [Billing settings for services, Cloud Run (request-based billing allocates CPU only during requests; instance-based billing with --no-cpu-throttling needs at least 512 MiB; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/billing-settings)
8. [Configure container health checks for services, Cloud Run (HTTP, TCP and gRPC startup and liveness probes; a startup probe may take up to 600 seconds; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/healthchecks)
9. [Configure in-memory volume mounts for services, Cloud Run (data in the volume uses the containers' memory; the default limit is half the total memory of all containers; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/services/in-memory-volume-mounts)
10. [Cloud Storage volume mounts for services, Cloud Run (no file locking, the last write wins, not fully POSIX; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/configuring/services/cloud-storage-volume-mounts)
11. [Cloud Storage FUSE overview, Google (not POSIX compliant; no file locking or file patching; read Oct 11, 2026)](https://docs.cloud.google.com/storage/docs/cloud-storage-fuse/overview)
12. [Cloud Storage pricing, Google (Standard storage in a region such as europe-west1 $0.020 per GiB a month; read Oct 11, 2026)](https://cloud.google.com/storage/pricing)
13. [Memorystore for Redis pricing, Google (Basic tier M1, 1 to 4 GiB: $0.049 per GiB-hour in Iowa, the page's default region; read Oct 11, 2026)](https://cloud.google.com/memorystore/docs/redis/pricing)
14. [Memorystore for Valkey pricing, Google (shared-core-nano node, 1.4 GB: $0.0318 an hour in Iowa; read Oct 11, 2026)](https://cloud.google.com/memorystore/valkey/pricing)
15. [Instance and node specification, Memorystore for Valkey (shared-core-nano has no SLA and is recommended only for development or testing; read Oct 11, 2026)](https://docs.cloud.google.com/memorystore/docs/valkey/instance-node-specification)
16. [Connect to a Redis instance from a Cloud Run service, Memorystore (Direct VPC egress recommended; read Oct 11, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/connect-redis-instance-cloud-run)
17. [Cloud Tasks pricing, Google (first 1 million billable operations a month free, then $0.40 per million; read Oct 11, 2026)](https://cloud.google.com/tasks/pricing)
18. [Cloud Tasks REST reference, Task (HTTP tasks: dispatch deadline 10 minutes by default, between 15 seconds and 30 minutes; an OIDC token can be attached for Cloud Run; read Oct 11, 2026)](https://docs.cloud.google.com/tasks/docs/reference/rest/v2/projects.locations.queues.tasks)
19. [Instances, Memorystore for Redis (Basic and Standard tiers; Standard provides high availability with automatic failover; read Oct 11, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/instances)
20. [Authenticating service-to-service, Cloud Run (the calling service sends a Google-signed ID token; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/authenticating/service-to-service)
21. [Restrict network ingress, Cloud Run (all, internal, internal-and-cloud-load-balancing; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/securing/ingress)
22. [Control startup and shutdown order in Compose, Docker docs (depends_on with service_started, service_healthy, service_completed_successfully; read Oct 11, 2026)](https://docs.docker.com/compose/how-tos/startup-order)
23. [Manage secrets securely in Docker Compose, Docker docs (secrets mounted as files at /run/secrets/<secret_name>; read Oct 11, 2026)](https://docs.docker.com/compose/how-tos/use-secrets)
24. [Define services in Docker Compose, Docker docs (depends_on, env_file, healthcheck; read Oct 11, 2026)](https://docs.docker.com/reference/compose-file/services)
25. [Moving from a VPS to Cloud Run, Exodus (Hetzner CX23 with IPv4 and backups $8.39 a month; the .env pipe, cron, nginx and the switch; Oct 11, 2026)](https://exodus.elim.io/blog/a-vps-to-cloud-run)
26. [Cloud SQL's smallest instances and what they cost, Exodus (db-f1-micro $9.37 a month with 10 GiB of SSD; Oct 11, 2026)](https://exodus.elim.io/blog/cloud-sql-smallest-instances-and-what-they-cost)
27. [Egress on Google Cloud explained, Exodus (Cloud Run to users in Europe at $0.105 per GiB; Oct 11, 2026)](https://exodus.elim.io/blog/egress-on-google-cloud-explained)
28. [Cloud Run services, jobs and worker pools, Exodus (a worker pool with one instance of 1 vCPU and 512 MiB, $31.17 a month; Oct 11, 2026)](https://exodus.elim.io/blog/cloud-run-services-jobs-and-worker-pools)
29. Exodus team: europe-west1 prices read from the Cloud Billing Catalog API (Memorystore for Redis Basic M1 in Belgium $0.051 per GiB-hour, Valkey shared-core-nano $0.035 an hour, worker pools at the Iowa price); a sample compose project translated with the run-compose component of gcloud 587.0.0 and its output validated with gcloud run services replace --dry-run against our project; the compose up source read; docker compose config and a pg_dump through docker compose exec run in Docker 29.3 with Compose 5.1; gcloud help output; run on Oct 11, 2026; the cost model and its arithmetic
