A webhook provider for External DNS that enables automatic DNS record management for Rackspace Cloud DNS.
This webhook integrates External DNS with Rackspace Cloud DNS, allowing Kubernetes services and ingresses to automatically create, update, and delete DNS records in your Rackspace-managed domains.
- Automatic DNS Management: Creates and manages DNS records based on Kubernetes services and ingresses
- Multiple Record Types: Supports A, AAAA, CNAME, TXT, and other standard DNS record types
- Domain Filtering: Configure which domains the webhook should manage
- TTL Management: Configurable TTL values with automatic normalization (minimum 300s)
- Dry Run Mode: Test changes without actually modifying DNS records
- Health Checks: Built-in health endpoints for monitoring
- Kubernetes cluster
- Helm 3.8.0 or higher
- Rackspace Cloud DNS account with API access
- Rackspace username and API key
The recommended installation method uses the official external-dns Helm chart from the kubernetes-sigs project, with this webhook running as a sidecar container.
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm repo updatekubectl create namespace external-dns
kubectl create secret generic rackspace-credentials \
--namespace external-dns \
--from-literal=username="YOUR_USERNAME" \
--from-literal=api-key="YOUR_API_KEY"# external-dns-rackspace-values.yaml
provider:
name: webhook
webhook:
image:
repository: ghcr.io/rackerlabs/external-dns-rackspace-webhook
tag: v0.3.1
env:
- name: RACKSPACE_USERNAME
valueFrom:
secretKeyRef:
name: rackspace-credentials
key: username
- name: RACKSPACE_API_KEY
valueFrom:
secretKeyRef:
name: rackspace-credentials
key: api-key
- name: LOG_LEVEL
value: info
- name: DRY_RUN
value: "false"
service:
port: 8888
livenessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2
readinessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
sources:
- service
- ingress
domainFilters:
- example.com # replace with your domain(s)
policy: upsert-only
logLevel: infoNote:
service.port: 8888must match the webhook'sPORTenv var (default8888). The chart uses this value to configure--webhook-provider-urlautomatically and to route health probe traffic.
helm upgrade --install external-dns external-dns/external-dns \
--namespace external-dns \
-f external-dns-rackspace-values.yamlhelm upgrade external-dns external-dns/external-dns \
--namespace external-dns \
-f external-dns-rackspace-values.yaml| Variable | Required | Default | Description |
|---|---|---|---|
RACKSPACE_USERNAME |
Yes | - | Rackspace account username |
RACKSPACE_API_KEY |
Yes | - | Rackspace API key |
RACKSPACE_TENANT_ID |
No | - | Rackspace tenant ID |
DOMAIN_FILTER |
No | - | Comma-separated list of domains to manage |
DRY_RUN |
No | false |
Enable dry run mode (no actual changes) |
LOG_LEVEL |
No | info |
Log level (debug, info, warn, error) |
PORT |
No | 8888 |
HTTP server port |
See the external-dns chart documentation for the full list. Commonly used values:
| Value | Description |
|---|---|
domainFilters |
List of domains to manage |
policy |
upsert-only (safe default) or sync (enables deletions) |
sources |
Kubernetes resource types to watch (service, ingress, crd, etc.) |
interval |
Sync interval (default 1m) |
logLevel |
external-dns log level |
resources |
CPU/memory limits for the external-dns container |
provider.webhook.resources |
CPU/memory limits for the webhook sidecar |
Start with these defaults unless you have a reason to change them:
policy: upsert-only
registry: txt
txtOwnerId: my-cluster-prod
txtPrefix: external-dns-| Flag | Use it when | Notes |
|---|---|---|
--domain-filter=example.com |
Always | Keeps external-dns away from domains this install should not manage. |
--policy=upsert-only |
First install, testing, or any zone where deletes must be manual | Creates and updates records, but does not delete records when Kubernetes objects disappear. |
--policy=sync |
You want external-dns to remove stale records automatically | Use only with a tight domain-filter and a stable ownership registry. This is what removes old records. |
--registry=txt |
Production or any shared Rackspace zone | Creates TXT ownership records so external-dns only changes records owned by this install. |
--txt-owner-id=<id> |
Any time registry=txt is enabled |
Pick one stable value per cluster/environment. Changing it makes existing records look unowned. |
--txt-prefix=external-dns- |
Recommended with TXT registry | Keeps ownership records separate from user TXT records and supports CNAME ownership cleanly. |
--registry=noop |
Short-lived tests where ownership records are unwanted | Pair with upsert-only unless you are intentionally managing every matching record. |
--source=service,ingress,... |
To choose what Kubernetes objects create DNS | Use only the sources you actually need. Fewer sources are easier to reason about. |
--interval=1m or higher |
Normal reconciliation | Short intervals are useful in tests; production usually does not need sub-minute polling. |
Use sync when Kubernetes should be the source of truth for the selected domain. Use upsert-only when DNS deletion needs a human review step. Use TXT ownership in production; use noop only when you deliberately do not want ownership records.
# Build binary
make build
# Build Docker image
make docker-build
# Run tests
make test
# Run Kubernetes e2e tests against OrbStack/current kubectl context
make e2eexport RACKSPACE_USERNAME="your-username"
export RACKSPACE_API_KEY="your-api-key"
export DOMAIN_FILTER="example.com"
make runmake e2e builds the webhook image, starts a mock Rackspace Identity/Cloud DNS API in Kubernetes, and runs ExternalDNS against the webhook in the current kubectl context. It is designed for OrbStack and also works with any cluster that can run locally built Docker images.
The e2e test covers:
upsert-onlywithnoopregistry: creates records and confirms source deletion does not remove DNS.syncwithtxtregistry,--txt-owner-id, and--txt-prefix: creates records, removes seeded older owned records, and removes records after the source is deleted.
Requirements: Docker, kubectl, curl, and jq.
GET /- Negotiation endpoint (returns domain filter)GET /records- Retrieve all DNS recordsPOST /records- Apply DNS record changesPOST /adjustendpoints- Normalize and validate endpointsGET /healthz- Health check endpoint
docker run -e RACKSPACE_USERNAME=user -e RACKSPACE_API_KEY=key \
ghcr.io/rackerlabs/external-dns-rackspace-webhook:latestdocker build -t external-dns-rackspace-webhook .- Authentication Errors: Verify your Rackspace username and API key
- Domain Not Found: Ensure the domain exists in your Rackspace Cloud DNS
- Permission Denied: Check that your API key has DNS management permissions
- TTL Too Low: The webhook enforces a minimum TTL of 300 seconds
SRV record support requires external-dns v0.21.0 or later:
-
Versions prior to 0.21 do not include SRV in the TXT registry's
getSupportedTypes(). This prevents the registry from matchingsrv-prefixed TXT ownership records to their SRV data records, causing all SRV records to appear unowned. external-dns will not update or delete them. -
Version 0.21.0 fixes the ownership matching but introduces contradictory SRV validation in the CRD source. The CRD validator rejects targets with a trailing dot, while
ValidateSRVRecordrequires one per RFC 2782. This makes it impossible to create SRV records via DNSEndpoint CRDs. See kubernetes-sigs/external-dns#6357 and the fix in PR #6383.
Enable debug logging by setting LOG_LEVEL in your values file:
provider:
webhook:
env:
- name: LOG_LEVEL
value: debug
logLevel: debug- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This project is licensed under the terms specified in the LICENSE file.
For issues and questions:
- Create an issue in this repository
- Check the External DNS documentation
- Review Rackspace Cloud DNS API documentation