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-on | What it does | Clusters |
|---|---|---|
NGINX Ingress (nginx-ingress) | HTTP/S ingress controller; routes external traffic to your apps and powers custom domains | All |
ExternalDNS (external-dns) | Publishes DNS records for your services and ingresses | All |
cert-manager (cert-manager) | Issues and renews TLS certificates automatically | All |
Karpenter (karpenter) | Just-in-time node autoscaling | AWS (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-on | Namespace | Service account |
|---|---|---|
| nginx-ingress | ingress-nginx | ingress-nginx |
| external-dns | external-dns | external-dns |
| cert-manager | cert-manager | cert-manager |
| karpenter | kube-system | karpenter |
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: trueto 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: truewithissuerType: letsencrypt(a public Let's Encrypt ACME issuer;emailis required for expiry notices) orissuerType: 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)
- Open your cluster and go to the Add-ons tab.
- 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.
- 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.
- 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.
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:80curl -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, soastro-selfsignedbecomes 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."
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 cluster | Workload / ambient identity | Route |
|---|---|---|
| AstroPulse-provisioned with IRSA / Workload Identity | Available | A: grant DNS permission to the identity |
| AstroPulse-provisioned without IRSA / Workload Identity | Not enabled | Enable it on the cluster, then A, or use B |
| Registered / bring-your-own — EKS, GKE, AKS | Usually available (IRSA / WI) | A |
| Registered / bring-your-own — kOps / self-managed on a cloud | Node role available | A (node role) |
| On-prem, bare-metal, or a provider with only static API keys | None | B |
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)
- Create an IAM policy allowing ExternalDNS to update Route 53:
route53:ChangeResourceRecordSetson your hosted zone, plusroute53:ListHostedZones,route53:ListResourceRecordSets, androute53:ListTagsForResources. Scope the zone ARN to your zone(s) for least privilege. - 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 connectcan set up the OIDC trust on provisioned clusters.) - 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)
- Enable Workload Identity on the cluster.
- Create a Google service account and grant it DNS Administrator
(
roles/dns.admin) on the project that owns the Cloud DNS zone. - Bind it to
external-dns/external-dns(namespace/serviceaccount) via Workload Identity (iam.gke.io/gcp-service-accountannotation +roles/iam.workloadIdentityUser).
Azure · Azure DNS: Workload Identity (AKS)
- Enable the OIDC issuer and Workload Identity on the cluster and create a managed identity.
- Assign it DNS Zone Contributor on the DNS zone and Reader on the zone's resource group.
- 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
| Option | What it is |
|---|---|
| DNS provider | Which cloud DNS service manages your zone: AWS Route 53, Google Cloud DNS, or Azure DNS. Defaults to your cluster's cloud. |
| Domain filter | An 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 run | ExternalDNS 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.
- ExternalDNS: set the Domain filter to your zone (e.g.
example.com) and give the cluster DNS permission (see the setup above). - cert-manager: enable Create a Let's Encrypt ClusterIssuer with an
ACME contact email. The add-on creates a ClusterIssuer named
astro-letsencryptwhose ACME HTTP-01 challenge is solved through thenginxingress class, so it works the moment NGINX is installed. - 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:
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.