Kubernetes Deployment¶
dnsweaver runs as a single-replica Deployment in Kubernetes, watching Ingress, IngressRoute, HTTPRoute, and Service resources for hostname extraction. It uses in-cluster authentication and requires read-only RBAC permissions.
Prerequisites¶
- Kubernetes 1.26+
kubectlaccess to the cluster- DNS provider credentials (API tokens)
Quick Start (Kustomize)¶
# Clone the repository
git clone https://github.com/maxfield-allison/dnsweaver.git
cd dnsweaver
# Edit the ConfigMap with your providers
$EDITOR deploy/kustomize/base/configmap.yaml
# Create the provider credentials Secret
kubectl create namespace dnsweaver
kubectl -n dnsweaver create secret generic dnsweaver-provider-credentials \
--from-literal=DNSWEAVER_INTERNAL_TOKEN=your-token-here
# Deploy
kubectl apply -k deploy/kustomize/base/
Helm¶
helm install dnsweaver deploy/helm/dnsweaver/ \
--namespace dnsweaver --create-namespace \
--set config.providers[0].name=internal \
--set config.providers[0].type=technitium \
--set config.providers[0].domains[0]="*.internal.example.com" \
--set config.providers[0].config.url=http://dns:5380 \
--set config.providers[0].config.zone=internal.example.com
Or use a values file:
helm install dnsweaver deploy/helm/dnsweaver/ \
--namespace dnsweaver --create-namespace \
-f my-values.yaml
See deploy/helm/dnsweaver/values.yaml for all options.
RBAC¶
dnsweaver needs read-only cluster-wide access to networking resources. The minimum ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dnsweaver
rules:
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["traefik.io"]
resources: ["ingressroutes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["httproutes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
Minimal RBAC
If you only use Traefik IngressRoutes, you can disable the other resource types and remove their RBAC rules. Set rbac.watchIngress=false, rbac.watchHTTPRoute=false, and rbac.watchServices=false in the Helm values.
Configuration¶
Platform Mode¶
Set platform: kubernetes in your config or use the DNSWEAVER_PLATFORM environment variable:
| Value | Behavior |
|---|---|
docker |
Watch Docker events only (default) |
kubernetes |
Watch Kubernetes resources only |
both |
Watch both Docker and Kubernetes simultaneously |
Kubernetes Settings¶
All settings can be configured via YAML config file or environment variables:
| YAML Key | Environment Variable | Default | Description |
|---|---|---|---|
kubernetes.kubeconfig |
DNSWEAVER_K8S_KUBECONFIG |
(empty) | Path to kubeconfig; empty = in-cluster |
kubernetes.namespaces |
DNSWEAVER_K8S_NAMESPACES |
(empty) | Comma-separated namespace filter; empty = all |
kubernetes.watch_ingress |
DNSWEAVER_K8S_WATCH_INGRESS |
true |
Watch networking.k8s.io/v1 Ingress |
kubernetes.watch_ingressroute |
DNSWEAVER_K8S_WATCH_INGRESSROUTE |
true |
Watch traefik.io/v1alpha1 IngressRoute |
kubernetes.watch_httproute |
DNSWEAVER_K8S_WATCH_HTTPROUTE |
true |
Watch gateway.networking.k8s.io/v1 HTTPRoute |
kubernetes.watch_services |
DNSWEAVER_K8S_WATCH_SERVICES |
false |
Watch v1 Service (opt-in) |
kubernetes.label_selector |
DNSWEAVER_K8S_LABEL_SELECTOR |
(empty) | Label selector filter |
kubernetes.annotation_filter |
DNSWEAVER_K8S_ANNOTATION_FILTER |
(empty) | Annotation key=value filter |
Resource Types¶
| Resource | API Group | Hostname Source |
|---|---|---|
| Ingress | networking.k8s.io/v1 |
.spec.rules[].host |
| IngressRoute | traefik.io/v1alpha1 |
Parsed from Host() matcher in .spec.routes[].match |
| HTTPRoute | gateway.networking.k8s.io/v1 |
.spec.hostnames[] |
| Service | v1 |
LoadBalancer ingress hostnames, ExternalName values |
Annotations¶
dnsweaver supports per-resource annotation overrides using the dnsweaver.dev/ prefix:
| Annotation | Values | Description |
|---|---|---|
dnsweaver.dev/enabled |
true / false |
Enable/disable DNS management for this resource |
dnsweaver.dev/record-type |
A, AAAA, CNAME, SRV, TXT |
Override the record type |
dnsweaver.dev/target |
IP or hostname | Override the DNS target |
dnsweaver.dev/ttl |
seconds (e.g., 300) |
Override the TTL |
dnsweaver.dev/provider |
provider name | Route to a specific provider |
dnsweaver.dev/proxied |
true / false |
Enable Cloudflare proxy |
Example: Ingress with Annotations¶
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
dnsweaver.dev/enabled: "true"
dnsweaver.dev/record-type: "A"
dnsweaver.dev/target: "10.0.0.100"
dnsweaver.dev/ttl: "300"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
Example: Traefik IngressRoute¶
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: my-app
annotations:
dnsweaver.dev/provider: "internal"
dnsweaver.dev/target: "10.0.0.100"
spec:
entryPoints:
- websecure
routes:
- match: Host(`app.internal.example.com`)
kind: Rule
services:
- name: my-app
port: 80
Health Endpoints¶
| Path | Description |
|---|---|
/health |
Liveness probe — returns 200 when the process is running |
/ready |
Readiness probe — returns 200 when watchers are synced |
/metrics |
Prometheus metrics (when enabled) |
Monitoring¶
Enable the ServiceMonitor for Prometheus scraping:
Troubleshooting¶
Pod not starting¶
kubectl -n dnsweaver logs deploy/dnsweaver
kubectl -n dnsweaver describe pod -l app.kubernetes.io/name=dnsweaver
RBAC errors¶
Look for 403 Forbidden in logs. Verify the ClusterRoleBinding references the correct namespace:
No hostnames detected¶
- Verify the resource type is being watched (
watch_ingress,watch_ingressroute, etc.) - Check namespace filters — empty means all namespaces
- Verify annotations:
dnsweaver.dev/enableddefaults totrueif absent - Check label/annotation selectors if configured
- Run with
logging.level: debugfor detailed extraction logs