This is a runbook for moving one Cloud Run service onto a Levelrail instance you run yourself. It maps only to features that exist in this repository today. Where something is unverified or has no equivalent, it says so.
Levelrail has no stable release yet (see feature status). You operate the server it runs on: OS updates, disk, backups and the Docker daemon are yours. Run the new deployment in parallel with Cloud Run, and keep Cloud Run as the rollback path until the shadow run in section 7 is clean.
There is no importer for Cloud Run. Everything below is manual. A Cloud Run service is already a container image listening on a port, which is the unit Levelrail deploys, so most services move without a rebuild. Whether the move saves money is a separate question, covered in Cloud Run alternative.
1. Inventory what Cloud Run holds
Collect this before touching Levelrail. Much of it lives in the Google Cloud console or in the service’s revision template, not in your repo.
2. Map Cloud Run concepts to Levelrail
3. Deploy the same container image
If the service already runs from an image in a registry, you can deploy that image as is.
Public image
--port is the port your container listens on. Cloud Run tells your container which port to bind through the PORT environment variable. Set the same value as a plain env var so the app behaves the same here:
where prod.env contains PORT=8080 along with your other plain variables.
Private image in Artifact Registry
Create a registry credential first. Artifact Registry accepts a service account key as the password with the username _json_key, and the host is REGION-docker.pkg.dev:
Create a service account that has only the Artifact Registry Reader role for this, and keep the key out of your shell history. Check the credential before the first deploy:
Then reference it from an app.yaml and create the app from the file:
A { secret: true, required: true } variable must be supplied with --secret at creation, otherwise the CLI refuses to create the app. The field reference is in app.yaml reference, and the registry credential steps are in Backup targets, registry credentials and app volume backups.
Image not reachable from your server
If you would rather not give the server a Google credential, pull the image where you have access, retag it and push it to a registry the server can reach:
Image deploys do not rebuild anything. If you want Levelrail to build from your repo instead, that is the git flow in Deploying apps.
Redeploy and roll back
4. Env vars, secrets, health checks
-
Plain env. Copy the non-secret entries from the exported service YAML into a
.env file, then apps env import <app> --file prod.env --dry-run, then again without --dry-run.
-
Secrets. Read each value from Secret Manager and set it as a Levelrail secret:
For many keys write a
secrets.env file and use apps secrets set web --env-file secrets.env. Delete the file afterwards. There is no live link to Secret Manager: rotating a secret in Google does not change it here.
-
Apply.
apps apply web restarts the app so saved env and secrets reach the running container.
-
Secret files. Cloud Run can mount a secret as a file. Levelrail injects secrets as env vars. If your code reads a file path, either change it to read the env var or write the file from the env var in your entrypoint.
-
Health. A Cloud Run startup probe becomes a readiness probe, and a liveness probe stays a liveness probe:
A deploy that fails readiness does not take over traffic: the ingress keeps routing to the previous release and the attempt is recorded as failed. Cloud Run’s default TCP startup check has no direct counterpart, so add a real
/healthz route.
5. Move Cloud SQL data
A managed database on Levelrail is a container on your node with its own volume, credentials and scheduled backups. It is not a hosted service with a replica in another zone. Read Managing databases before you commit.
-
Create the database with a matching major version:
-
Dump from Cloud SQL. Use the Cloud SQL Auth Proxy or an authorized network, then:
Alternatively use
gcloud sql export sql INSTANCE gs://BUCKET/appdb.sql --database=appdb and download the file.
-
Restore into the new database. Publish the database port temporarily, then restore with
pg_restore using the database’s own credentials:
The credentials are the ones Levelrail generated for the database (see the Overview tab and levelrail-cli databases get main). A temporary login from databases access temp with --preset read_write cannot change the schema, so it is not enough for a first restore. Clear public access when you are done.
-
Connect the app to the database instead of pasting a URL:
-
Rehearse the cutover. A dump is a point in time. For a live database, plan a short write freeze: stop writes on Cloud Run, take a final dump, restore, switch. Time the restore before the real day.
-
Backups. Configure a backup target and a schedule, then run a restore into a new database to prove it works. See Backup targets.
Cloud SQL’s automatic failover, read replicas and point-in-time recovery are not replicated by default here. Point-in-time restore exists for Postgres, see Managing databases.
6. Domains, jobs, logs and metrics
Domains and certificates
-
Add the domain to the app while DNS still points at Google:
-
Test before you move DNS, with a host override:
-
Lower the DNS TTL a day ahead, then change the A and AAAA records to the server’s public IP. Cloud Run domain mappings point at Google’s records, so those are replaced, not edited.
-
Certificates: with ACME enabled, issuance needs the domain to already point at the server, so expect a short window, or upload a certificate for the domain first. ACME has one recorded live run, see the ACME verification runbook.
-
Keep the Cloud Run service and its domain mapping untouched until the shadow run is clean. Restoring the old DNS records is the rollback.
Scheduled jobs
A Cloud Scheduler job that POSTs to /cron/cleanup has no direct equivalent, because Levelrail runs a command, not an HTTP request. Options:
- Put the work behind a command your image already contains and schedule that:
levelrail-cli apps scheduled-tasks create web --schedule "0 3 * * *" -- ./cleanup.sh.
- Keep the HTTP route and call it from a command that exists in the image, for example
curl if it is installed. Confirm the tool exists before relying on it.
Run each once with levelrail-cli apps scheduled-tasks run web <id> and check last_run_status. More in Scheduled jobs. Cloud Run Jobs (batch tasks) can use the jobs: block, which runs a command in a fresh container of the service’s image.
Logs and metrics
Logs and metrics are stored on the node. If you have alerts or dashboards in Google Cloud Monitoring, rebuild them as Levelrail alert rules, see Observability. Structured JSON logs written to stdout work as they do on Cloud Run, but the field names Google uses for severity and trace are not interpreted specially.
7. Cutover and shadow run
Run both platforms side by side long enough for boring problems to appear. Suggested minimum: one full business cycle, including any weekly or monthly job.
Decommission the Cloud Run service only after this passes and you have decided what to do about each item in the next section.
8. What you give up
See also
Last modified on October 12, 2026