StackPractices
intermediate By Mathias Paulenko

System Decommissioning Checklist Template

A checklist for safely retiring old services, removing dependencies, and cleaning up infrastructure without breaking downstream consumers.

Overview

Old services never die quietly. They linger in DNS records, confuse new engineers, cost money, and occasionally break things when someone changes a certificate or rotates a secret. Decommissioning is the disciplined process of shutting a service down for good: identifying every dependency, migrating every user, deleting every resource, and documenting what was removed. Done well, it cuts cost and shrinks the attack surface. Done poorly, it takes down systems you thought were unrelated — usually weeks later, when some monthly batch job finally runs.

This template gives you a copy-paste checklist ordered by risk, an execution timeline you can adapt, a notification email, a verification script, and a 30-day audit so nothing gets left behind.

When to Use

Use this checklist when a service is being retired for good:

  • A replacement system is live and already handling all traffic
  • A feature is being sunset and its supporting infrastructure is no longer needed
  • A third-party integration is being discontinued
  • An experiment or proof-of-concept has concluded
  • An audit requires evidence that unused resources were removed

Don’t use it when the service is only being deprecated. If consumers still need time to migrate, run the deprecation timeline template first — decommissioning is the last step of that process, not the first. It also doesn’t apply to shared infrastructure other teams depend on; retire tenant by tenant instead of shutting everything down at once.

Prerequisites

Before decommissioning a system:

  • The replacement system is production-ready and handling all traffic
  • A deprecation timeline has been communicated to all stakeholders
  • All data retention and compliance requirements have been reviewed
  • A rollback plan exists in case the new system fails after shutdown — fold it into your disaster recovery plan
  • The service owner and the billing owner have approved the decommissioning date

The Checklist

# Decommissioning Checklist: `<System / Service Name>`

> Service owner: ______ | Date: ______ | Approved by: ______
> Replacement system: ______ | Decommissioning target date: ______

## 1. Discovery

- [ ] List all upstream services that call this system
- [ ] List all downstream services this system calls
- [ ] List all external integrations (vendors, partners, webhooks)
- [ ] List all DNS entries, load balancers, and CDN configs pointing to this service
- [ ] List all SSL certificates tied to this service
- [ ] List all databases, caches, queues, and storage buckets owned by this service
- [ ] List all scheduled jobs, cron tasks, or background workers
- [ ] List all feature flags or configuration entries referencing this service

## 2. Data Handling

- [ ] Confirm data retention requirements (legal, compliance, business)
- [ ] Export and archive data that must be retained
- [ ] Verify archive location, encryption, and access controls
- [ ] Migrate active data to the replacement system
- [ ] Document the data archive location and retention schedule
- [ ] Confirm deletion of data that does not need to be retained

## 3. Dependency Removal

- [ ] Remove service from upstream load balancers and DNS
- [ ] Update upstream services to stop calling this system
- [ ] Remove service references from API gateways and service meshes
- [ ] Remove service from monitoring, alerting, and dashboards
- [ ] Remove service from CI/CD pipelines and deployment tools
- [ ] Remove service from secret managers and credential stores
- [ ] Remove service from backup schedules and disaster recovery plans

## 4. Shutdown

- [ ] Redirect traffic (if applicable) to replacement or sunset page
- [ ] Stop the service in staging / pre-production
- [ ] Monitor for errors or unexpected traffic for 24-48 hours
- [ ] Stop the service in production
- [ ] Monitor again for 24-48 hours
- [ ] Disable auto-restart or health-check recovery

## 5. Resource Cleanup

- [ ] Terminate compute instances, containers, or serverless functions
- [ ] Delete databases (after confirming archive and retention)
- [ ] Delete caches, queues, and topics
- [ ] Delete storage buckets and volumes
- [ ] Release static IPs and elastic network interfaces
- [ ] Delete load balancer listeners and target groups
- [ ] Delete DNS records (after TTL expires)
- [ ] Revoke or delete SSL certificates
- [ ] Delete IAM roles, policies, and service accounts

## 6. Documentation

- [ ] Update architecture diagrams to remove the service
- [ ] Update runbooks and operational documentation
- [ ] Update service catalog or ownership documents
- [ ] Write a brief postmortem or retrospective on the decommissioning
- [ ] Notify stakeholders that the service has been fully retired

## 7. Verification

- [ ] Confirm no billing charges for the service in the next billing cycle
- [ ] Confirm no alerts or errors reference the service
- [ ] Confirm no code repositories reference the service (excluding history)
- [ ] Confirm no new engineers are onboarding to the retired service

Explanation

The checklist is ordered by risk, and the order matters.

Discovery first, because you can’t retire what you haven’t mapped. The service ownership document is the fastest way to find who owns each consumer. Expect this phase to take longer than planned: traffic analysis, grep over service configs, and DNS query logs always reveal dependencies nobody documented.

Data handling second, because deletions are irreversible. If active data moves to a replacement, the data migration runbook covers sequencing and validation. If it only archives, pick a format that doesn’t depend on the dying system — a SQL dump or Parquet files outlive a proprietary export.

Dependency removal third, because cutting consumers before stopping the service surfaces errors while you can still react. Cleaning API gateways, meshes, and CI/CD at this point also avoids the “service looks alive but isn’t” state that confuses whoever is on call.

Shutdown fourth, and staged: staging first, watch 24–48 hours, then production. Traffic that only arrives on monthly or quarterly schedules is the classic failure here — that’s why the audit waits a full billing cycle.

Cleanup fifth, once the observation window closes — deleting something a surprised consumer still needs is exactly the failure this order exists to prevent.

Documentation last but not optional: update the service catalog, leave a tombstone in the wiki, and write a short retrospective — the postmortem guide structure works well even when nothing went wrong.

The verification phase exists because forgotten resources follow a pattern: a DNS record with a week-long TTL, a cron job on a server nobody remembers, an IAM role that quietly grants access to something else. The script below automates the boring part of that check.

flowchart diagram: 1. Discovery

Execution Timeline

Decommissioning is a calendar exercise as much as a technical one. A safe default for a mid-size internal service:

MilestoneTimingGate to proceed
Discovery and dependency mapT-60 daysEvery consumer identified, owner assigned
Notification sentT-30 daysAll consumers acknowledged; migration support channel open
Data export and restore testT-14 daysOne archive file actually restored and read
Service stopped (staging, then prod)T-0Zero requests or errors for 24-48 hours
Infrastructure deletionT+7 daysNo rollback events during observation
Post-decommissioning auditT+30 daysZero charges, zero alerts, catalog updated
Artifact purgeT+90 daysRetention policy and legal sign-off

Compress this for experiments and low-risk services; stretch it for anything with paying consumers or compliance obligations. If the migration is large, the data migration guide covers cutover strategy in more depth.

Artifact Retention and Rollback

Keep a restorable snapshot for 90 days after shutdown: the tagged repository archive, the last configuration export, and a verified data dump. That window covers most surprise dependencies without turning into indefinite hoarding. If a critical consumer surfaces at day 40, you can restart the service temporarily, migrate them, and shut down again without an emergency rebuild.

Rollback isn’t a failure — it’s the cost of discovering a dependency late. What makes it cheap or expensive is preparation: artifacts you can redeploy versus artifacts you deleted on day one.

Remaining Resource Verification Script

#!/bin/bash
# Verify remaining resources after decommissioning
set -euo pipefail

SERVICE_NAME="legacy-auth-service"
AWS_REGION="us-east-1"

echo "=== Remaining Resource Verification ==="
echo "Service: $SERVICE_NAME"
echo "Date: $(date)"
echo ""

# Check DNS records
echo "--- DNS Records ---"
dig +short "$SERVICE_NAME.internal.example.com" && echo "WARNING: DNS record still exists" || echo "OK: No DNS record"

# Check AWS resources
echo "--- AWS Resources ---"
aws ec2 describe-instances --region $AWS_REGION --filters "Name=tag:Service,Values=$SERVICE_NAME" --query 'Reservations[*].Instances[*].InstanceId' --output text 2>/dev/null | while read id; do
  [ -n "$id" ] && echo "WARNING: EC2 instance found: $id"
done

aws rds describe-db-instances --region $AWS_REGION --query 'DBInstances[?DBInstanceIdentifier.contains(@, `'$SERVICE_NAME'`)].DBInstanceIdentifier' --output text 2>/dev/null | while read id; do
  [ -n "$id" ] && echo "WARNING: RDS instance found: $id"
done

aws s3 ls --region $AWS_REGION 2>/dev/null | grep "$SERVICE_NAME" && echo "WARNING: S3 bucket found" || echo "OK: No S3 buckets"

# Check certificates
echo "--- Certificates ---"
aws acm list-certificates --region $AWS_REGION --query 'CertificateSummaryList[*].DomainName' --output text 2>/dev/null | tr '\t' '\n' | grep -i "$SERVICE_NAME" && echo "WARNING: ACM certificate found" || echo "OK: No certificates"

# Check CloudWatch alarms
echo "--- CloudWatch Alarms ---"
aws cloudwatch describe-alarms --region $AWS_REGION --query 'MetricAlarms[?AlarmName.contains(@, `'$SERVICE_NAME'`)].AlarmName' --output text 2>/dev/null | while read alarm; do
  [ -n "$alarm" ] && echo "WARNING: CloudWatch alarm found: $alarm"
done

echo ""
echo "=== Verification Complete ==="

Run it weekly during the 30-day audit window. Extend it with whatever your stack adds — Kubernetes namespaces, Datadog monitors, Vault policies — because the generic checks only cover the common cases.

Decommissioning Notification Template

=== Service Decommissioning Notification ===

To: engineering@example.com, stakeholders@example.com
From: platform-team@example.com
Date: [DATE]
Subject: Decommissioning of [SERVICE_NAME]

The service [SERVICE_NAME] will be decommissioned on [SHUTDOWN_DATE].

What does this mean?
- The service will stop accepting traffic on [SHUTDOWN_DATE]
- Data will be archived and available for [RETENTION_PERIOD]
- Endpoints will return 410 Gone after shutdown
- Upstream dependencies must be updated before [SHUTDOWN_DATE]

What do you need to do?
1. Check if your service depends on [SERVICE_NAME]
2. If it does, migrate to [REPLACEMENT_SERVICE] before [SHUTDOWN_DATE]
3. Update configurations, environment variables, and documentation
4. Contact platform-team@example.com if you need migration support

Timeline:
- [TODAY]: Notification sent
- [T-30]: Service shutdown (stop)
- [T-60]: Infrastructure deletion
- [T-90]: Data deletion (per retention policy)

Contact: platform-team@example.com

Send it at T-30, resend at T-14 and T-3, and post it in the channels where the consumer teams actually read — an email nobody opened isn’t notice.

Post-Decommissioning Audit Checklist

=== Post-Decommissioning Audit (30 days) ===

Infrastructure:
  [ ] No cloud resources generating charges
  [ ] No active alerts referencing the service
  [ ] No obsolete dashboards in Grafana/Datadog
  [ ] No targets in Prometheus/CloudWatch

Network and DNS:
  [ ] DNS records deleted or redirected
  [ ] TLS certificates revoked or deleted
  [ ] Firewall/security group rules deleted
  [ ] Load balancers and target groups deleted

Code and CI/CD:
  [ ] Repository archived (not deleted)
  [ ] CI/CD pipelines disabled
  [ ] Webhooks removed
  [ ] CI environment variables deleted

Data:
  [ ] Backups archived to cheap storage
  [ ] Retention policy documented
  [ ] Access to archived data restricted and audited

Documentation:
  [ ] Tombstone created in wiki/documentation
  [ ] Service catalog updated
  [ ] Architecture diagrams updated
  [ ] Runbooks referencing the service updated

Variants

ContextAdjustmentsNotes
Third-party service replacementAdd vendor contract termination, data export, and API key revocationYou don’t control the infrastructure
Database-only retirementFocus on schema migration, referential integrity, and query redirectionData outlives code
Microservice retirementAdd service mesh removal, contract updates, and consumer notificationConsumers may be other teams
Region shutdownAdd data residency checks, user migration, and latency impact assessmentRegions have compliance implications
Experiment teardownShorter checklist; focus on data deletion and cost confirmationExperiments should clean up fast

What Works

  1. Never delete on day one — stop the service, wait, watch. Deletion comes last, once everything else has proven quiet
  2. Archive before deleting. Storage is cheap; explaining missing data to an auditor isn’t
  3. Communicate early and often. The worst decommissioning surprise is finding out another team was still using the service
  4. Document what was removed. Future engineers will search for the service; leave a tombstone, not a mystery
  5. Verify billing. Cloud resources have a way of generating charges even after you think they’re gone

Common Mistakes

  1. Skipping discovery. The service you’re shutting down is someone else’s critical dependency
  2. Deleting data too early. Legal hold, audit, or business needs may require retention longer than you expect
  3. Not waiting after shutdown. Some workloads only wake up on monthly or quarterly runs
  4. Forgetting DNS. A DNS record pointing to a deleted IP can be hijacked or cause strange errors
  5. Ignoring certificates. Expired certificates for deleted services still trigger renewal alerts and automation

Troubleshooting

  • A consumer still calls the service after shutdown: check load balancer access logs and gateway metrics for hits on the retired endpoints, identify the caller’s owner, and give them a hard migration date. If the traffic is load-bearing, restart temporarily from the retained artifacts while they migrate — that’s what they’re for.
  • The DNS record comes back: someone else’s Terraform, CDK, or Ansible is re-creating it. Search the infrastructure-as-code state for the record and remove it there; deleting it from the console only holds until the next apply.
  • Charges still appear in the next bill: the usual suspects are elastic IPs, EBS snapshots, S3 buckets in regions you forgot, Route 53 health checks, and old log groups. Filter the cost report by the service tag and by region before declaring cleanup done.
  • Alerts keep firing for the dead service: the rules live in Prometheus rule files, CloudWatch, or Terraform — not in the dashboards someone deleted. Remove the rule definitions and any silence policies that mention the service.
  • Someone urgently needs the archived data: this is why the archive uses a portable format and a documented restore procedure. Test-restore one file at the T-14 gate so the first real restore doesn’t happen under incident pressure.

Further Reading

Frequently Asked Questions

How long should we wait between shutdown and cleanup?

At least one full billing cycle and one complete business cycle. If the service processes monthly reports, wait until the next month to confirm nothing broke. Thirty days is the number I'd pick for anything critical.

What if we discover an unknown dependency after shutdown?

Have a rollback plan: keep the service artifacts (code, config, data snapshot) for 90 days after shutdown. If something critical surfaces, you restart temporarily and migrate it properly the second time.

Should we keep the code repository?

Archive it, don't delete it. Park it in an archive or retired org. Preserve the git history — it contains the rationale for decisions that may still be relevant. Delete active CI/CD triggers and deployment keys.

What do we do with data after decommissioning?

Export data to a portable format (CSV, JSON, SQL dump) and store it in cheap archival storage (S3 Glacier, Azure Archive). Document the schema, the meaning of each column, and any transformations applied. Legal and business requirements set the retention window — write it down. Configure cost alerts for the archival storage. Write down how to query the archive before anyone actually needs to.

How do we handle dependencies on already-decommissioned services?

Before shutdown, identify all consumers using traffic analysis, code review, and team surveys. Tell every consumer at least 30 days out — longer if they pay you. Give them migration docs and someone to ping when they get stuck. If someone can't finish in time, freeze the service read-only until they do. Document any consumers that couldn't migrate and the reason.

Should we reuse the service name after decommissioning?

No. Retired service names should stay retired — reuse breeds confusion. Reuse the name and engineers will wire config and docs to a system that no longer exists, which is how subtle outages start. Use versioned or descriptive names (auth-service-v2 instead of auth-service). Keep a tombstone in the documentation indicating the original name was retired.

How do we calculate cost savings from decommissioning?

Sum all direct costs (compute, storage, network, licenses) and indirect costs (engineering time for maintenance, monitoring, on-call). Subtract the cost of archival storage. Watch the bill for six months; 'deleted' and 'no longer billed' are different states. Report savings to finance and leadership. Those numbers are your argument for the next decommissioning.

What if we need to restart a decommissioned service?

If you kept the artifacts (code, configuration, data snapshot), you can restart temporarily. Follow the documented rollback procedure. Tell consumers it's temporarily back, and why. Give the temporary restart an expiry date. If artifacts were deleted, rebuild from the archived repository using the tagged version at decommissioning time. Document the reason for restart and update the tombstone.