# Stopped Deployment

> Flags Deployments that are at least 24 hours old and set to 0 replicas, a cleanup candidate whose claims and Services may still cost money.

Source: https://zop.dev/integrations/kubernetes/recommendations/stopped-deployment

---

## Zero pods, but not zero footprint

Scaling a Deployment to zero, for example with
`kubectl scale deployment/<name> --replicas=0` from the
[Deployments documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/),
stops its pods and frees their node capacity. Everything around the Deployment stays: the Service
pointing at it, its ConfigMaps and Secrets, and any PersistentVolumeClaims it used. Claims backed
by cloud disks keep billing for their provisioned size, and a `LoadBalancer` Service keeps its
cloud load balancer.

The other cost is confusion. A Deployment at zero looks like something that could come back at
any moment, so nobody deletes its dependencies, and after a few months nobody remembers why it was
parked. If an HPA targets it, Kubernetes treats a target set to zero as an
[implicit maintenance-mode deactivation](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/):
the HPA stops adjusting it until someone changes the replica count or the HPA's minimum.

## Listing Deployments set to zero

```bash
kubectl get deployments -A -o json | jq -r '
  .items[]
  | select(.spec.replicas == 0)
  | "\(.metadata.namespace)/\(.metadata.name) created=\(.metadata.creationTimestamp)"'
```

To see what each one still holds on to, list the claims and Services in the same namespace:

```bash
kubectl get pvc,services -n <namespace>
```

## Gate: zero replicas and a day of age

ZopNight reads the replica count from the Deployment spec and fires when it is exactly 0. A
Deployment created a few minutes ago, or one briefly at zero during a rollback, would otherwise
flicker in and out of the results, so the rule also requires the Deployment to be at least 24
hours old by its creation timestamp. Note that this is the object's age, not how long it has been
at zero. When the replica count or creation time is missing, no finding is raised.

## What it does not measure

The rule does not look up the claims, Services or other objects around the Deployment, so it
cannot tell you how much they cost. It also does not read intent: a Deployment scaled to zero by a
schedule or kept as a warm standby looks the same as an abandoned one. StatefulSets are out of
scope. Services that end up with no pods behind them are reported separately by
[Service with no endpoints](https://zop.dev/integrations/kubernetes/recommendations/service-with-no-endpoints).

## A cleanup item with no computed saving

The Deployment object itself has no charge, so the finding carries no dollar figure and is filed
as an orphan. Any saving comes from the resources you remove along with it.

## Deciding between delete and document

1. Ask the owning team whether the Deployment will ever run again.
2. If not, delete it with `kubectl delete deployment <name>`.
3. Remove the claims, Services, ConfigMaps and Secrets that only it used.
4. If it is parked on purpose, add an annotation recording the reason and an owner.

**Warning**
Deleting a PersistentVolumeClaim whose StorageClass uses the `Delete` reclaim policy also
deletes the cloud disk and its data. Snapshot first if there is any doubt.
