runway builds your app, creates what it needs (identity, grants, secrets, buckets, tags), rolls it out and manages its traffic, from a runway.yaml next to your code. No state file. No cluster.
macOS and Linux. Also as binaries, a container image or from source.
$ runway plan --stage prod ~ update hello-prod ~ memory: 512Mi -> 1Gi + secrets.API_KEY: api-key@3 + grant secretAccessor on api-key to hello-run $ runway deploy --stage prod --preview feature/login ==> Building hello from . (image exists → skip) ==> Updating Cloud Run service hello-prod ✓ hello-prod updated in 38s Preview: https://feature-login---hello-prod-…run.app Traffic: 100% hello-prod-00012 · 0% feature-login $ runway deploy --stage prod --traffic 10 # canary $ runway traffic --stage prod --promote
One runway.yaml, then the offline diagram, a first deploy, an exact plan, a URL per branch, a canary and pruning. Pause any time and copy what you see. Cloud output uses sample names (my-gcp-project); waits are shortened.
One configuration covers the build, the runtime identity and its least-privilege grants, secrets, storage, probes, scaling, access and traffic. Every step reads the live project first and changes only what differs.
Dockerfile or buildpacks on Cloud Build. Images are content-addressed: same source and base images, no rebuild.
--preview $BRANCH deploys a revision with its own URL and no traffic. Merge to main and the release gets the traffic.
--traffic 10, watch, runway traffic --promote. Roll back with one command. No revision churn.
Declared secrets are created empty, the right people can fill them, and the deploy waits (with the exact command) until they have a value.
A runtime service account per service, roles on exactly one dataset, bucket or secret, private by default, IAP when you want it.
Tags bound to the service and awaited until effective; a first deploy that satisfies run.allowedIngress conditions.
runway plan diffs config, image, traffic and grants against the live service, and says when something is only known at deploy time.
otel_collector: {} adds Google's OpenTelemetry Collector as a sidecar with the roles and APIs it needs. runway logs --follow.
The project is the state. Ownership labels keep runway away from what it did not create. Undeploy keeps your data.
runway.yamlStages, build, identity, secrets, access. runway init scaffolds one.
See the exact changes against the live project, read-only.
APIs, buckets, secrets, accounts, grants, build, rollout, health, traffic, access: in dependency order, each step idempotent.
Workload Identity Federation, a container image, JSON output and stable exit codes.
version: 1 app: hello provider: project: my-project region: europe-west1 enable_apis: true create_build_resources: true secrets: api-key: { adders: [group:devs@example.com] } service: source: . # Dockerfile or buildpacks health_check: { path: /healthz } service_account: "hello-run@${project}.iam.gserviceaccount.com" identity: create: true roles: - { role: roles/storage.objectUser, bucket: my-data } secrets: API_KEY: { secret: "${secrets.api-key}" } iap: { members: [group:devs@example.com] } stages: dev: {} prod: { service: { min_instances: 1 } }
Keep Terraform for shared platform resources (networks, load balancers, DNS) and Kubernetes GitOps for Kubernetes. runway takes the part that changes every day: the Cloud Run application.
| CI scripts + Terraform | GitOps (Argo CD) | runway | |
|---|---|---|---|
| What the app team maintains | CI YAML + HCL, often in several repos | CI + manifests + controller CRDs | one runway.yaml |
| Extra infrastructure | state buckets, Terraform pipeline | a cluster and controllers | none |
| Branch previews, canaries | build it yourself | Kubernetes-centric | built in |
| Secrets | created by Terraform, filled by hand | secrets operator | created, granted, checked before deploy |
| Drift | next apply | healed continuously | next plan / deploy |
Read the docs, deploy your first app, and tell us what you think.