Setting Up Cloud Run: Where Container Deployments Break
A container that starts in seconds locally fails on its very first deployment in a managed environment — a common scenario during migration.
The causes are almost always the same: environment configurations diverge, internal services are unreachable from the new network, and some settings are lost between releases.
The divergence starts with small things — environment variables, mounted secrets, service account permissions. Locally they are supplied by hand; in a managed environment they come from the service definition.
If the set differs by even one value, the application starts up but talks to the wrong database or crashes during initialization. Add memory and concurrency limits and you get an unstable start: some revisions respond, others go into a restart loop on the readiness check.
The unreachability of internal services is a story of its own. By default, the container cannot see the private network: until a network connector is attached and routes with firewall rules are defined, calls to the database, cache, and broker time out.
Release errors show up here too: traffic is switched to a new revision while it is not yet ready to accept requests — the service returns errors for several minutes.
Configuration loss during release is the third trap. Edits made by hand in the console live only until the next deployment: the service is overwritten by the definition from the repository, and variables, permissions, and environment settings are rolled back.
Below we break down how this is closed — from declarative service definitions to checks before traffic switching.
What Managed Container Execution Delivers
Managed container execution means the platform itself keeps revisions, distributes traffic between them, and spins up instances under load, while the team only describes the image, environment variables, and limits.
In Cloud Run, a release boils down to publishing a new revision: it comes up alongside the current one, passes the readiness check, and only then receives traffic.
This yields two effects at once — service downtime during updates disappears, and rollback becomes a switch of traffic to the previous revision instead of a rebuild and manual edits.
The time to release changes starts being determined by the pipeline rather than by a person: image build, checks, configuration application, gradual traffic rollout.
Along with manual deployment, an entire class of failures goes away — forgotten variables, diverged versions, running from the wrong environment: all of this is fixed in the service definition and verified before the production environment.
Autoscaling here is calculated from requests rather than from server utilization: with no traffic, the number of instances drops to zero, and during a spike it grows within predefined bounds.
The bounds are set in advance — instance limit, concurrency per instance, timeouts, memory size; otherwise the service hits the limits of the external database or the project quotas and starts returning errors during a peak.
Traffic isolation is handled separately from the application code.
Calls to internal databases and queues go through a connector into the private network, and the service itself is closed off from public access: entry only through a gateway or an internal load balancer with token verification.
Internal addresses and service accounts stop being reachable from outside, and permissions are reduced to specific roles for specific services.
Deployment Options: Service, Job, Virtual Machines
A managed environment takes scaling, node updates, and isolation off the team's hands, but in return it imposes constraints: a maximum request processing duration, no persistent local disk, behavior during idle time.
That is why the container deployment mode is chosen by load profile rather than by habit — it determines both the architecture and which failures are even possible. In Cloud Run there are three such modes, and they are not interchangeable.
| Option | When it fits | Limitations |
|---|---|---|
| Service with inbound traffic | Steady load: APIs, web applications, webhook handlers | Request duration is capped; disk is not preserved between runs; instances are shut down when idle, so the first request takes longer |
| Job | Long-running background tasks: batch processing, data migrations, nightly reports | No incoming traffic; per-job time limit; launched on a schedule or manually |
| Virtual machines | Full control needed: persistent disk, long-lived connections, specific system requirements | Updates, scaling, and fault tolerance fall on the team |
How We Choose in Practice
We assess the profile: the duration of a single operation, whether there is inbound traffic, whether disk persistence is needed between runs.
If processing fits within the managed environment's limit, we go with a service — it is cheaper to maintain and shuts down idle instances on its own.
If the task runs for hours, we move it into a job, and leave the service to accept the request and enqueue the work: that way the timeout does not cut computations off midway.
We bring in virtual machines only where the managed environment hits its limits, and we keep them in the same configurations as the rest of the infrastructure. Otherwise you end up with a zoo of settings that can never be reproduced later.
Setup Stages: From Audit to Handover to Support
We work in sequential steps: first we capture the current state, then we design, and only after that do we move the load. Decisions about environments and the network perimeter are made before the first release — redoing them after an incident costs more.
- Infrastructure audit. We break down the current setup: how images are built, where the registry lives, what permissions the service accounts have, what is already running in production. The output is a dependency map and a list of bottlenecks.
- Environment scheme. We design the separation of dev, stage, and prod: separate projects, their own service accounts, configuration via environment variables and a secrets manager instead of values baked into the image. A test key will no longer make its way into production.
- Build and release. We build the container, publish it to the registry, and set up the pipeline: checks on every branch, manual approval before production, release of a new Cloud Run revision with gradual traffic rollout.
- Network perimeter. We connect the service to the private network: connector, egress rules, database access only via internal addresses — the database stops being visible from outside.
- Verification scenarios. We run through cold start, revision failure, rollback to the previous one, secret rotation, and unavailability of an external dependency. Each scenario remains a reproducible test rather than a one-off manual check.
- Handover to operations. We give the team the environment scheme, access policies, metrics and alerts, and the rollback procedure. We walk through it on a live release — engineers go through the cycle hands-on.
Configurations and instructions stay in the team's repository, so support can be handled in-house as well.
Handover Package: Configurations, Diagrams, Policies
The work is not finished at the moment the service in Cloud Run answers requests, but when your team can release and roll it back without us. That is why each stage ends with an artifact that lives in your repository and is checked alongside the code. Below is what remains with the client after handover.
- Documentation package: description of the service, revisions, environment variables, and secrets — the environment comes up from an empty project with a single command.
- Environment diagram: which stands exist, how they differ in resources and permissions, and how changes progress from development to the production environment.
- Access policies: roles for developers, on-call staff, and contractors, service account bindings, and permissions granted through groups instead of individual accounts.
- Rollback procedure: the process for returning to the previous revision, decision criteria, and the person responsible for it.
- Release configuration: image build, pre-rollout checks, and the rules that keep a broken build from reaching the production environment.
- Network diagram: the traffic path from the load balancer to the container, the boundaries of private access, which addresses are open and why.
- Runbook for the team: step-by-step scenarios — releasing a version, adding a variable, investigating a failed deployment, reading logs and metrics.
We hand over the package in your format and verify it on live tasks: the client's engineer performs the release and rollback themselves while we only observe and answer questions.
Case Study: Eliminating Downtime During Service Updates
A service with peak traffic ran on several virtual machines behind a load balancer. Updates were launched at night in a manual window: an engineer would stop instances one by one, bring up new ones, and watch the logs.
Before the migration, a release took 40–50 minutes, and nearly every fifth attempt ended in a rollback — usually due to an incompatible schema migration. If an update broke the service during peak hours, recovery stretched into 20–30 minutes of manual actions under pressure.
We moved execution to managed Cloud Run containers. The image is built by the pipeline on every merge into the main branch, and the new revision receives traffic only after passing readiness checks.
The database stayed in the private network: access to it goes through a connector, so the security perimeter did not expand.
Rollback became a switch of traffic to the previous revision. Failed checks return the load to the old version on their own, so an unsuccessful update no longer requires a night window or manual actions.
The Result in Numbers
After the migration, a release takes 6–8 minutes, the share of failed updates dropped from 20% to 5%, and recovery takes seconds instead of half an hour.
Night windows are gone: we release during business hours several times a week, and every rollout is visible in the revision history with an exact image version.
What to Do If Some Services Already Run in the Cloud
Moving everything at once is unnecessary and usually harmful. The working approach is this: some services stay on the current site, some move to the cloud, and communication between them goes over a shared internal network rather than the public internet. For the remaining systems nothing changes — only the addresses of specific services change.
First we build the network bridge: a private connection between the sites, non-overlapping address ranges, routes to internal services in both directions.
On top of that — unified access rules: one role model, shared service accounts, identical requirements for encryption and request logging.
A service in Cloud Run sees the database and internal APIs just as it did at its previous location — there is no need to rewrite code for the new perimeter.
Then we migrate one service at a time rather than in a wave. Each gets its own migration plan: which dependencies are already reachable from the cloud, what stays on the old site, how traffic is switched, and how to roll back.
We set the order by coupling: first services with few dependencies, then those that drag databases and queues along with them.
Coexistence of environments is not a temporary crutch but an operating mode for months. So we immediately fix where the source of truth for configuration and secrets is, who owns each access rule, and by what criterion we consider a service fully migrated.
We verify rollback before switching: as long as traffic can be returned in minutes, the migration proceeds calmly.
Technical Doubts: Access, Scaling, Rollback
Before work starts, clients ask the same engineering questions — not about timelines, but about how the service will behave in production. Below are short answers so that infrastructure decisions are made before the first rollout rather than after an incident.
Will the service get access to the database if it has no public address?
Yes. The connection goes through Serverless VPC Access: the container egresses into your network and reaches the database at a private address. The database stays closed off from the internet, and traffic never leaves the perimeter.
How is internal traffic between services restricted?
Services communicate over internal addresses, inbound public access is disabled, and calls are authenticated at the service account level. Only the service that genuinely needs to be reachable is exposed externally.
How is a failed version rolled back?
Every rollout creates an immutable revision, and rollback is a switch of traffic to the previous one: seconds, with no image rebuild. Previous revisions are retained, so you can return to any of the recent ones.
Are environment accounts separated?
Yes, the test and production environments have different service accounts with the minimum required roles and different permissions on the infrastructure. Secrets are injected from the vault at startup and end up neither in the image nor in the repository.
What happens when a running service is updated?
The new revision comes up alongside the old one, passes the readiness check, and only then accepts traffic. If the checks fail, the switch does not happen — users keep working on the previous version, and the rollout is rolled back automatically.
Let's Discuss Your Task: We'll Put Together a Design and an Implementation Plan
To come back with an estimate and an implementation plan rather than general thoughts, we need input on the project.
Below is what we go through in the first conversation: this data is used to assemble the deployment design in Cloud Run, the order of stages, and the timeline for each. If some of the data is not yet formalized, we will reconstruct the picture together.
- Service inventory: which applications are already in containers, what runs in Cloud Run, what remains on virtual machines, and how the services communicate with each other.
- Repositories and build: where the code lives, how the build and delivery pipeline is structured, which checks are mandatory before release, and how often updates ship.
- Environments and access: how many environments are needed (test, pre-production, production), who owns the cloud project, and what permissions we receive for the setup.
- Network requirements: whether a static external address is needed, whether the service must reach the database inside a closed perimeter, and whether there is a mandatory egress route through the corporate network.
- Load profile: peak requests, acceptable response time, how quickly a new instance must come up — the autoscaling rules depend on this.
- Reliability and rollback: acceptable downtime, the process for returning to the previous version, where secrets are stored, and who is responsible for rotating them.
This set is enough for us to come back with an estimate, a breakdown by stage, and timelines. Describe the task in whatever form is convenient — in writing or a short call: we will clarify the details and send the design.







