Services are launched with a checklist and switched off with a Slack message. The result is a long tail of half-decommissioned systems: the container is gone but the database is still there, the DNS still resolves, the API key still works, and the IAM role still grants production access to something nobody is watching.
A half-deleted service is more dangerous than a running one, because it retains its privileges and loses its attention. This post is the order to do it in, and the parts that get missed. For whoever is turning something off this quarter.
Why partial removal is the worst state
A running service has an owner, appears on dashboards, gets patched in the normal cycle, and somebody notices when it behaves strangely.
A half-removed service has none of that and keeps everything that made it powerful. Its credentials still authenticate. Its role still grants access. Its data still exists. Nobody patches it because nobody believes it is running, and no alert covers it because it was removed from monitoring in step one, which is usually the only step that gets completed.
The pattern is consistent: the visible parts go quickly, the invisible parts persist indefinitely, and the invisible parts are the ones with the credentials.
The order that matters
Two ordering rules do most of the work.
Remove DNS before cancelling anything hosted. A record outliving the service it points at is claimable by whoever can register that name on the vendor's platform, and you now have content served from your domain. Delete the record first and the window never exists.
Revoke credentials before deleting infrastructure. If you delete the service first and plan to clean up credentials afterwards, the credentials are live and unowned in the meantime, and the cleanup task is the one that gets deprioritised once the visible work is done.
The checklist
Work through it in this order, and record what you actually did rather than what you intended.
Traffic and naming. DNS records, including any wildcard that would still resolve. Load balancer rules and target groups. CDN configuration. Certificates, which should be revoked rather than merely left to expire.
Credentials and identity. This is the section that matters most and gets the least attention.
- API keys the service held, and keys other systems used to call it.
- The OAuth client or application registration.
- The service account, and the role or policy it assumed.
- Webhook endpoints registered with third parties, which will keep delivering to a hostname that may later belong to someone else.
- Signing keys and certificates.
- Any entry in a secrets manager, deleted rather than just unreferenced.
- The vendor accounts the service used, because a cancelled service does not cancel its SaaS subscriptions.
Data. The database, object storage, caches, message queues, search indices, and the backups of all of those. Backups are the one people miss, and they are the reason a service can be "deleted" while its customer data survives for another year under a retention policy nobody reviewed.
If the data is subject to a retention obligation, that is a decision to record with a date and an owner, not a reason to leave it undeleted by default.
Access paths. Network rules and security groups referencing it. VPC peering or private links. Firewall exceptions. Bastion or jump-host configuration. Anything added once "temporarily" to make the service work.
Code and pipelines. Archive the repository rather than deleting it, so history survives. Remove CI pipelines and their secrets, which often hold the most privileged credentials in the whole list. Remove deploy keys. Remove the service from any dependency or configuration that still references it.
Observability and process. Monitors, alerts, dashboards, log ingestion, on-call rotations, status page components, and the runbooks. Retain the logs for whatever your policy requires, then actually delete them at the end of it.
Records. Remove it from the asset inventory, the data map, the subprocessor list if it involved one, and the SOC 2 or ISO scope. An auditor comparing your inventory to reality will find the discrepancy, and an inventory with dead entries undermines confidence in the live ones.
Verify rather than assume
The checklist is a claim. Test it.
# DNS should not resolve
dig +short api.oldservice.example.com
# nothing should answer
curl -s -o /dev/null -w "%{http_code}\n" -m 10 https://api.oldservice.example.com/
# the credential should be dead
curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $OLD_TOKEN" \
https://api.internal/whoami # want 401
Then search your logs for the service name and for its hostname over the following month. Traffic after decommissioning means something still depends on it, and finding that out from your own logs is much better than finding out from a customer.
Announce it before, and leave a marker after
Tell dependants with real notice, and if you can, make the service fail loudly for a short window before it goes, so unknown consumers surface while someone is still around to help them. An hour of deliberate 503s a week before shutdown finds integrations no documentation records.
Afterwards, leave a note somewhere durable: what was removed, when, by whom, and where the data went. In a year somebody will ask, and the difference between an answer and an investigation is a paragraph written at the time.
The concession
Full decommissioning is slower than switching something off, and the pressure is always to move on to the next thing. It is also genuinely hard to do completely, because the references are scattered across systems owned by different teams and no tool enumerates them all.
The pragmatic version: do credentials and DNS immediately and completely, on the day, because those are the ones with security consequences and they are the smallest section. The rest can be a tracked cleanup over the following weeks. What is not acceptable is the common pattern where the compute is removed on day one and nothing else is ever touched.
The implication
Your attack surface includes everything you built and stopped watching. Most inventories describe what is running, and the interesting question is what is still reachable, still credentialed, and no longer anyone's job.
A decommissioning checklist is how those two lists stay the same. It is twenty minutes at the end of a project that otherwise leaves a permanent, unowned hole.