A DNS-only cutover can send a user’s next request to Cloud Run or Lambda. Their session, upload, WebSocket connection, or cache entry may still depend on the old stack.
A 60-second DNS TTL does not drain live connections. It does not copy state or force every resolver to switch on schedule.
You can migrate to Cloud Run or AWS Lambda without interrupting users. Run old and new stacks in parallel. Validate production-like traffic. Shift requests gradually with canary or weighted routing.
Success needs stateless application behavior and compatible APIs. It also needs observable error and latency thresholds. Set automatic rollback before a partial rollout becomes a customer-facing outage.
Follow this order to keep users on a working path
Start with a written runbook. Give every change an owner, a success rule, and a reversal path.
This order keeps the old application available. The serverless version must earn more traffic.
- Map workload fit and dependencies: Confirm the app does not depend on one server's disk, memory, or background process.
- Externalize state: Move sessions, uploads, cache, and queued work to shared managed services.
- Deploy in parallel: Put the serverless service behind a separate revision, alias, hostname, or origin route.
- Validate with mirrored traffic: Send safe read requests to both paths. Compare results before users see the new response.
- Shift real traffic slowly: Use canary routing at 1%, 5%, 10%, 25%, 50%, then 100%.
- Watch user outcomes: Stop or reverse when errors, p95 latency, login success, or checkout conversion exceed approved limits.
- Retire the old stack last: Keep it ready until logs, backups, webhooks, and business results stay stable for 24 to 72 hours.
Use routing, not DNS, as the main safety control. DNS propagation can take minutes or many hours because records stay cached. Revision weights and Lambda aliases can move traffic back within minutes. They work behind the active endpoint.
1. Assess
Map state and limits
2. Parallel deploy
Keep old origin live
3. Mirror
Compare safe requests
4. Canary
Move 1% to 50%
5. Confirm
Reach 100% and hold
Set the success bar before deployment
Define success in terms a customer can feel. Record a seven-day baseline for HTTP success rate, p95 latency, and p99 latency.
Also record login completion, payment approval, search results, webhook acknowledgments, and support contacts. These figures show whether users can finish key tasks.
A practical first limit is an error-rate rise of no more than 0.2 percentage points. Allow no more than a 10% p95 latency rise for 10 consecutive minutes.
P95 means 95 out of 100 requests finish at or below that time. It exposes slow experiences that an average can hide.
Keep the rollback path deployable
Keep the existing VPS, managed-hosting app, or cloud service reachable. Keep it behind its current load balancer or reverse proxy.
Store its release artifact, environment settings, database credentials, and health endpoint. Put them in the same change record as the new service.
The frequent mistake is deleting the old server after a clean staging test. Staging rarely has real payment retries or expired sessions.
Staging also lacks mobile networks, third-party webhook bursts, and a full production cache pattern.
Choose Cloud Run for a containerized HTTP service needing runtime control or longer requests. Choose AWS Lambda for short event-driven work tied closely to AWS services.
Base this choice on request duration, traffic bursts, state needs, and cloud dependencies. Do not base it on a generic Cloud Run versus Lambda comparison.
Cloud Run runs a Docker container behind an HTTP endpoint. AWS Lambda runs a function when an event arrives.
Events include API calls, S3 uploads, SQS messages, and EventBridge events.
Match the service to the application
Use Cloud Run when your app already runs in Docker containers. It also fits web routes, custom system packages, and work that resists small functions.
Cloud Run can keep a minimum number of instances warm. This helps when predictable response time matters.
Use Lambda when each work unit stands alone. Examples include image resizing, queue processing, notifications, and narrow API routes.
Lambda aliases give a direct weighted-routing method for version releases.
| Workload condition | Cloud Run | AWS Lambda | Safer alternative |
|---|
| Existing Docker web app | Strong fit | Requires splitting routes | Managed containers |
| Bursty S3, SQS, or EventBridge work | Possible with adapters | Strong fit | Lambda plus queue |
| Persistent local process | Poor fit | Poor fit | Kubernetes or VPS |
| Very low latency at all hours | Use minimum instances | Use provisioned concurrency | Always-on containers |
Select region and compliance controls
Place compute near the database and most users. For US customers, test Google Cloud regions like us-central1, us-east1, and us-west1.
Also test AWS regions like us-east-1 and us-west-2. Measure each path with real application requests.
Keep application compute and the primary database in the same region when possible. A cross-region database round trip can add 20 to 100 milliseconds or more.
That delay affects every request. It often looks like a serverless latency problem.
For regulated data, check your contract duties against platform controls. Google Cloud and Amazon Web Services publish materials for SOC 2, ISO/IEC 27001, HIPAA, PCI DSS, and GDPR.
Your application design still controls data sent to logs, queues, object storage, and analytics tools. Review the current Cloud Run documentation before a regulated cutover.
Review your AWS account's regional service terms too.
Estimate fit with a short load test
Run a 15 to 30 minute production-shaped test against the new service. Run it before creating a canary.
Include ordinary requests, cache misses, large payloads, and login refreshes. Include slow database responses and the heaviest endpoint from access logs.
I have seen a container migration pass a simple homepage test. Its report export then queued requests for over two minutes.
The service looked healthy, but users abandoned jobs. The team had not mapped its request timeout and worker model.
Google Cloud Functions is a useful third option for small, single-purpose Google Cloud event handlers. It is not a full containerized web service.
Choose it for Pub/Sub, Cloud Storage, Firestore, or Eventarc triggers. The handler should have limited dependencies and no need for custom process control.
Cloud Run is usually stronger for multi-route APIs and containerized apps. It also fits HTTP services needing consistent middleware and runtime packages.
Cloud Functions can reduce operational surface area for narrow event handlers. Cloud Run revisions give more direct control for gradual releases across several endpoints.
Externalize state before any serverless traffic reaches users
Move every piece of state outside the instance before routing 1% of users. State is information that must survive after a request ends.
State includes login sessions, uploaded invoices, shopping carts, job status, and in-memory cache entries. These items must be shared by both stacks.
A serverless instance is like a rental car, not a filing cabinet. It may serve one request and then disappear.
The next request may land on another instance. That instance cannot read the first instance's disk or memory.
Replace sessions, uploads, and local cache
Put sessions in shared Redis, a database session table, or signed tokens. Use signed tokens only when token revocation is not required.
Test a login on one instance and the next request on another. A single-instance test hides session affinity mistakes.
Send uploads and generated files to durable object storage. Change code that writes to /tmp/uploads, /var/www/files, or a local image cache.
Write to a bucket first and return a durable URL. Do not treat local disk as shared storage.
Move cache to Redis, Memcached, a content delivery network, or a managed application cache. Local cache can help one request.
It cannot be the source of truth after horizontal scaling.
Make writes and retries safe
Add an idempotency key to payment, order, subscription, and webhook writes. It is a unique value stored with the result.
A retry then returns the first result. It does not charge a card or create an order twice.
Use a database transaction when related writes must all succeed. Use an outbox table when the app saves data and later sends a queue event.
The outbox lets a worker retry the event without losing it.
A common case is an ecommerce order endpoint on one VPS process. It writes an order, calls a payment provider, and sends an email.
A serverless timeout can cause a retry. The order can duplicate unless payment and database writes share an idempotency key.
Record hidden dependencies in one inventory
Create a worksheet with one row for each dependency. Include endpoint or job name, read or write behavior, timeout, and retry count.
Also include state store, owner, rollback effect, and test result. This inventory exposes dependencies before live traffic does.
- Authentication: Record cookie domain, SameSite setting, token issuer, callback URL, and clock-skew tolerance.
- Webhooks: Record source IP expectations, signature validation, response deadline, retry policy, and dead-letter path.
- Queues: Record visibility timeout, ordering expectations, duplicate handling, and maximum message age.
- Database: Record connection limit, transaction time, replica lag, schema version, and migration rollback rule.
- Browser path: Record CORS headers, redirects, CSP, CDN cache key, and file-upload size limit.
Release in parallel with shadow traffic and canary weights
Deploy the new service beside the old one. Mirror safe requests first, then move a small share of real users.
A canary deployment gives a small live audience the new version. A blue-green deployment keeps two complete versions ready for switching.
Cloud Run supports traffic splitting by revision. AWS Lambda supports weighted traffic through aliases that point to published function versions.
Deploy a Cloud Run revision without a full cutover
Build and deploy your container with a release-specific revision name. This command creates a revision with no traffic assigned:
bash
gcloud run deploy web-api /
--image us-docker.pkg.dev/PROJECT/app/web-api:2026-07-14 /
--region us-central1 /
--no-traffic /
--tag canary /
--set-env-vars APP_RELEASE=2026-07-14
Check its tagged URL with authenticated test calls. Then split traffic by revision.
Replace revision names with names from gcloud run revisions list.
Bash
gcloud run services update-traffic web-api /
--region us-central1 /
--to-revisions web-api-00042-abc=99,web-api-00043-def=1
Hold at 1% for 15 to 30 minutes during normal traffic. Do not judge a 9:00 AM Eastern peak from 20 requests at midnight.
Route Lambda traffic with an alias
Publish an immutable Lambda version. Then create or update an alias.
The alias is the stable name for API Gateway, EventBridge, or another trigger. Its weights decide which version gets traffic.
Bash
aws lambda update-alias /
--function-name orders-api /
--name live /
--function-version 42 /
--routing-config 'AdditionalVersionWeights={41=0.01}'
In this example, version 42 receives 99% and version 41 receives 1%. For a new canary, point the alias at the proven old version.
Give the new version a small additional weight. Reverse that setup after the canary is accepted.
Use AWS CodeDeploy canary settings for timed increments and CloudWatch alarm-based reversal. See the AWS Lambda alias guide for alias behavior.
Mirror read traffic before serving it
Send a copy of safe GET, HEAD, or known-idempotent requests to serverless. Return only the old stack's answer during this stage.
Compare response status, body shape, key headers, authorization behavior, downstream calls, and p95 latency. Redact personal data in comparison logs.
Never mirror credentials into a test service with weaker access controls.
Most guides say shadow traffic proves parity. They often omit cache warmth, request order, and third-party rate limits.
These factors can change mirrored traffic behavior. Limit the mirror rate and label every shadow request.
When moving from AWS Lambda to Cloud Run, decouple AWS-specific integrations before moving user traffic. Replace SQS, S3 notifications, and EventBridge rules with Google Cloud equivalents.
Map IAM execution-role permissions to dedicated Google service accounts. Give each account least-privilege access.
Keep AWS Lambda serving production requests while Cloud Run receives mirrored traffic. Send that traffic through a proxy or gateway.
Verify API contracts, JWT issuers, webhook signatures, CORS behavior, session persistence, and externalized state. Do this before weighted routing begins.
Use routing rather than DNS for the first release. DNS cannot safely coordinate a cross-cloud rollback after clients cache the new destination.
Monitor user outcomes and automate rollback thresholds
Watch the canary with user-facing signals. Return traffic to the old version when an approved threshold fails.
Observability means inferring user experience from metrics, logs, traces, and business events. It is more than knowing a container started.
A green deployment status only means the platform accepted code. It does not prove OAuth callbacks, carts, inventory updates, or payments work.
Set technical and business guardrails
Create one dashboard showing old and new routes side by side. Tag each request with release version, route, region, and traffic source.
These tags tie a p99 spike to a revision. They prevent guesswork during a rollout.
| Signal | Canary stop rule | Action |
|---|
| HTTP 5xx rate | More than 0.2 points above baseline for 5 minutes | Set new route to 0% |
| p95 latency | More than 10% above baseline for 10 minutes | Pause increase and inspect traces |
| Login completion | More than 1% below baseline over 30 minutes | Return traffic and inspect session flow |
| Checkout approval | More than 0.5% below baseline over 30 minutes | Return traffic and stop payment route canary |
Use a minimum sample size before judging a business signal. Ten checkout attempts cannot prove a 0.5% change.
A busy store may reach a useful sample within 15 minutes.
Test the reversal under controlled conditions
Run a rollback drill before launch. On Cloud Run, return the old revision to 100%.
On Lambda, update the live alias to the earlier version. You can also set the new version weight to zero.
Bash
gcloud run services update-traffic web-api /
--region us-central1 /
--to-revisions web-api-00042-abc=100
Test the reversal with a harmless feature flag enabled. Then confirm new logs stop receiving live traffic.
Check that the old version can read records created by the new version. An incompatible database write can turn a two-minute rollback into a long incident.
Assign roles for the release window
Name a release operator, a database owner, and a user-impact decision owner. One person can hold several roles in a small business.
Write the decision rule before incident pressure begins.
I have seen teams delay rollback because no one owned that call. A written owner can prevent a short canary issue from becoming a customer outage.