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 |
dnsweaver.dev/adopt |
true / false |
Enable or disable existing-record adoption. Enabling requires the matching provider's ADOPT_EXISTING_ALLOW_OVERRIDES gate. Disabling is always honored. |
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: "192.0.2.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: "192.0.2.100"
spec:
entryPoints:
- websecure
routes:
- match: Host(`app.internal.example.com`)
kind: Rule
services:
- name: my-app
port: 80
Health Endpoints¶
The Helm chart and Kustomize base bind the management listener to pod loopback and use local exec probes. No network listener is needed for normal liveness or readiness checks.
| Path | Description |
|---|---|
/health |
Liveness probe — returns 200 when the process is running |
/ready |
Readiness probe — returns 200 when watchers are synced |
/metrics |
Prometheus metrics (local-only by default) |
Monitoring¶
Enable the ServiceMonitor for Prometheus scraping:
# Helm values
config:
server:
address: 0.0.0.0
allowNetwork: true
serviceMonitor:
enabled: true
interval: 60s
This explicit opt-in is not authentication. Apply a NetworkPolicy that permits port 8080 only from the monitoring namespace (or an equivalent network restriction). The chart rejects a ServiceMonitor with its managed loopback configuration. With existingConfigMap, set server.address and server.allow_network in that ConfigMap because Helm cannot validate it.
For Kustomize, make the same two server changes in the ConfigMap and add a restrictive NetworkPolicy overlay. Keep the default exec probes; they remain compatible with either listener mode.
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