Skip to main content

Cluster Add-ons

Add-ons are platform-curated versions of the standard Kubernetes ecosystem apps most clusters need: installed, upgraded, and monitored with one command or one click, on any AstroPulse cluster (provisioned or registered).

You declare intent: which add-on, and a few typed options. AstroPulse owns the rest: the chart, the version, the deployment lifecycle, and health. You never handle Helm values, and the platform never asks for (or stores) cloud credentials in an add-on configuration.

Ways to manage add-ons

Manage add-ons through whichever surface fits how you work:

  • Console: the cluster's Add-ons tab; point and click.
  • CLI: astroctl infra k8s addons … commands; good for GitOps and reproducible setups.
  • Nova (AI): ask in plain language. Nova is available as Nova Cloud (hosted chat in the console), Nova Connect (Nova in your editor or IDE via the AstroPulse MCP endpoint platform.astropulse.io/mcp), and Nova Direct. See One AI, Any Interface for how the three fit together.

All three drive the same platform and enforce the same validation and approvals. There's no separate path with looser rules.

Available add-ons

Add-onWhat it doesClusters
NGINX Ingress (nginx-ingress)HTTP/S ingress controller; routes external traffic to your apps and powers custom domainsAll
ExternalDNS (external-dns)Publishes DNS records for your services and ingressesAll
cert-manager (cert-manager)Issues and renews TLS certificates automaticallyAll
Karpenter (karpenter)Just-in-time node autoscalingAWS (EKS and self-hosted)

Add-ons are cluster capabilities, addressed by cluster + add-on name. One instance of each add-on per cluster. They are managed here (in the console's cluster page and the astroctl infra k8s addons commands) and intentionally do not appear in your Applications view: your app list stays yours.

Each add-on installs into its own dedicated namespace (the ecosystem standard), created automatically:

Add-onNamespaceService account
nginx-ingressingress-nginxingress-nginx
external-dnsexternal-dnsexternal-dns
cert-managercert-managercert-manager
karpenterkube-systemkarpenter

What each add-on does by default

You declare intent; these are the defaults AstroPulse applies unless you override them in the add-on configuration.

NGINX Ingress

  • Runs 1 replica; ingress class nginx; no autoscaling.
  • Service is a cloud LoadBalancer by default. On clusters without a cloud LB (local, bare-metal), set nginx.enableNodePort: true to expose it via NodePort.
  • TLS is terminated at the controller: you attach a TLS Secret to each Ingress (spec.tls) and NGINX terminates HTTPS and forwards plaintext to your Service. SSL passthrough is not enabled by default (the controller does not forward encrypted traffic to the backend). Pair with cert-manager to get and renew those certificates automatically.

ExternalDNS

  • Watches both Ingresses and Services for hostnames and publishes the matching records to your DNS provider.
  • Runs with policy: sync: it creates, updates, and deletes records to match the cluster. Removing an Ingress/Service removes its DNS record. (It only touches records it owns; see the TXT owner ID below.)
  • Uses a TXT registry for ownership, tagged with an owner ID that defaults to the cluster name, so multiple clusters can safely share a DNS zone.
  • No domain filter by default (it manages every zone the cluster identity can reach); set one to scope it. Not a dry run by default. See ExternalDNS: DNS automation for the required cluster identity and per-provider setup.

cert-manager

  • Installs the cert-manager CRDs by default (certManager.installCRDs: true). Turn this off only if the CRDs are already present on the cluster.
  • No ClusterIssuer by default: you define your own Issuer/ClusterIssuer and reference it from your Certificates/Ingresses.
  • Optionally, the add-on can create a ClusterIssuer for you: set certManager.enableClusterIssuer: true with issuerType: letsencrypt (a public Let's Encrypt ACME issuer; email is required for expiry notices) or issuerType: selfsigned (internal/testing). Leave it off to manage issuers yourself.

Karpenter

  • AWS only (EKS and self-hosted). On EKS it is installed with the cluster and managed as part of it (upgrade-only; not independently deletable). Installs the Karpenter controller into kube-system. Spot-interruption handling is off unless you enable it. Needs AWS identity: see Karpenter cloud access.

Install

Three routes, same result: pick whichever you prefer.

Console (UI)

  1. Open your cluster and go to the Add-ons tab.
  2. Click Install Add-on and pick from the searchable, category-grouped catalog (Networking, Security, Autoscaling). Add-ons that don't fit this cluster's cloud are marked incompatible.
  3. Each add-on installs with sensible defaults: the form shows only the choice that matters (for NGINX, whether TLS is terminated at the ingress or passed through). Open Advanced settings to tune anything the API supports: sizing/autoscaling, node selectors, the cert-manager ClusterIssuer, the ExternalDNS domain filter, and so on. Inputs are validated inline.
  4. After install, the add-on's row opens a detail view with live Events, Logs, and History (roll back from there).

CLI (manifest)

Declare the add-on in a small K8sClusterAddon manifest and apply it: good for GitOps and reproducible setups. Ready-to-copy examples for every add-on, with all options annotated, live in the public astro-platform-apps repo.

cert-manager.yaml
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: cert-manager # which add-on
spec:
clusterName: production # which cluster
configuration:
certManager:
installCRDs: true
astroctl infra k8s addons apply -f cert-manager.yaml
astroctl infra k8s addons status production cert-manager # watch it converge

apply is declarative and idempotent: edit spec.version or the configuration and apply the same file again to upgrade or reconfigure.

Nova (AI)

Describe what you want in plain language. Nova generates and applies the same add-on intent the console and CLI produce (the manifest, the configuration, the apply), then reports back on health once it converges.

"Install NGINX, cert-manager with a Let's Encrypt issuer, and ExternalDNS for example.com on my production cluster."

Test it locally (kind / any cluster without a cloud load balancer)

You can exercise the whole flow on a local cluster (for example kind), no cloud account needed:

  • NGINX: install with Expose via NodePort enabled (Advanced settings, or nginx.enableNodePort: true). A local cluster has no cloud LoadBalancer, so NodePort is how the controller is reachable. Then port-forward it and curl:

    kubectl -n ingress-nginx port-forward svc/ingress-nginx-controller 8080:80
    curl -H 'Host: myapp.local' http://localhost:8080/ # routes to your app's Ingress
  • cert-manager: enable a ClusterIssuer with issuerType: selfsigned. It needs no ACME account, public DNS, or email, so astro-selfsigned becomes Ready entirely offline, ideal for validating TLS wiring in kind.

  • ExternalDNS: skip it locally. It needs a real cloud DNS zone and cloud identity to create records, which a local cluster doesn't have.

Deploy any app with an Ingress (ingressClassName: nginx, a host:), and it's reachable through the NodePort exactly as it would be through a cloud LoadBalancer in production.

Observe

astroctl infra k8s addons get production # all add-ons, health at a glance
astroctl infra k8s addons get production nginx-ingress # one add-on in detail
astroctl infra k8s addons logs production nginx-ingress # workload logs
astroctl infra k8s addons events production nginx-ingress # deployment + K8s events
astroctl infra k8s addons history production nginx-ingress # version history

You can also ask Nova to show an add-on's health, logs, or version history.

Upgrade and roll back

astroctl infra k8s addons versions production cert-manager # curated versions
# then set spec.version in your manifest and re-apply
astroctl infra k8s addons rollback production cert-manager # previous eligible version

The console shows an Upgrade action on an installed add-on whenever a newer curated version is available.

Nova can upgrade or roll back an add-on the same way. Because rollback is destructive, Nova will ask you to confirm the exact version to roll back to: you choose the target; it is never implicit.

Uninstall

astroctl infra k8s addons delete production cert-manager

Uninstall is destructive, so via Nova, Nova hands you the exact console or CLI step to run and you confirm it, rather than deleting the add-on on its own.

Add-ons are independent: each one installs and uninstalls on its own. ExternalDNS, for example, works with any ingress controller or plain LoadBalancer Services, so it neither requires NGINX Ingress nor blocks its removal. One rule protects your cluster:

  • Karpenter is cluster-managed on EKS. On EKS clusters provisioned by AstroPulse it is installed as part of the cluster itself, appears here for visibility and upgrades, and cannot be deleted on its own; it is removed when the cluster is deleted. On AWS self-hosted clusters Karpenter is a regular add-on: install and uninstall it like any other.

ExternalDNS: DNS automation

ExternalDNS watches your Ingresses and Services and creates the matching records in your cloud DNS zone, so a hostname you put on an Ingress becomes a live DNS record automatically. It runs on your cluster and authenticates to your DNS provider with the cluster's own identity: AstroPulse never receives, stores, or asks for DNS credentials, and cannot act on your DNS account itself.

Before you install: give the cluster permission to your DNS

ExternalDNS runs on your cluster and needs an identity that is allowed to change records in your DNS zone. This is the one thing you must set up yourself: ExternalDNS deploys and runs healthy without it, but creates no records until the identity exists. It is also the most common reason people say "I installed it and nothing happened."

Identity only: the managed add-on never takes a secret

The managed add-on authenticates only with the cluster's ambient cloud identity: IRSA / EKS Pod Identity (AWS), Workload Identity (GCP/Azure), or a node role. It has no credential fields, and AstroPulse never receives, mounts, or stores DNS credentials. This is a deliberate security decision. There is nothing to leak.

Need to authenticate with a static access key or Secret instead? Then don't use the managed add-on: see Route B below.

Step 1 — Pick your route

Which route applies depends on whether your cluster has workload/ambient identity available. That is independent of who created the cluster: you can provision an AstroPulse cluster with or without IRSA / Workload Identity enabled, and bring-your-own clusters vary by how they were built. Check what your cluster actually has, then follow the matching route:

Your clusterWorkload / ambient identityRoute
AstroPulse-provisioned with IRSA / Workload IdentityAvailableA: grant DNS permission to the identity
AstroPulse-provisioned without IRSA / Workload IdentityNot enabledEnable it on the cluster, then A, or use B
Registered / bring-your-own — EKS, GKE, AKSUsually available (IRSA / WI)A
Registered / bring-your-own — kOps / self-managed on a cloudNode role availableA (node role)
On-prem, bare-metal, or a provider with only static API keysNoneB

Route A — Workload / ambient identity (use the managed add-on)

Set up the identity for your cluster's cloud and grant it DNS permission. In every case the identity binds to the external-dns service account in the external-dns namespace (where the add-on runs). On AstroPulse-provisioned clusters that already have identity enabled, astroctl cloud <provider> connect --cluster <name> can wire the trust relationship for you. You then only attach the DNS permission below.

AWS · Route 53: IRSA / Pod Identity (EKS) or node role (self-managed)

  1. Create an IAM policy allowing ExternalDNS to update Route 53: route53:ChangeResourceRecordSets on your hosted zone, plus route53:ListHostedZones, route53:ListResourceRecordSets, and route53:ListTagsForResources. Scope the zone ARN to your zone(s) for least privilege.
  2. EKS: ensure the cluster's OIDC provider is associated, create an IAM role trusting system:serviceaccount:external-dns:external-dns, and attach the policy. The chart annotates the service account, so the pod assumes the role automatically. (astroctl cloud aws connect can set up the OIDC trust on provisioned clusters.)
  3. Self-managed / kOps: attach the policy to the node instance role instead. ExternalDNS picks it up from instance metadata. No IRSA required.

GCP · Cloud DNS: Workload Identity (GKE)

  1. Enable Workload Identity on the cluster.
  2. Create a Google service account and grant it DNS Administrator (roles/dns.admin) on the project that owns the Cloud DNS zone.
  3. Bind it to external-dns/external-dns (namespace/serviceaccount) via Workload Identity (iam.gke.io/gcp-service-account annotation + roles/iam.workloadIdentityUser).

Azure · Azure DNS: Workload Identity (AKS)

  1. Enable the OIDC issuer and Workload Identity on the cluster and create a managed identity.
  2. Assign it DNS Zone Contributor on the DNS zone and Reader on the zone's resource group.
  3. Create a federated credential binding the identity to system:serviceaccount:external-dns:external-dns.

Once the identity is in place, install ExternalDNS with Dry run on (below), confirm from its logs that it sees your zone, then re-apply with Dry run off.

Route B — Static credentials (deploy ExternalDNS as an app)

If your cluster has no workload/ambient identity, or you specifically want to authenticate with a DNS API key or Secret, the managed add-on is not the right tool. It is intentionally credential-free. Instead, deploy ExternalDNS yourself as a regular AstroPulse application, with your credential mounted as a Kubernetes Secret and referenced by the chart values. Ready-to-adapt manifests live in the astro-platform-apps repo (apps/external-dns/aws.yaml, apps/external-dns/gcp.yaml). Applications are yours to run however you like; the managed add-on stays credential-free.

Configuration options

OptionWhat it is
DNS providerWhich cloud DNS service manages your zone: AWS Route 53, Google Cloud DNS, or Azure DNS. Defaults to your cluster's cloud.
Domain filterAn exact DNS zone/domain suffix, e.g. example.com. ExternalDNS then manages only records at or below it (example.com, app.example.com, …). It is a suffix match, not a regex. No wildcards or patterns. Leave empty to manage every zone the identity can reach (not recommended).
Dry runExternalDNS logs the records it would create, update, or delete and writes nothing. Turn it on for the first install to validate the identity and domain filter, then re-apply with it off to go live.
Owner ID (automatic)ExternalDNS tags each record it manages with an owner ID (defaults to the cluster name) so it never touches records another cluster or tool owns. This matters when several clusters share one DNS zone; you don't set it.

How records get created

Deploy your app with a hostname under your Domain filter: an Ingress host: (NGINX Ingress powers custom domains) or a external-dns.alpha.kubernetes.io/hostname annotation on a Service. ExternalDNS reads it and creates the matching A/CNAME record in your zone. Remove the Ingress/Service and the record is cleaned up.

Recipe: automatic public HTTPS

Install NGINX Ingress, ExternalDNS, and cert-manager together and a new app gets a public DNS name and a trusted TLS certificate with no manual steps: you only declare the hostname on the Ingress. The three compose like this: ExternalDNS publishes the DNS record, cert-manager obtains the certificate via Let's Encrypt, and NGINX terminates HTTPS with it.

  1. ExternalDNS: set the Domain filter to your zone (e.g. example.com) and give the cluster DNS permission (see the setup above).
  2. cert-manager: enable Create a Let's Encrypt ClusterIssuer with an ACME contact email. The add-on creates a ClusterIssuer named astro-letsencrypt whose ACME HTTP-01 challenge is solved through the nginx ingress class, so it works the moment NGINX is installed.
  3. NGINX Ingress: install with defaults (ingress class nginx).

Then give any app an Ingress that references both; ExternalDNS reads host, cert-manager reads the cluster-issuer annotation and tls block:

app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
cert-manager.io/cluster-issuer: astro-letsencrypt # cert-manager issues the cert
spec:
ingressClassName: nginx # NGINX terminates TLS
tls:
- hosts: [app.example.com]
secretName: my-app-tls # cert-manager fills this in
rules:
- host: app.example.com # ExternalDNS publishes this
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80

Apply it and, within a minute or two: app.example.com resolves to the NGINX load balancer, my-app-tls is populated with a Let's Encrypt certificate, and https://app.example.com serves trusted TLS. For a self-signed internal issuer instead, set cert-manager's ClusterIssuer type to self-signed and reference astro-selfsigned.

Karpenter cloud access

Like ExternalDNS, Karpenter reaches AWS with the cluster's own identity (an IRSA role or the node role with EC2/pricing/interruption-queue permissions), set up as part of the EKS cluster. AstroPulse never places AWS credentials in the add-on configuration.