Delete a cluster
Deleting a cluster AstroPulse created removes your applications, the add-ons, the cluster, its nodes and the cloud resources AstroPulse made for it, and every record AstroPulse holds of it, including its cost history. Some resources in your cloud account outlive it: storage your applications created, DNS records, the access you granted AstroPulse, and logs you turned on. A dry run lists what the delete would do before anything is removed. The commands on this page are for an EKS cluster on AWS unless a section says otherwise. A cluster you registered is unregistered instead, which leaves the cluster running: Unregister a cluster.
Before you start
-
The organization owner role.
-
Cost history is deleted with the cluster, on every plan, and cannot be recovered. Export it first:
Terminalastroctl infra k8s cost allocation <cluster-name> --window 30d -o json > cost.json -
Delete the storage claims you do not need first, so their volumes go with them: Remove less by hand.
In the console
- Open the cluster from the cluster list and select Delete at the top of the page. The dialog reads Delete Cluster: "This action cannot be undone."
- The delete's dry run starts by itself. It shows a summary line, then what the delete acts on by kind: applications, add-ons, volumes, cloud resources and access policies. What stays behind or needs you comes first. A long list shows its first items; Show all lists every one.
- Once it reads Dry run passed, type the cluster name and select Delete Permanently.
You should see
The cluster list shows the cluster deleting, and its Events tab, or astroctl infra k8s progress stream <cluster-name>, shows each step. The delete's result lists the volumes it kept. When it finishes, the cluster leaves the list.
What a delete removes
A delete runs in this order. Follow it with
astroctl infra k8s progress stream <cluster-name> or on the cluster's
Events tab.
-
Checks access. AstroPulse must still reach your cloud account with the cluster's stored credentials. If it cannot, nothing is removed and the delete fails, so the cluster keeps running.
-
Removes your applications and application profiles from the cluster. The add-ons your applications do not depend on are removed at the same time. The add-ons that serve your applications, such as Ingress and Certificates, keep running while the delete gives your applications up to a minute to go. A cluster your cloud already reports gone is not waited for.
-
Removes the rest of the managed add-ons AstroPulse installed, in stages: first the add-ons your applications may depend on, then DNS Automation after one more sync (about a minute and a half) in which it removes your applications' DNS records, and the Astro Agent last.
-
Deletes the cloud resources AstroPulse created, on EKS:
- Node Autoscaling's AWS prerequisites: its controller and node roles, Pod Identity association, interruption queue and event rules;
- the EKS cluster and its managed node groups (nodes Node Autoscaling launched go when its add-on is removed in step 3);
- the VPC, subnets, NAT gateways and route tables, when AstroPulse created the network (a VPC you supplied is kept);
- every CloudFormation stack tagged with the cluster, including those a failed create left behind (their names contain the cluster name);
- the cluster's IAM OIDC provider.
-
Deletes AstroPulse's records of the cluster, including its cost history, on every plan and whatever your retention setting. Cost history cannot be recovered. Export it first:
Terminalastroctl infra k8s cost allocation <cluster-name> --window 30d -o json > cost.json
A delete does not stop for what it cannot confirm. What may be left behind is reported as a warning on the delete's progress and in its result, and the delete goes on:
- Applications were still being removed, for example "This application was still being removed from cluster eks-prod when the cluster was deleted: web. Check your cloud account for load balancers it made, and delete any you no longer need." An application was not gone within the wait. Check for load balancers it made: How to find what a delete kept.
- DNS records may still be in your zone, for example "This DNS record may still be in your zone: web.example.com. Delete it if you no longer need it." DNS Automation did not confirm it removed your applications' records. Delete them: How to remove what a delete kept.
The delete stops, and the cluster reads DeleteFailed, only when it cannot start removing your applications or cannot remove an add-on: see If it's refused.
What a delete keeps
| Kept | Why it is kept | Billed while kept |
|---|---|---|
| EBS volumes created by your applications' PersistentVolumeClaims | The cluster is deleted with its claims still bound, so the storage driver never deletes their volumes. They stay in the available state. The cluster's default StorageClass, astro-default-gp3, uses reclaimPolicy: Delete, which only applies when a claim is deleted while the cluster runs. | Yes, per GiB-month |
Volumes of PersistentVolumes with reclaimPolicy: Retain | Retain means Kubernetes never deletes the volume. | Yes, per GiB-month |
| EBS snapshots | Snapshots are independent of the volume and the cluster. | Yes, per GiB-month |
DNS records written by DNS Automation with policy: UpsertOnly, and their TXT ownership records | With UpsertOnly, DNS Automation never deletes a record, so the records stay in your hosted zone after your applications are removed and may point at addresses that no longer belong to you. With the default policy: Sync they are removed in step 2. | Route 53 queries only |
The AWS connect stack, astro-target-stack-<id> | You created it in your account when you connected AWS. It holds the role AstroPulse uses to reach your account, which the delete itself needs, and AstroPulse cannot delete a stack in your account. The CLI prints the exact command to remove it after a delete request. | No |
The control plane log group, /aws/eks/<cluster-name>/cluster | AstroPulse does not turn on EKS control plane logging. If you turned it on, the log group and its retention setting stay in CloudWatch Logs; AstroPulse does not delete it. | Yes, for stored logs |
| A VPC you supplied | AstroPulse used it and did not create it. A VPC made from the AstroPulse template for a test is one CloudFormation stack: delete it after the cluster delete finishes. | Only its NAT gateways and addresses, if any |
| EC2 capacity reservations and Capacity Blocks | You bought them; AstroPulse never buys or cancels one. | Yes, until they end or you cancel them |
| Resources a CloudFormation stack could not delete | CloudFormation keeps a resource still in use, often a security group or network interface, and the delete can finish without it. | Depends on the resource |
| A load balancer of the Ingress or Gateway add-on, rarely | The add-on's load balancer goes when the add-on is removed. One stays, with its security group, only if that removal fails before the add-on's Service is deleted. | Yes, per hour |
The delete lists the volumes it keeps on its progress and in its result, with each volume's claim, size, storage class and disk ID, for example This volume was kept: data/db-0 (20Gi, gp3, vol-0db9f3). Delete it in your cloud account if you no longer need it. A volume whose application was still being removed when the cluster was deleted is not listed as kept: the delete says AstroPulse can't say whether this volume was removed, because its application was still being removed, and names the volume. The list comes from the cluster's agent; if the agent did not report it, the delete says the volumes couldn't be listed, never that there are none. To see the list before you delete, run a dry run. It deletes nothing:
astroctl infra k8s delete <cluster-name> --dry-run
--dry-run lists only the items that need you; add --show-all to list every item with its ID. The console's delete dialog runs the same dry run.
The other kept items are not listed. Use the commands in the next section after the delete finishes.
How to find what a delete kept
Set the cluster's name and region first. Every command is read only.
CLUSTER=eks-prod
REGION=us-west-2
Everything still tagged with the cluster (volumes, load balancers, security groups, network interfaces):
aws resourcegroupstaggingapi get-resources --region "$REGION" \
--tag-filters Key=kubernetes.io/cluster/$CLUSTER \
--query 'ResourceTagMappingList[].ResourceARN' --output table
Volumes your applications created, with size and the claim that made each:
aws ec2 describe-volumes --region "$REGION" \
--filters Name=tag-key,Values=kubernetes.io/cluster/$CLUSTER Name=status,Values=available \
--query 'Volumes[].{ID:VolumeId,GiB:Size,Type:VolumeType,Claim:Tags[?Key==`kubernetes.io/created-for/pvc/name`]|[0].Value}' \
--output table
Volumes from the storage driver also carry the tag ebs.csi.aws.com/cluster=true.
For a monthly estimate, multiply each volume's GiB by the gp3 price per GiB-month
for your region on Amazon EBS pricing.
Load balancers still tagged with the cluster (normally none):
for arn in $(aws elbv2 describe-load-balancers --region "$REGION" --query 'LoadBalancers[].LoadBalancerArn' --output text); do
aws elbv2 describe-tags --region "$REGION" --resource-arns "$arn" \
--query "TagDescriptions[?Tags[?Key=='kubernetes.io/cluster/$CLUSTER']].ResourceArn" --output text
done
aws elb describe-load-balancers --region "$REGION" \
--query 'LoadBalancerDescriptions[].[LoadBalancerName,DNSName]' --output table
The second command lists Classic load balancers; read the tags of any you do
not recognise with aws elb describe-tags --load-balancer-names <name>.
Snapshots of those volumes:
aws ec2 describe-snapshots --region "$REGION" --owner-ids self \
--filters Name=volume-id,Values=vol-0123456789abcdef0 \
--query 'Snapshots[].{ID:SnapshotId,GiB:VolumeSize,Started:StartTime}' --output table
DNS records. DNS Automation marks every record it owns with a TXT record
containing external-dns/owner=<txtOwnerId>. Read the owner before you delete
the cluster:
astroctl infra k8s addons get <cluster-name> external-dns -o yaml
Then list the TXT records with that owner in each hosted zone it managed. The
TXT record's name is the hostname, sometimes with a record-type prefix such as
a- or cname-:
aws route53 list-resource-record-sets --hosted-zone-id Z0123456789ABCDEFGHIJ \
--query "ResourceRecordSets[?Type=='TXT' && contains(ResourceRecords[0].Value, 'external-dns/owner=eks-prod-1a2b3c4d')].Name" \
--output table
The AWS connect stack for the cluster. Its ClusterIdentifier parameter
starts with the cluster name:
aws cloudformation describe-stacks --region "$REGION" \
--query "Stacks[?starts_with(StackName,'astro-target-stack-')].{Stack:StackName,Cluster:Parameters[?ParameterKey=='ClusterIdentifier']|[0].ParameterValue}" \
--output table
Leftover cluster stacks and the log group. The cluster's stacks have the
cluster name between hyphens in their name. The query also lists stacks of any
other cluster whose name contains yours, for example eks-prod also matches
team-eks-prod. Delete a stack only when the text between its first hyphen
and the stack's own part (such as -cluster, -nodegroup-..., -addon-..., or the 8-character suffix of a Karpenter- stack)
is exactly your cluster name.
aws cloudformation list-stacks --region "$REGION" \
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE ROLLBACK_COMPLETE UPDATE_ROLLBACK_COMPLETE DELETE_FAILED \
--query "StackSummaries[?contains(StackName,'-${CLUSTER}-')].[StackName,StackStatus]" \
--output table
aws logs describe-log-groups --region "$REGION" \
--log-group-name-prefix "/aws/eks/$CLUSTER/cluster" \
--query 'logGroups[].{Name:logGroupName,StoredBytes:storedBytes}' --output table
How to remove what a delete kept
Deleting these cannot be undone. Snapshot a volume first if you may need its data.
| Kept item | AWS console | AWS CLI |
|---|---|---|
| Volume | EC2 › Volumes, select it, Actions › Delete volume | aws ec2 delete-volume --region "$REGION" --volume-id vol-0123456789abcdef0 |
| Snapshot | EC2 › Snapshots, select it, Actions › Delete snapshot | aws ec2 delete-snapshot --region "$REGION" --snapshot-id snap-0123456789abcdef0 |
| DNS record | Route 53 › Hosted zones, open the zone, select the record and its TXT record, Delete records | See the change batch below |
| AWS connect stack | CloudFormation › Stacks, select astro-target-stack-<id>, Delete | aws cloudformation delete-stack --region "$REGION" --stack-name astro-target-stack-<id> then aws cloudformation wait stack-delete-complete --region "$REGION" --stack-name astro-target-stack-<id> |
| Log group | CloudWatch › Log groups, select it, Actions › Delete log group(s) | aws logs delete-log-group --region "$REGION" --log-group-name "/aws/eks/$CLUSTER/cluster" |
| Load balancer | EC2 › Load balancers, select it, Actions › Delete load balancer | aws elbv2 delete-load-balancer --region "$REGION" --load-balancer-arn <arn>, or for a Classic load balancer aws elb delete-load-balancer --region "$REGION" --load-balancer-name <name> |
| Leftover stack | CloudFormation › Stacks, select it, Delete | aws cloudformation delete-stack --region "$REGION" --stack-name <stack-name> |
Delete the AWS connect stack only after the cluster delete has finished, and only if you will not re-create a cluster with the same name in that region.
To delete a DNS record from the CLI, read it exactly as Route 53 stores it,
then send it back with DELETE:
aws route53 list-resource-record-sets --hosted-zone-id Z0123456789ABCDEFGHIJ \
--query "ResourceRecordSets[?Name=='app.example.com.']"
{
"Changes": [
{
"Action": "DELETE",
"ResourceRecordSet": {
"Name": "app.example.com.",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [{ "Value": "a1b2c3-123456789.us-west-2.elb.amazonaws.com" }]
}
}
]
}
aws route53 change-resource-record-sets --hosted-zone-id Z0123456789ABCDEFGHIJ \
--change-batch file://delete-record.json
Each ResourceRecordSet must match the stored record field for field, or Route
53 refuses the change. Delete the matching TXT record the same way.
Remove less by hand: before you delete
The delete removes your applications before the add-ons, so their DNS records
go with them. To keep their volumes from the list above, delete the storage
claims you do not need before you delete the cluster, while its storage driver
still runs. A claim on astro-default-gp3 takes its EBS volume with it.
kubectl get pvc -A
kubectl get pv -o custom-columns=NAME:.metadata.name,CLAIM:.spec.claimRef.name,POLICY:.spec.persistentVolumeReclaimPolicy,VOLUME:.spec.csi.volumeHandle
kubectl delete pvc <claim-name> -n <namespace>
A volume with reclaimPolicy: Retain is kept even then; delete it in EC2
afterwards if you do not need it.
Re-creating a cluster with the same name
A cluster's identity in AstroPulse comes from your organization, the cluster name, the provider and the region. A new cluster with the same name in the same region is therefore the same identity:
- AWS connect stack. The new cluster uses the same stack name and role. If
you kept the stack, the connection is already in place; if you deleted it,
connect again with
astroctl cloud aws connect. - DNS records. DNS Automation on the new cluster gets the same default
txtOwnerId, so it takes over the records the old cluster left. Withpolicy: Syncit deletes those its new applications do not publish, and updates those they do. - Kept volumes are not attached to the new cluster. A volume attaches only to a PersistentVolume that names its volume ID, and their tags still carry the old cluster's name.
- Log group. If you turn on control plane logging again, EKS writes to the
same
/aws/eks/<cluster-name>/clusterlog group, after the old logs. - Cost history of the old cluster is not restored.
Google Cloud and Azure
After a delete request, astroctl infra k8s delete prints the commands that
remove what you created when you connected the cloud:
- Google Cloud: the cluster's fleet membership and the service account
AstroPulse used. The workload identity pool
astroplatformis shared by every cluster in the project; delete it only when no other cluster uses it. - Azure: the two role assignments on the resource group. Delete the AstroPulse service principal only when AstroPulse is connected to no other subscription in the tenant.
Disks, snapshots and DNS records your applications created follow the same rules as on AWS: claims still bound when the cluster is deleted leave their disks behind.
Other ways in
- Nova: ask "what would happen if I delete redis-qa?". Nova reads what depends on the cluster and returns the delete command for you to run; it never deletes a cluster itself.
- SDK and API: the same operations, in the API reference.
With astroctl
astroctl infra k8s delete <cluster-name> --dry-run
astroctl infra k8s delete <cluster-name> --dry-run --show-all
astroctl infra k8s delete <cluster-name>
After a delete request, astroctl prints the commands that remove what you created when you connected the cloud. Every flag: astroctl infra k8s delete.
If it's refused
DeleteFailed.DeleteFailed.