Cloud provider retiring a service: migration checklist and safe cutover
For customers who have been told a cloud service they rely on is being retired and are not sure what breaks next. This runbook helps you confirm whether you are affected, find the dependency in your app or infrastructure, migrate to a replacement, and cut over with the least possible downtime.
TL;DR — If your cloud provider is retiring a service, the most common problem is that your app, DNS, or automation still points at the old endpoint. Start by finding every place the retired service name appears in your provider dashboard, app settings, and CI/CD (build-and-deploy automation), then switch to the replacement service in a test environment before updating production DNS or credentials. Reading time: ~6 min
The scenario
It is a normal Tuesday afternoon and you get an email from your cloud provider with a subject like "Action required: service retirement". Your site is still up today, but the notice says the old database, object storage, message queue, or load balancer endpoint will stop working on a fixed date. You open the dashboard and see both the old service and a suggested replacement, but it is not obvious which apps still depend on it. Your client does not care what the service is called; they care whether checkout, logins, uploads, and scheduled jobs will still work next week.
Symptoms
- Email or dashboard banner saying the service is being retired, disabled, or "reaches end of life" on a specific date.
- New deploys fail because the old resource can no longer be created.
- Existing apps show connection errors such as:
ENOTFOUND old-service.example.internalgetaddrinfo ENOTFOUNDconnection refusedi/o timeoutTLS handshake timeoutcertificate verify failed403 Forbidden401 Unauthorized
- Background jobs start failing after cutover with log lines like:
dial tcp: lookup old-endpoint: no such hostFATAL: password authentication failedNoSuchBucketorBucketNotFoundx509: certificate signed by unknown authority
- End users may see
500 Internal Server Error,502 Bad Gateway, broken file uploads, login loops, or missing media/assets.
Likely causes
| Cause | How common | Quick check |
|---|---|---|
| App config still points to the retired endpoint/service name | Very common | Search your app settings for the old hostname or resource ID in your provider dashboard: Settings/Configuration/Environment Variables |
| DNS still points traffic to the retired service | Very common | dig +short yourdomain.com |
| CI/CD or infrastructure code still creates the old service | Common | Search your repo for the retired service name: grep -R "old-service-name" . |
| Credentials or IAM (access permissions) were not recreated for the replacement | Common | Try one authenticated request from the dashboard test tool or app logs and look for 401, 403, or auth failures |
| Data was not migrated to the replacement service | Less common but high impact | Compare object counts / row counts / queue depth in old vs new service dashboards |
| TLS/certificate settings differ on the replacement endpoint | Less common | curl -Iv https://new-endpoint.example.com |
Step-by-step diagnosis
-
Read the retirement notice and identify the exact service and deadline. In your provider dashboard, open the alert/notification and copy the retired service name, old endpoint, and any replacement named there.
- This is your problem if: the notice names a resource your app uses today, such as a database hostname, storage bucket, queue URL, or load balancer.
- Jump to: the matching fix section below after you complete steps 2-4.
-
Check whether production still points at the old service. First use the dashboard path for your app settings: in your provider dashboard, open the app/service, then look for Settings → Configuration / Environment Variables. Search for the old hostname, bucket name, queue URL, or resource ID.
- CLI option if you have repo access:
grep -R "old-service-name\|old-endpoint.example.com\|old-bucket-name" .
- This is your problem if: you find the old value in
DATABASE_URL,REDIS_URL,S3_BUCKET,QUEUE_URL, API base URLs, or app config files. - Jump to:
### App config still points to the retired endpoint/service name.
- Check DNS before touching the app. In your DNS provider dashboard (for example, your provider's dashboard or a DNS service such as Cloudflare: DNS → Records), inspect the records for the app domain, API domain, upload domain, and any custom mail or webhook domains.
- CLI check:
dig +short yourdomain.com
- This is your problem if: the answer is the old IP/hostname, or a CNAME (alias record) still targets the retired service endpoint.
- Jump to:
### DNS still points traffic to the retired service.
- Check your deployment automation and infrastructure code. Open your code host and search the repository for the retired service name.
grep -R "old-service-name\|old-endpoint.example.com" .
- Also check CI/CD variables in your code host or provider dashboard under Settings → Secrets / Variables.
- This is your problem if: Terraform (infrastructure-as-code), deployment scripts, Docker files, or CI variables still reference the retired service.
- Jump to:
### CI/CD or infrastructure code still creates the old service.
- Test authentication against the replacement service. Use the provider dashboard's built-in connection test if available; otherwise use a single request from a machine that can reach the service.
curl -Iv https://new-endpoint.example.com
- For databases, use the provider's connection test button first; if not available, check app logs after temporarily pointing a staging app at the new endpoint.
- This is your problem if: you get
401 Unauthorized,403 Forbidden,password authentication failed, or permission denied errors. - Jump to:
### Credentials or IAM were not recreated for the replacement.
-
Confirm the data exists on the replacement before cutover. In the provider dashboard, compare the old and new service: object count, database row counts for key tables, or queue backlog.
- This is your problem if: the new service is empty, incomplete, or missing recent writes.
- Jump to:
### Data was not migrated to the replacement service.
-
Check TLS on the new endpoint. If the app reaches the new service but fails during secure connection setup, test the certificate.
curl -Iv https://new-endpoint.example.com
- This is your problem if: output shows certificate name mismatch, unknown CA (certificate authority), or handshake failure.
- Jump to:
### TLS/certificate settings differ on the replacement endpoint.
View as text
[Retired Service Notice] ---- 1. identify ----> [Old Resource] [Old Resource] ---- 2. search refs ----> [App Config] [Old Resource] ---- 3. check target ----> [DNS] [Old Resource] ---- 4. search refs ----> [CI/CD] [App Config] ---- 5. test auth ----> [Replacement Service] [Replacement Service] ---- 6. verify data ----> [Cutover] [Replacement Service] ---- 7. verify TLS ----> [Cutover]
Fixes
App config still points to the retired endpoint/service name
Update every app setting that references the old service.
- In your provider dashboard, open the app/service and go to Settings → Configuration / Environment Variables.
- Replace values such as
DATABASE_URL,REDIS_URL,S3_BUCKET,QUEUE_URL, or API base URLs with the replacement values. - Redeploy or restart the app from the dashboard.
Example env change:
DATABASE_URL=postgres://appuser:NEWPASSWORD@new-db.example.internal:5432/appdb
REDIS_URL=redis://new-cache.example.internal:6379
S3_BUCKET=customer-uploads-prod-v2
QUEUE_URL=https://queue.example.com/accounts/prod/jobs-v2
If your app uses a config file:
{
"storageBucket": "customer-uploads-prod-v2",
"queueUrl": "https://queue.example.com/accounts/prod/jobs-v2",
"apiBaseUrl": "https://api-v2.example.com"
}
Verify it worked: redeploy, then load the affected feature and confirm logs no longer show the old endpoint.
DNS still points traffic to the retired service
Lower DNS TTL (cache lifetime) first if your DNS provider allows it, then update the record.
- In your DNS provider dashboard, open DNS → Records.
- Edit the
A,AAAA, orCNAMErecord that points to the old service. - Change it to the new IP or hostname.
Example:
dig +short yourdomain.com
If using a CNAME, the record should look like:
app.yourdomain.com. 300 IN CNAME new-endpoint.example.com.
Verify it worked: run dig +short yourdomain.com until it returns the new target, then open the site in a private/incognito window.
CI/CD or infrastructure code still creates the old service
Update deployment code so the old service is not recreated on the next deploy.
- Search the repo and CI variables for the retired service name.
- Replace resource types, module names, and endpoint variables with the replacement.
Example search:
grep -R "old-service-name\|old-endpoint.example.com" .
Example CI variable update:
SERVICE_ENDPOINT=https://new-endpoint.example.com
SERVICE_CLASS=replacement-service
If you use Terraform, update the resource/module names and run a plan before apply.
⚠️ Applying infrastructure changes can cause downtime or replacement of live resources. Run the change in staging first and review the plan output before production.
terraform plan
terraform apply
Verify it worked: a fresh deploy completes without trying to create or reference the retired service.
Credentials or IAM were not recreated for the replacement
Create new credentials for the replacement service and give them the minimum required permissions.
- In your provider dashboard, open the replacement service and create a new app user, API key, or access policy.
- Update the app secret in Settings → Secrets / Environment Variables.
Example env values:
SERVICE_ACCESS_KEY=AKIAEXAMPLE
SERVICE_SECRET_KEY=replace-with-new-secret
DATABASE_URL=postgres://appuser:NEWPASSWORD@new-db.example.internal:5432/appdb
For object storage, the app usually needs read/write/list on the specific bucket only.
Verify it worked: repeat the failed action and confirm 401, 403, and permission-denied errors stop.
Data was not migrated to the replacement service
Move the data before switching production traffic.
⚠️ Data migration can overwrite newer data or create split-brain (writes landing in two places). Freeze writes or use a documented replication/cutover window before the final sync.
Use the provider's migration tool first if one exists. If not, use service-native export/import or sync tools.
Examples:
pg_dump "$OLD_DATABASE_URL" > appdb.sql
psql "$NEW_DATABASE_URL" < appdb.sql
aws s3 sync s3://old-bucket-name s3://new-bucket-name
After the bulk copy, stop writes briefly and run one final sync.
Verify it worked: compare key counts in old vs new service dashboards and test the latest records/files are present in the new service.
TLS/certificate settings differ on the replacement endpoint
If the replacement uses a different hostname or certificate chain, update the app to trust and call the new hostname exactly.
Test first:
curl -Iv https://new-endpoint.example.com
If the certificate name does not match, change the app endpoint to the hostname on the certificate. If your app pins a CA bundle (trust store file), update it to the current CA bundle used by your operating system or runtime.
For nginx reverse proxy:
proxy_ssl_server_name on;
proxy_pass https://new-endpoint.example.com;
Verify it worked: curl -Iv https://new-endpoint.example.com completes without certificate or handshake errors.
Prevention
- Add a dependency inventory file to the repo. Keep one plain-text list of external services and endpoints so retirement notices are easy to map.
cat > docs/dependencies.txt <<'EOF'
Database: new-db.example.internal
Cache: new-cache.example.internal
Storage bucket: customer-uploads-prod-v2
Queue: https://queue.example.com/accounts/prod/jobs-v2
DNS: app.yourdomain.com -> new-endpoint.example.com
EOF
- Add a CI check that fails if retired endpoints appear in code or config.
! grep -R "old-service-name\|old-endpoint.example.com\|old-bucket-name" .
- Monitor for DNS drift and certificate expiry on critical domains. Run a scheduled check from your monitoring tool or cron.
dig +short app.yourdomain.com
curl -Iv https://app.yourdomain.com
Alert if the target changes unexpectedly or TLS fails.
- Use separate staging and production cutovers with the same replacement config. Put the replacement endpoint and credentials in staging first, run smoke tests, then copy the same values to production.
curl -f https://staging.yourdomain.com/health
curl -f https://app.yourdomain.com/health
-
Set calendar reminders for provider notices and certificate renewals. Track retirement dates, migration windows, and final shutdown dates in your shared ops calendar so the work is scheduled before the deadline.
-
Keep DNS TTL low during migrations, then raise it after cutover. During the migration window use a shorter TTL such as 300 seconds; after the cutover is stable, raise it again to reduce query churn.
dig +nocmd app.yourdomain.com +noall +answer
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI