Step-by-Step Guide¶
This comprehensive guide walks you through the complete process of installing Bindy and creating your first DNS infrastructure, from storage provisioning to DNS testing.
Step 1: Install Storage Provisioner (Optional)¶
For persistent zone data storage, install a storage provisioner. For Kind clusters or local development:
# Install local-path provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.28/deploy/local-path-storage.yaml
# Wait for provisioner to be ready
kubectl wait --for=condition=available --timeout=60s \
deployment/local-path-provisioner -n local-path-storage
# Set as default StorageClass (or create one if it doesn't exist)
if kubectl get storageclass local-path &>/dev/null; then
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
else
# Create default StorageClass if local-path wasn't created
cat <<EOF | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: default
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
EOF
fi
# Verify StorageClass is available
kubectl get storageclass
Note: For production clusters, use your cloud provider's StorageClass (AWS EBS, GCP PD, Azure Disk, etc.)
Step 2: Install Bindy¶
# Create namespace
kubectl create namespace dns-system
# Install CRDs from latest release
kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/crds.yaml
# Install RBAC from latest release
kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/rbac/serviceaccount.yaml
kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/rbac/role.yaml
kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/rbac/rolebinding.yaml
# Deploy operator from latest release
kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/operator/deployment.yaml
# Wait for operator to be ready
kubectl wait --for=condition=available --timeout=300s \
deployment/bind9-operator -n dns-system
Step 3: Create a BIND9 Cluster¶
First, create a cluster configuration that defines shared settings:
Create a file bind9-cluster.yaml:
apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Cluster
metadata:
name: production-dns
namespace: dns-system
spec:
version: "9.18"
global:
recursion: false
allowQuery:
- "0.0.0.0/0"
allowTransfer:
- "10.0.0.0/8"
⚠️ Warning: There are NO defaults for
allowQueryandallowTransfer. If you don't specify these fields, BIND9's default behavior applies (no queries or transfers allowed). Always explicitly configure these fields for your security requirements.
Apply it:
Optional: Add Persistent Storage¶
To persist zone data across pod restarts, you can add PersistentVolumeClaims to your Bind9Cluster or Bind9Instance.
First, create a PVC for zone data storage:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: bind9-zones-pvc
namespace: dns-system
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
# Uses default StorageClass if not specified
# storageClassName: local-path
Then update your Bind9Cluster to use the PVC:
apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Cluster
metadata:
name: production-dns
namespace: dns-system
spec:
version: "9.18"
global:
recursion: false
allowQuery:
- "0.0.0.0/0"
allowTransfer:
- "10.0.0.0/8"
# Add persistent storage for zones
volumes:
- name: zones
persistentVolumeClaim:
claimName: bind9-zones-pvc
volumeMounts:
- name: zones
mountPath: /var/cache/bind
Or add storage to a specific Bind9Instance:
apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Instance
metadata:
name: primary-dns
namespace: dns-system
spec:
clusterRef: production-dns
role: primary # Required: primary or secondary
replicas: 1
# Instance-specific storage (overrides cluster-level)
volumes:
- name: zones
persistentVolumeClaim:
claimName: bind9-primary-zones-pvc
volumeMounts:
- name: zones
mountPath: /var/cache/bind
Note: When using PVCs with
accessMode: ReadWriteOnce, each replica needs its own PVC since the volume can only be mounted by one pod at a time. For multi-replica setups, useReadWriteManyif your storage class supports it, or create separate PVCs per instance.
Step 4: Create a BIND9 Instance¶
Now create an instance that references the cluster:
Create a file bind9-instance.yaml:
apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Instance
metadata:
name: primary-dns
namespace: dns-system
spec:
clusterRef: production-dns # References the Bind9Cluster
role: primary # Required: primary or secondary
replicas: 1
Apply it:
Step 5: Create a DNS Zone¶
Create a file dns-zone.yaml:
apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
name: example-com
namespace: dns-system
spec:
zoneName: example.com
clusterRef: production-dns # References the Bind9Cluster
soaRecord:
primaryNs: ns1.example.com.
adminEmail: admin.example.com. # Note: @ replaced with .
serial: 2024010101
refresh: 3600
retry: 600
expire: 604800
negativeTtl: 86400
ttl: 3600
Apply it:
Step 6: Add DNS Records¶
Create a file dns-records.yaml:
Note: DNS records reference zones using labels. The operator uses an event-driven architecture that watches all 8 record types (ARecord, AAAARecord, TXTRecord, CNAMERecord, MXRecord, NSRecord, SRVRecord, CAARecord). When you create a record, the DNSZone operator receives a watch event immediately (⚡ sub-second), evaluates label selectors, and sets
status.zoneRefif the record matches. The record operator then watches for this status change and reconciles to BIND9 instantly. Total time: ~500ms from record creation to BIND9 update ✅
# Web server A record
apiVersion: bindy.firestoned.io/v1beta1
kind: ARecord
metadata:
name: www-example
namespace: dns-system
labels:
zone: example.com # Matches DNSZone selector
spec:
name: www
ipv4Address: "192.0.2.1"
ttl: 300
---
# Blog CNAME record
apiVersion: bindy.firestoned.io/v1beta1
kind: CNAMERecord
metadata:
name: blog-example
namespace: dns-system
labels:
zone: example.com # Matches DNSZone selector
spec:
name: blog
target: www.example.com.
ttl: 300
---
# Mail server MX record
apiVersion: bindy.firestoned.io/v1beta1
kind: MXRecord
metadata:
name: mail-example
namespace: dns-system
labels:
zone: example.com # Matches DNSZone selector
spec:
name: "@"
priority: 10
mailServer: mail.example.com.
ttl: 3600
---
# SPF TXT record
apiVersion: bindy.firestoned.io/v1beta1
kind: TXTRecord
metadata:
name: spf-example
namespace: dns-system
labels:
zone: example.com # Matches DNSZone selector
spec:
name: "@"
text:
- "v=spf1 include:_spf.example.com ~all"
ttl: 3600
Apply them:
Step 7: Verify Your DNS Configuration¶
Check the status of your resources:
# Check BIND9 cluster
kubectl get bind9clusters -n dns-system
# Check BIND9 instance
kubectl get bind9instances -n dns-system
# Check DNS zone
kubectl get dnszones -n dns-system
# Check DNS records
kubectl get arecords,cnamerecords,mxrecords,txtrecords -n dns-system
# View detailed status
kubectl describe dnszone example-com -n dns-system
You should see output like:
Step 8: Test DNS Resolution¶
If your BIND9 instance is exposed (via LoadBalancer or NodePort):
# Get the BIND9 service IP
kubectl get svc -n dns-system
# Test DNS query (replace <BIND9-IP> with actual IP)
dig @<BIND9-IP> www.example.com
dig @<BIND9-IP> blog.example.com
dig @<BIND9-IP> example.com MX
dig @<BIND9-IP> example.com TXT
What's Next?¶
You've successfully deployed Bindy and created your first DNS zone with records!
Learn More¶
- Protocol Reference - Understand RNDC and HTTP API protocols
- Architecture Overview - Understand how Bindy works
- Multi-Region Setup - Deploy across multiple regions
- Status Conditions - Monitor resource health
Common Next Steps¶
- Add Secondary DNS Instances for high availability
- Configure Zone Transfers between primary and secondary
- Set up Monitoring to track DNS performance
- Integrate with GitOps for automated deployments
- Configure DNSSEC for enhanced security
Production Checklist¶
Before going to production:
- Deploy multiple operator replicas for HA
- Set up primary and secondary DNS instances
- Configure resource limits and requests
- Enable monitoring and alerting
- Set up backup for CRD definitions
- Configure RBAC properly
- Review security settings
- Test disaster recovery procedures
Troubleshooting¶
If something doesn't work:
-
Check operator logs:
-
Check resource status:
-
Verify CRDs are installed:
See the Troubleshooting Guide for more help.