# Moving an app from DigitalOcean App Platform to Cloud Run

> App Platform bills each container all month with a traffic allowance; Cloud Run bills while requests run. What each part of the app spec becomes, the commands, and the bill on both sides.

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

Moving from DigitalOcean App Platform to Cloud Run turns each part of the app spec into a Google Cloud resource: services into Cloud Run services, workers into worker pools, its jobs into Cloud Run jobs, the database into Cloud SQL. The bill changes shape: App Platform charges $5 to $392 a month per container, Cloud Run only while requests run, so idle apps get cheaper and busy ones dearer.

## In short

- App Platform's smallest container is $5 a month with 50 GiB of traffic and $0.02 per GiB past it; Cloud Run charges $0.105 per GiB to users in Europe, with no allowance.
- In our model Cloud Run costs $0.49 against $5.00 at 100,000 requests a month, $14.61 against $20.00 at 1 million with Postgres, and $146.27 against $36.63 at 10 million and 1,000 GB.
- Cloud Run kills a container 10 seconds after SIGTERM; App Platform waits 120 by default.
- Encrypted `EV[1:...]` variables cannot be decrypted outside DigitalOcean, so values come from their source or the running container.
- Scheduled jobs move to Cloud Scheduler, $0.10 a job a month after 3 free, with no 15-minute minimum.

Prices are list prices of October 11, 2026, Cloud Run in europe-west1.

## When moving pays, and when it does not

App Platform's price is per container. The smallest, `apps-s-1vcpu-0.5gb`, is $5 a month with 50 GiB of outbound traffic; `apps-s-1vcpu-1gb` is $12 with 150 GiB; dedicated-CPU plans run from $29 to $392 with up to 900 GiB. The $5 and $10 plans are fixed: one container, no scaling. Traffic past the allowance costs $0.02 per GiB, and the allowance is pooled across every app of your team. Containers are billed by the second, so a container that runs all month costs its full price. Cloud Run charges for the seconds your code handles requests and starts no instance when there are none.

Cloud Run's [free tier](https://exodus.elim.io/blog/the-cloud-run-free-tier-explained) is per billing account: 180,000 vCPU-seconds, 360,000 GiB-seconds and 2 million requests a month. Past it, 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). Traffic to users in Europe is $0.105 per GiB from the first gigabyte, five times App Platform's overage price, with no allowance; the [egress guide](https://exodus.elim.io/blog/egress-on-google-cloud-explained) explains the lines.

![Six bars: at 100,000 requests and 5 GB, App Platform costs $5.00 and Cloud Run $0.49; at 1 million requests and 50 GB with the smallest managed Postgres, App Platform $20.00 and Cloud Run with Cloud SQL $14.61; at 10 million requests and 1,000 GB, App Platform $36.63 and Cloud Run $146.27](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-e77774584682f3620a1ff54a858ce47360ba2da1305be03302d7d8eba9969479-680x360-svg/bytes "One app on App Platform 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; the Cloud Run service has 1 vCPU and 512 MiB.

- **100,000 requests and 5 GB a month.** App Platform: one $5 container. Cloud Run: the compute stays inside the free tier, and 4.7 GiB of egress costs $0.49.
- **1 million requests and 50 GB, plus Postgres.** App Platform: the $5 container, whose 50 GiB allowance covers the traffic, and a single-node managed Postgres with 1 GiB at $15: $20.00. Cloud Run: $0.48 of CPU and $4.89 of egress, plus a `db-f1-micro` Cloud SQL instance with 10 GiB of SSD at $9.24 (720 hours here; the [Cloud SQL guide](https://exodus.elim.io/blog/cloud-sql-smallest-instances-and-what-they-cost) counts 730 hours and gets $9.37): $14.61. The micro instance has 0.6 GB of memory and is outside Cloud SQL's SLA; that guide has every size.
- **10 million requests and 1,000 GB.** App Platform: two $12 containers, so a deploy or a crash never leaves the app with none, and their 300 GiB allowance; the other 631 GiB cost $12.63: $36.63. 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, the CPU line falls; the egress line does not.

So Cloud Run is cheaper for an app that is idle most of the day, and App Platform is cheaper for an app that is busy all day or sends a lot of traffic. Other reasons to move are not on the chart: the rest of your system is on Google Cloud; you want Cloud SQL, Pub/Sub, BigQuery or IAM next to the app; you need a region App Platform lacks (it lists 13, Cloud Run runs in 43); or you want instances that appear with traffic instead of a count you pay for all month. Both platforms promise 99.95% a month. If none of these is your reason, the move costs time and saves nothing, and Exodus says so in its verdict.

## What App Platform did for you

Everything App Platform runs is described in one file, the app spec. Download the live one, because the console may have changed it since the repository's copy was written:

```shell
doctl apps list --format ID,Spec.Name
doctl apps spec get APP_ID > app.yaml
```

A typical spec has a web service, a worker, a migration that runs before each deploy, a nightly job, a static site and a database:

```yaml
name: shop
region: fra
services:
- name: web
  github: {repo: acme/shop, branch: main}
  http_port: 8080
  instance_size_slug: apps-s-1vcpu-1gb
  instance_count: 2
  envs:
  - {key: DATABASE_URL, value: "${db.DATABASE_URL}"}
  - {key: STRIPE_KEY, type: SECRET, value: "EV[1:...]"}
workers:
- name: queue
  github: {repo: acme/shop, branch: main}
  run_command: node worker.js
jobs:
- name: migrate
  kind: PRE_DEPLOY
  run_command: npx prisma migrate deploy
- name: nightly
  kind: SCHEDULED
  schedule: {cron: "0 3 * * *", time_zone: Europe/Berlin}
  run_command: node report.js
static_sites:
- name: docs
  output_dir: dist
databases:
- {name: db, engine: PG, production: true, cluster_name: shop-db}
```

Each component needs a home on Google Cloud, and some of them become more than one thing.

![Ten pairs: services become Cloud Run services; workers become worker pools; PRE_DEPLOY jobs become a Cloud Run job run between build and deploy; SCHEDULED jobs become a job started by Cloud Scheduler; static sites become Firebase Hosting; databases become Cloud SQL; SECRET variables become Secret Manager; Spaces buckets become Cloud Storage; internal URLs, ingress routes and domains become service URLs with ID tokens and a load balancer or Firebase Hosting](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-f4a86075675c7cdce924274508acf97a67e8b2b04e4d924af763e02a532c3f48-680x604-svg/bytes "Where each part of an App Platform app goes on Google Cloud")

## Step 1: the container

If the repository has a Dockerfile, App Platform built it, and Cloud Run builds the same file. Without one, App Platform used Cloud Native Buildpacks, for Node.js the Heroku buildpack; `gcloud run deploy --source .` uses Google's buildpacks, which detect the same languages and read a `Procfile` if you have one. If the spec sets `run_command` or `build_command`, put them in a `Procfile` (`web: npm start`) or in `package.json` scripts, because Cloud Run does not read the spec. The [buildpacks guide](https://exodus.elim.io/blog/buildpacks-or-a-dockerfile) covers the choice.

Three runtime details differ.

**The port.** App Platform sends traffic to `http_port`, 8080 by default, and sets `PORT` to it. Cloud Run does the same with 8080 and `PORT`; deploy with `--port` if the app listens elsewhere. Both need the app to listen on `0.0.0.0`.

**The stop signal.** Both platforms send `SIGTERM` before they stop a container. App Platform then waits `grace_period_seconds`, 120 by default and up to 600, before `SIGKILL`. Cloud Run waits 10 seconds, and bills the CPU meanwhile. Code that finishes a long job after `SIGTERM` worked on App Platform and is killed halfway on Cloud Run. Keep the shutdown under 10 seconds and move long work into jobs, which the [Fly guide](https://exodus.elim.io/blog/fly-to-cloud-run) shows with a handler tested in Docker.

**The disk.** App Platform gives each container up to 4 GiB of local disk, lost on every deploy. On Cloud Run the writable file system lives in memory: a file of 300 MB written to `/tmp` takes 300 MB of the instance's memory until it is deleted. Code that downloads large files to disk needs more memory, or streams.

## Step 2: environment variables and secrets

Plain variables (`type: GENERAL`) are in the spec in clear text; they go on the service with `--set-env-vars`.

Encrypted variables are not. App Platform encrypts a `SECRET` value when you save it and keeps only the encrypted form, `EV[1:...]`, in the spec; nothing decrypts it outside DigitalOcean. The value exists in clear text in one place: the running container. DigitalOcean's documentation says plainly that team members with console access can read it there. So the values come either from where they were made (the payment provider's dashboard, the OAuth app, the database) or from the console.

From the console, `doctl apps console APP_ID web` opens a shell in the web container, and `printenv STRIPE_KEY` prints the value on your screen. On your own machine, read it without echo and pipe it into Secret Manager, so it never lands in a file or in your shell history:

```shell
gcloud iam service-accounts create web
read -rs VALUE && printf %s "$VALUE" | gcloud secrets create STRIPE_KEY --data-file=- ; unset VALUE
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
```

`read -rs` waits for you to paste the value and press Enter, without showing it; `printf %s` adds no newline. The last command prints only the length, to compare with the original. Anyone who could open the console could read these values all along, so a move is a good moment to rotate the ones that matter.

**Bindable variables** need real values. `${db.DATABASE_URL}`, `${db.HOSTNAME}`, `${APP_URL}`, `${APP_DOMAIN}` and the like are filled in by App Platform at deploy time; on Cloud Run they are just strings. Search the spec for `${` and give each one a value: the Cloud SQL connection string (Step 6), the service's URL, the domain.

## Step 3: deploy and scaling

Pick the region next to the database. For an app in `fra`, `europe-west3` is Frankfurt; for `ams`, `europe-west4` is the Netherlands; `europe-west1` in Belgium is a Tier 1 region with the prices used above. 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=1Gi \
  --max-instances=10 \
  --set-env-vars=NODE_ENV=production \
  --set-secrets=STRIPE_KEY=STRIPE_KEY:latest
```

`--source .` builds the app 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 onto different defaults:

- **Instance count.** `instance_count` was the number of containers you paid for, all month. Cloud Run has no such number: it starts instances as requests arrive, up to 100 per service by default, and stops them when traffic ends. Set `--max-instances` to the most you will pay for, and to protect a database with a fixed number of connections.
- **Autoscaling.** `autoscaling` with `min_instance_count` becomes `--min-instances`. On App Platform the minimum is at least 1; on Cloud Run 0 is the default, and each instance kept warm is billed at the idle rate. An app that must answer the first request quickly keeps one.
- **Scale to Zero**, App Platform's inactivity sleep, is labeled "private" in DigitalOcean's docs, so check that your account has it, and bills a sleeping service at 10% of its price. On Cloud Run an idle service with request-based billing costs nothing.
- **Size.** `instance_size_slug` becomes `--cpu` and `--memory`.

![A table comparing App Platform and Cloud Run: idle app, more instances, stop signal, local disk, protocols, cron, regions and traffic out](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-2b7fb6bc69a1ebc2ed971a1f494743b6e570d27ae55f95207f635518e0b05a41-680x528-svg/bytes "What changes in a move, from DigitalOcean's and Google's pages of October 11, 2026")

## Step 4: workers, deploy jobs and cron

**Workers** are App Platform components that take no HTTP traffic and run all the time, usually a queue consumer. On Cloud Run that is a worker pool, deployed from the same image, with a fixed number of instances that run all the time, like the worker did:

```shell
gcloud run worker-pools deploy queue --source . --region europe-west1 \
  --command=node --args=worker.js --instances=1 \
  --service-account=web@PROJECT_ID.iam.gserviceaccount.com
```

Putting the worker inside the web service does not work: with request-based billing it gets no CPU between requests, and it runs on every instance. The [services, jobs and worker pools guide](https://exodus.elim.io/blog/cloud-run-services-jobs-and-worker-pools) explains the three kinds.

**Deploy jobs.** `PRE_DEPLOY` jobs ran a migration before App Platform released the new version. On Cloud Run the same command becomes a Cloud Run job, run between the build and the deploy. The [Fly guide](https://exodus.elim.io/blog/fly-to-cloud-run) has the whole sequence (`gcloud builds submit`, `jobs deploy`, `run deploy --image`); for this spec the job step is, with `IMAGE` the tag built in that sequence:

```shell
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=npx --args=prisma,migrate,deploy \
  --execute-now --wait
```

`--wait` makes the command fail when the migration fails, so a script or CI step stops before the deploy, as App Platform did. `POST_DEPLOY` jobs become a step after the deploy, and `FAILED_DEPLOY` jobs a step your CI runs when the deploy fails.

**Scheduled jobs.** `SCHEDULED` jobs become a Cloud Run job started by Cloud Scheduler. App Platform runs a schedule every 15 minutes at most; Scheduler takes any unix-cron line and its own time zone per job (`--time-zone=Europe/Berlin`). It costs $0.10 a job a month, with 3 jobs free per billing account. The [Fly guide](https://exodus.elim.io/blog/fly-to-cloud-run) has the `gcloud scheduler jobs create http` command that calls a job's `:run` endpoint.

## Step 5: static sites

App Platform serves static sites from a CDN, free for up to three static-only apps and free inside a paid app. Cloud Run can serve the files from the app's container, but every request then counts as a request and as egress. Firebase Hosting is the closer match: a CDN for the files, free up to 10 GB stored and 360 MB a day transferred, with rewrites that send some paths to a Cloud Run service:

```json
{
  "hosting": {
    "public": "dist",
    "rewrites": [{"source": "/api/**", "run": {"serviceId": "web", "region": "europe-west1"}}]
  }
}
```

`firebase deploy --only hosting` uploads the folder. The same file replaces App Platform's ingress rules that sent `/api` to one component and `/` to another.

## Step 6: the database

App Platform has two kinds. A dev database is a small Postgres for development, $7 a month; a managed database is a DigitalOcean Managed Databases cluster, from $15 a month for a single node with 1 GiB, or $30 plus a $30 standby for high availability. A note for planning: from October 15, 2026 new accounts, and from November 30 all accounts, cannot add standby nodes to new Standard Edition clusters; existing clusters keep theirs. Both kinds move to Cloud SQL the same way, with `pg_dump` and `pg_restore` during a short write freeze. The guides for [a VPS](https://exodus.elim.io/blog/a-vps-to-cloud-run), [Docker Compose](https://exodus.elim.io/blog/docker-compose-to-cloud-run) and [ECS on Fargate](https://exodus.elim.io/blog/ecs-fargate-to-cloud-run) restore their dumps with the commands below.

Create the Cloud SQL instance and database first. Managed clusters accept connections only from trusted sources, so add your computer's address in the cluster's Network Access tab or with `doctl databases firewalls append CLUSTER_ID --rule ip_addr:YOUR_IP`. Stop writes (scale the app's workers to zero and put the app in maintenance), then dump over TLS with the host, port and user from the cluster's Connection Details:

```shell
PGSSLMODE=require pg_dump -Fc --no-owner --no-acl \
  -h HOST -p PORT -U doadmin -d defaultdb -f app.dump
```

`pg_dump` asks for the password, so it never sits in a command line. Then restore through the Cloud SQL Auth Proxy, in a second 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
```

Check row counts on a few tables on both sides. On Cloud Run the service connects through a Unix socket: deploy with `--add-cloudsql-instances=PROJECT_ID:europe-west1:INSTANCE`, give the service account the Cloud SQL Client role, and set `DATABASE_URL` to `postgresql://USER:PASSWORD@/DB?host=/cloudsql/PROJECT_ID:europe-west1:INSTANCE`, stored in Secret Manager because it holds the password.

## Step 7: Spaces buckets

Spaces speaks the S3 API, and `gcloud storage` can read from any S3-compatible service. Create a Spaces access key, put it in `~/.aws/credentials` with an editor (`aws_access_key_id` and `aws_secret_access_key` under `[default]`), point gcloud at your Spaces region, and copy:

```shell
gcloud storage buckets create gs://BUCKET --location=europe-west1
gcloud config set storage/s3_endpoint_url https://fra1.digitaloceanspaces.com
gcloud storage rsync s3://SPACE gs://BUCKET --recursive
gcloud config unset storage/s3_endpoint_url
```

![A terminal: gcloud points storage/s3_endpoint_url at a local S3-compatible server, lists three objects in s3://uploads, copies them with gcloud storage rsync, and a second rsync copies nothing and exits 0](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-38efc58b50059a8aaa323d3307700b394d1d438ac1c1dbdcb091a25395ef1b7b-680x396-svg/bytes "The same commands on our machine, October 11, 2026, against a local S3-compatible server standing in for Spaces and a local folder standing in for the bucket. Progress lines are cut")

We ran these commands against a local S3-compatible server. `rsync` copies only what changed, so run it once ahead of the move and again during the write freeze. Unset the endpoint afterwards, or every later `s3://` command goes to DigitalOcean. Large buckets can go through Storage Transfer Service instead.

Check what serving the files will cost. Spaces includes 1,024 GiB of outbound traffic for $5 a month and charges $0.01 per GiB past it; Cloud Storage serving files to users in Europe is internet egress at Google's rates. An app that serves a lot of images or video may be better off leaving them on Spaces, or behind a CDN, and moving only the app.

## Step 8: internal routing and the domain

Inside an app, components reached each other at `http://web` over App Platform's private network, with no authentication. Cloud Run has no such network. Each service has its own URL; a private one gets `--ingress=internal` and no `--allow-unauthenticated`, the caller sends a Google-signed ID token with the receiving service's URL as audience, and the caller's service account gets `roles/run.invoker` on the receiver. Search the code for `http://` followed by component names, and give each an environment variable holding the new URL.

App Platform issued the certificate for your domain. On Google Cloud, Firebase Hosting can carry the domain and route paths to services (Step 5); a global external Application Load Balancer does the same with more control; Cloud Run's own domain mapping is in Preview. [Custom domains and HTTPS on Cloud Run](https://exodus.elim.io/blog/custom-domains-and-https-on-cloud-run) compares the three and their costs.

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. App Platform adds three things:

1. Test every component on the `run.app` URL: sign-in, a payment in test mode, an upload, the scheduled job run by hand.
1. Before the DNS change, freeze writes, copy the database (Step 6) and the buckets again (Step 7), point the services at the new ones and deploy.
1. Keep the App Platform app for the week. Archiving it (`maintenance: archive: true` in the spec) stops the components' charges; the database and dedicated IPs are still billed. Then delete the app, the database and the Spaces.

## What breaks most often

- Shutdown code that took a minute on App Platform is killed after 10 seconds.
- A bindable variable such as `${db.DATABASE_URL}` reaches the app as a literal string.
- A `run_command` from the spec is lost, and the buildpack starts the wrong process.
- Large files written to `/tmp` use up the instance's memory.
- A worker copied into the web service gets no CPU between requests.
- Calls to `http://api` fail, because that name does not exist on Google Cloud.
- No `--max-instances`, and a traffic spike opens more database connections than Postgres allows.
- The endpoint setting stays on, and an unrelated `s3://` command reaches DigitalOcean.

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 the app spec, the Dockerfile or buildpack files and the code on your machine, with your coding agent reading the parts that need judgement. The report lists every component from the picture above with what it becomes: services, workers as worker pools, deploy jobs and their place in the pipeline, scheduled jobs, static sites, bindable variables to replace, internal URLs, Spaces buckets, shutdown handling. It estimates the month on Cloud Run for your traffic next to what App Platform charges now, egress and database included, and says plainly when staying is cheaper.

## FAQ

### Is Cloud Run cheaper than DigitalOcean App Platform?

For an app that is idle most of the day, yes; for a busy app or heavy traffic, no. In our model Cloud Run costs $0.49 against $5.00 at 100,000 requests a month, but $146.27 against $36.63 at 10 million requests and 1,000 GB, because egress has no allowance.

### How do I get secret environment variables out of App Platform?

App Platform keeps only the encrypted form of a SECRET variable in the spec, and nothing decrypts it outside DigitalOcean. Take each value from where it was made, or read it in the running container through doctl apps console, then pipe it into Secret Manager without writing it to a file.

### What replaces App Platform workers on Cloud Run?

A worker pool, deployed from the same image with a fixed number of instances that run all the time. Putting the worker inside the web service does not work: with request-based billing it gets no CPU between requests, and it runs on every instance.

### Where do App Platform static sites go on Google Cloud?

Firebase Hosting is the closer match: a CDN for the files, free up to 10 GB stored and 360 MB a day transferred, with rewrites that send paths such as /api to a Cloud Run service. Serving the files from the app's container counts every file as a request and as egress.

## Sources

1. [App Platform pricing, DigitalOcean docs (per-container plans: apps-s-1vcpu-0.5gb $5 with 50 GiB, apps-s-1vcpu-1gb $12 with 150 GiB, up to apps-d-8vcpu-32gb $392 with 900 GiB; the $5 and $10 fixed plans run one container only; billed by the second with a one-minute minimum; outbound transfer past the allowance $0.02 per GiB, the allowance pooled across all apps of a team; dev database $7 a month; three static-site apps free; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/details/pricing/)
2. [App Platform pricing page, DigitalOcean (free tier for static sites, paid tier from $5 a month; autoscaling FAQ; read Oct 11, 2026)](https://www.digitalocean.com/pricing/app-platform)
3. [App spec reference, DigitalOcean docs (services, workers, jobs with kind PRE_DEPLOY, POST_DEPLOY, FAILED_DEPLOY and SCHEDULED, static_sites, databases, envs of type SECRET encrypted on first submission, http_port 8080 by default and PORT set from it, instance_count up to 250, termination; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/reference/app-spec/)
4. [How to use environment variables in App Platform, DigitalOcean docs (encrypted values are decrypted in the runtime environment and visible through the console; bindable variables such as ${db.DATABASE_URL}; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/)
5. [How to scale apps in App Platform, DigitalOcean docs (fixed count 1 to 250; CPU-based autoscaling needs a dedicated CPU plan; request-based autoscaling up to 100 containers; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/scale-app/)
6. [How to configure Scale to Zero in App Platform, DigitalOcean docs (marked private; a sleeping component billed at 10% of its price; inactivity 600 to 86,400 seconds; web services only; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/scale-to-zero/)
7. [How to configure app termination settings, DigitalOcean docs (drain_seconds 15 by default, 110 at most; grace_period_seconds between SIGTERM and SIGKILL 120 by default, 600 at most; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/configure-termination/)
8. [App Platform limits, DigitalOcean docs (local filesystem 4 GiB and lost on deploy; HTTP/2 downgraded to HTTP/1.1 unless configured; no gRPC; no IPv6 outbound; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/details/limits/)
9. [How to manage cron jobs and deployment jobs, DigitalOcean docs (scheduled jobs every 15 minutes at most, billed only while running; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/manage-jobs/)
10. [How to manage internal routing, DigitalOcean docs (components reach a service at http://name over the app's network; internal_ports; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/manage-internal-routing/)
11. [How to manage databases in App Platform, DigitalOcean docs (dev databases for development, managed databases for production, trusted sources; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/manage-databases/)
12. [App Platform buildpacks, DigitalOcean docs (a Dockerfile wins when present, otherwise Cloud Native Buildpacks; Node.js uses heroku-buildpack-nodejs; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/reference/buildpacks/)
13. [App Platform availability, DigitalOcean docs (13 regions in the availability table: NYC, AMS, SFO, SGP, LON, FRA, TOR, BLR, SYD, ATL, RIC, MKC, MEM; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/details/availability/)
14. [App Platform SLA, DigitalOcean (99.95% a month, commercially reasonable efforts; read Oct 11, 2026)](https://www.digitalocean.com/sla/app-platform)
15. [PostgreSQL pricing, DigitalOcean docs (single node from $15 a month with 1 GiB; high availability from $30 plus a $30 standby; from Oct 15, 2026 for new accounts new Standard Edition clusters cannot add standby nodes; read Oct 11, 2026)](https://docs.digitalocean.com/products/databases/postgresql/details/pricing/)
16. [How to connect to PostgreSQL clusters, DigitalOcean docs (TLS required, PGSSLMODE=require, add your computer to trusted sources; read Oct 11, 2026)](https://docs.digitalocean.com/products/databases/postgresql/how-to/connect/)
17. [Spaces pricing, DigitalOcean docs ($5 a month with 250 GiB stored and 1,024 GiB out, then $0.02 per GiB stored and $0.01 per GiB out; read Oct 11, 2026)](https://docs.digitalocean.com/products/spaces/details/pricing/)
18. [Spaces API reference, DigitalOcean docs (S3-compatible endpoint REGION.digitaloceanspaces.com with a Spaces key pair; read Oct 11, 2026)](https://docs.digitalocean.com/reference/api/spaces/)
19. [How to archive and restore an app, DigitalOcean docs (maintenance archive stops the components' charges; databases and dedicated egress IPs are still billed; read Oct 11, 2026)](https://docs.digitalocean.com/products/app-platform/how-to/archive-restore/)
20. [Storage Transfer Service pricing, Google (agent-based transfers from S3-compatible storage at no charge; read Oct 11, 2026)](https://cloud.google.com/storage-transfer/pricing)
21. [doctl apps reference, DigitalOcean docs (apps spec get, apps console, apps update; read Oct 11, 2026)](https://docs.digitalocean.com/reference/doctl/reference/apps/)
22. [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)
23. [Egress on Google Cloud explained, Exodus (Cloud Run to users in Europe at $0.105 per GiB, the SKU Europe to Europe; Oct 11, 2026)](https://exodus.elim.io/blog/egress-on-google-cloud-explained)
24. [Cloud SQL pricing, Google (db-f1-micro $0.0105 an hour; SSD storage $0.000232877 per GiB-hour; shared-core machines outside the SLA; read Oct 11, 2026)](https://cloud.google.com/sql/pricing)
25. [Container runtime contract, Cloud Run (listen on 0.0.0.0 and PORT; in-memory file system; SIGTERM, then SIGKILL after a 10 second period during which CPU is billed; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/container-contract)
26. [Cloud Run quotas and limits (100 instances by default per project and region; read Oct 11, 2026)](https://docs.cloud.google.com/run/quotas)
27. [About Procfiles, Google Cloud buildpacks (a Procfile sets the start process for any language; read Oct 11, 2026)](https://docs.cloud.google.com/docs/buildpacks/about-procfile)
28. [Connect from Cloud Run to Cloud SQL for PostgreSQL (--add-cloudsql-instances, the Unix socket under /cloudsql/, the Cloud SQL Client role; read Oct 11, 2026)](https://docs.cloud.google.com/sql/docs/postgres/connect-run)
29. [Interoperability with other storage providers, Cloud Storage (gcloud storage with S3 credentials, storage/s3_endpoint_url, gcloud storage rsync from s3://; read Oct 11, 2026)](https://docs.cloud.google.com/storage/docs/interoperability)
30. [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)
31. [Firebase pricing (Hosting at no cost up to 10 GB stored and 360 MB a day transferred; read Oct 11, 2026)](https://firebase.google.com/pricing)
32. [Serve dynamic content with Cloud Run, Firebase Hosting (rewrites to a serviceId and region; read Oct 11, 2026)](https://firebase.google.com/docs/hosting/cloud-run)
33. [Map custom domains, Cloud Run (global external Application Load Balancer; Firebase Hosting; Cloud Run domain mapping in Preview; read Oct 11, 2026)](https://docs.cloud.google.com/run/docs/mapping-custom-domains)
34. [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)
35. [Cloud Run SLA, Google (99.95% a month for Cloud Run; read Oct 11, 2026)](https://cloud.google.com/run/sla)
36. [Cloud Run locations, Google (43 regions in gcloud run regions list, Oct 11, 2026)](https://docs.cloud.google.com/run/docs/locations)
37. Exodus team: doctl 1.178.0 and gcloud 587.0.0 help output; the sample app spec checked with doctl apps spec validate --schema-only; gcloud storage ls and rsync against a local S3-compatible server standing in for Spaces, run on Oct 11, 2026; the cost model and its arithmetic
