Skip to main content

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:

    Terminal
    astroctl 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​

  1. 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."
  2. 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.
  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.
  5. 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:

    Terminal
    astroctl 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​

KeptWhy it is keptBilled while kept
EBS volumes created by your applications' PersistentVolumeClaimsThe 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: RetainRetain means Kubernetes never deletes the volume.Yes, per GiB-month
EBS snapshotsSnapshots 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 recordsWith 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>/clusterAstroPulse 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 suppliedAstroPulse 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 BlocksYou bought them; AstroPulse never buys or cancels one.Yes, until they end or you cancel them
Resources a CloudFormation stack could not deleteCloudFormation 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, rarelyThe 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:

Terminal
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.

Terminal
CLUSTER=eks-prod
REGION=us-west-2

Everything still tagged with the cluster (volumes, load balancers, security groups, network interfaces):

Terminal
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:

Terminal
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):

Terminal
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:

Terminal
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:

Terminal
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-:

Terminal
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:

Terminal
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.

Terminal
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 itemAWS consoleAWS CLI
VolumeEC2 › Volumes, select it, Actions › Delete volumeaws ec2 delete-volume --region "$REGION" --volume-id vol-0123456789abcdef0
SnapshotEC2 › Snapshots, select it, Actions › Delete snapshotaws ec2 delete-snapshot --region "$REGION" --snapshot-id snap-0123456789abcdef0
DNS recordRoute 53 › Hosted zones, open the zone, select the record and its TXT record, Delete recordsSee the change batch below
AWS connect stackCloudFormation › Stacks, select astro-target-stack-<id>, Deleteaws 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 groupCloudWatch › Log groups, select it, Actions › Delete log group(s)aws logs delete-log-group --region "$REGION" --log-group-name "/aws/eks/$CLUSTER/cluster"
Load balancerEC2 › Load balancers, select it, Actions › Delete load balanceraws 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 stackCloudFormation › Stacks, select it, Deleteaws 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:

Terminal
aws route53 list-resource-record-sets --hosted-zone-id Z0123456789ABCDEFGHIJ \
--query "ResourceRecordSets[?Name=='app.example.com.']"
delete-record.json
{
"Changes": [
{
"Action": "DELETE",
"ResourceRecordSet": {
"Name": "app.example.com.",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [{ "Value": "a1b2c3-123456789.us-west-2.elb.amazonaws.com" }]
}
}
]
}
Terminal
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.

Terminal
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. With policy: Sync it 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>/cluster log 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 astroplatform is 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​

Terminal
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​

"Insufficient organization permissions"
Cause
Deleting a cluster needs the organization owner role.
Fix
Ask an organization owner to delete it.
"AstroPulse could not start removing the applications on cluster eks-prod. Nothing was removed, and the cluster is as it was."
Cause
The delete could not start removing your applications, so it stopped and the cluster reads DeleteFailed.
Fix
Fix the cause the cluster's events name, then delete the cluster again.
"Removing the cluster's add-ons failed, so the cluster was not deleted: …"
Cause
An add-on could not be removed, and the cluster reads DeleteFailed.
Fix
Fix what the message names, then delete the cluster again.
"Cluster eks-prod is already being deleted. Follow its progress in the console or with astroctl infra k8s progress stream eks-prod."
Cause
A second delete arrived. It starts nothing new.
Fix
Follow the first delete.

Next​