external-dns

Complete External-DNS operations for automatic DNS management in Kubernetes clusters.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "external-dns" with this command: npx skills add julianobarbosa/claude-code-skills/julianobarbosa-claude-code-skills-external-dns

External-DNS Skill

Complete External-DNS operations for automatic DNS management in Kubernetes clusters.

Overview

External-DNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers, eliminating manual DNS record management. This skill covers configuration, best practices, and troubleshooting across multiple DNS providers with emphasis on Azure and Cloudflare.

Provider Quick Reference

Provider Auth Method Status Reference

Azure DNS Workload Identity (recommended) or Service Principal Stable references/azure-dns.md

Cloudflare API Token Beta references/cloudflare.md

AWS Route53 IRSA (recommended) or Access Keys Stable Below

Google Cloud DNS Workload Identity Stable Below

Essential Helm Values Structure

kubernetes-sigs/external-dns chart (v1.18.0+)

fullnameOverride: external-dns

provider: name: <provider> # azure, cloudflare, aws, google

Sources to watch

sources:

  • service
  • ingress

Domain restrictions

domainFilters:

  • example.com

Policy: sync (creates/updates/deletes) or upsert-only (creates/updates only)

policy: upsert-only # Recommended for production

Sync interval

interval: "5m"

TXT record ownership (MUST be unique per cluster)

txtOwnerId: "aks-cluster-name" txtPrefix: "_externaldns."

Logging

logLevel: info # debug, info, warning, error logFormat: json

Resources

resources: requests: memory: "64Mi" cpu: "25m" limits: memory: "128Mi" # cpu: REMOVED per best practice (no CPU limits)

Security context

securityContext: runAsNonRoot: true runAsUser: 65534 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"]

Prometheus metrics

serviceMonitor: enabled: true interval: 30s

Azure DNS Configuration

Workload Identity (Recommended)

provider: name: azure

serviceAccount: labels: azure.workload.identity/use: "true" annotations: azure.workload.identity/client-id: "<MANAGED_IDENTITY_CLIENT_ID>"

podLabels: azure.workload.identity/use: "true"

env:

  • name: AZURE_TENANT_ID value: "<TENANT_ID>"
  • name: AZURE_SUBSCRIPTION_ID value: "<SUBSCRIPTION_ID>"
  • name: AZURE_RESOURCE_GROUP value: "<DNS_ZONE_RESOURCE_GROUP>"

domainFilters:

  • example.com

txtOwnerId: "aks-cluster-name" policy: upsert-only interval: "5m"

Required Azure RBAC Permissions

Assign DNS Zone Contributor role to the managed identity

az role assignment create
--role "DNS Zone Contributor"
--assignee "<MANAGED_IDENTITY_OBJECT_ID>"
--scope "/subscriptions/<SUB_ID>/resourceGroups/<RG>/providers/Microsoft.Network/dnszones/<ZONE>"

For Private DNS Zones

az role assignment create
--role "Private DNS Zone Contributor"
--assignee "<MANAGED_IDENTITY_OBJECT_ID>"
--scope "/subscriptions/<SUB_ID>/resourceGroups/<RG>/providers/Microsoft.Network/privateDnsZones/<ZONE>"

Service Principal Alternative

provider: name: azure

env:

  • name: AZURE_TENANT_ID value: "<TENANT_ID>"
  • name: AZURE_SUBSCRIPTION_ID value: "<SUBSCRIPTION_ID>"
  • name: AZURE_RESOURCE_GROUP value: "<DNS_ZONE_RESOURCE_GROUP>"
  • name: AZURE_CLIENT_ID valueFrom: secretKeyRef: name: azure-credentials key: client-id
  • name: AZURE_CLIENT_SECRET valueFrom: secretKeyRef: name: azure-credentials key: client-secret

Cloudflare Configuration

provider: name: cloudflare

env:

  • name: CF_API_TOKEN valueFrom: secretKeyRef: name: cloudflare-api-token key: cloudflare_api_token

extraArgs: cloudflare-proxied: true # Enable CDN/DDoS protection cloudflare-dns-records-per-page: 5000 # Optimize API calls

domainFilters:

  • example.com

txtOwnerId: "aks-cluster-name" policy: upsert-only

Cloudflare API Token Permissions

  • Zone:Read - List zones

  • DNS:Edit - Create/update/delete DNS records

  • Zone Resources: All zones or specific zones

AWS Route53 Configuration (IRSA)

provider: name: aws

env:

  • name: AWS_DEFAULT_REGION value: "us-east-1"

serviceAccount: annotations: eks.amazonaws.com/role-arn: "arn:aws:iam::<ACCOUNT_ID>:role/external-dns"

extraArgs: aws-zone-type: public # or private aws-batch-change-size: 4000

domainFilters:

  • example.com

txtOwnerId: "eks-cluster-name"

Required AWS IAM Policy

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["route53:ChangeResourceRecordSets"], "Resource": ["arn:aws:route53:::hostedzone/"] }, { "Effect": "Allow", "Action": ["route53:ListHostedZones", "route53:ListResourceRecordSets"], "Resource": [""] } ] }

Google Cloud DNS Configuration

provider: name: google

env:

  • name: GOOGLE_PROJECT value: "<GCP_PROJECT_ID>"

serviceAccount: annotations: iam.gke.io/gcp-service-account: "external-dns@<PROJECT_ID>.iam.gserviceaccount.com"

domainFilters:

  • example.com

txtOwnerId: "gke-cluster-name"

Kubernetes Resource Annotations

Basic Usage

On Service or Ingress

metadata: annotations: external-dns.alpha.kubernetes.io/hostname: "app.example.com" external-dns.alpha.kubernetes.io/ttl: "300"

Multiple Hostnames

metadata: annotations: external-dns.alpha.kubernetes.io/hostname: "app1.example.com,app2.example.com"

Provider-Specific Annotations

Cloudflare - disable proxy for specific record

external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"

AWS Route53 - create ALIAS record

external-dns.alpha.kubernetes.io/alias: "true"

Custom TTL

external-dns.alpha.kubernetes.io/ttl: "60"

Environment-Specific Best Practices

Development

policy: sync # Auto-delete orphaned records interval: "1m" # Fast sync for rapid iteration logLevel: info resources: requests: memory: "50Mi" cpu: "10m" limits: memory: "50Mi"

Production

policy: upsert-only # NEVER auto-delete interval: "10m" # Conservative to reduce API load logLevel: error # Minimal logging

High Availability

replicaCount: 2

affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: [external-dns] topologyKey: kubernetes.io/hostname

podDisruptionBudget: enabled: true minAvailable: 1

priorityClassName: high-priority

Common Commands

Check external-dns pods

kubectl get pods -n external-dns

View logs

kubectl logs -n external-dns deployment/external-dns --tail=100 -f

Check configuration

kubectl get deployment external-dns -n external-dns -o yaml | grep -A20 args

Verify DNS records (Cloudflare)

dig @1.1.1.1 app.example.com

Verify DNS records (Azure)

az network dns record-set list -g <RESOURCE_GROUP> -z example.com -o table

Check TXT ownership records

dig TXT _externaldns.app.example.com

Force restart

kubectl rollout restart deployment external-dns -n external-dns

Dry-run mode (add to extraArgs)

extraArgs: dry-run: true

Key Metrics

Total endpoints managed

external_dns_registry_endpoints_total

Sync errors

external_dns_controller_sync_errors_total

Last sync timestamp

external_dns_controller_last_sync_timestamp_seconds

DNS records by type

external_dns_registry_a_records external_dns_registry_aaaa_records external_dns_registry_cname_records

ArgoCD ApplicationSet Pattern

apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: external-dns namespace: argocd spec: generators: - list: elements: - cluster: dev branch: main - cluster: prd branch: main template: metadata: name: 'external-dns-{{cluster}}' spec: project: infrastructure sources: - chart: external-dns repoURL: https://kubernetes-sigs.github.io/external-dns/ targetRevision: "1.18.0" helm: releaseName: external-dns valueFiles: - $values/argo-cd-helm-values/kube-addons/external-dns/{{cluster}}/values.yaml - repoURL: https://your-repo.git targetRevision: "{{branch}}" ref: values destination: server: '{{url}}' namespace: external-dns syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true

Security Checklist

  • Use Workload Identity/IRSA instead of static credentials

  • Grant least privilege permissions to DNS zones

  • Set runAsNonRoot: true and readOnlyRootFilesystem: true

  • Use unique txtOwnerId per cluster

  • Restrict domainFilters to necessary domains

  • Store API tokens in Kubernetes Secrets

  • Enable Pod Security Standards (restricted)

  • Use policy: upsert-only in production

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

obsidian-vault-management

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

zabbix

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

neovim

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

obsidian

No summary provided by upstream source.

Repository SourceNeeds Review