English | 日本語
arca-dns is a high-availability, scalable authoritative DNS system with BGP Anycast + ECMP and a split control/data plane architecture.
arca-dns is designed for large-scale DNS deployments with the following features:
- Split Architecture: Separate control plane (management/signing) and data plane (distribution/routing)
- BGP Anycast + ECMP: Horizontal scaling with equal-cost multi-path routing
- DNSSEC: Central signing with automated key management
- Pluggable Backends: SQLite (default), PostgreSQL, MySQL, Git, or etcd for zone storage
- High Performance: Designed for high-throughput deployments
- Observability: DNSTap binary logging and Prometheus metrics
- REST API for zone management (JSON and raw BIND formats)
- Central DNSSEC signing (KSK/ZSK management)
- Pluggable storage backends (SQLite, PostgreSQL, MySQL, Git, etcd)
- Zone versioning and artifact distribution
- Zone synchronization from controller
- DNS server orchestration via plugin interfaces (NSD, Unbound, BIRD)
- BIRD BGP route control with health checking
- DNSTap logging and Prometheus metrics
- Automatic failover and recovery
This project is operated as a split control/data plane:
- Control plane (controller): Kubernetes / Docker Compose / packages
- Data plane (agent): packages on edge nodes (recommended)
The controller is a standard HTTP API service; TLS is typically terminated by an ingress/reverse proxy.
Kubernetes (recommended backend: etcd):
- Manifests are under
deployments/kubernetes/controller/(base + overlays). - Apply:
kubectl apply -k deployments/kubernetes/controller/base(external/HA etcd)kubectl apply -k deployments/kubernetes/controller/overlays/demo-etcd(demo-only; includes single-node etcd)
- Replace in manifests:
deployments/kubernetes/controller/base/controller-secret.yaml(dnssec-master-key-b64)deployments/kubernetes/controller/base/controller.yaml(API keys, etcd endpoints)- (Optional)
deployments/kubernetes/controller/examples/ingress.yaml(ingress host/class; TLS at ingress)
Docker Compose (Controller + MySQL example):
- Example compose file:
deployments/compose/controller-mysql/docker-compose.yaml - Run:
docker compose -f deployments/compose/controller-mysql/docker-compose.yaml --project-directory . up -d
DEB/RPM packages:
- Packaging assets live under
packaging/and are built via.goreleaser.yaml. - See
docs/packaging.mdfor how to build/install packages anddocs/deployment.mdfor operational setup.
The agent is designed to control NSD/Unbound/BIRD on the host and is typically deployed on edge nodes/VMs (not Kubernetes).
DEB/RPM packages (recommended):
- Install runtime deps: NSD, Unbound, BIRD (bird2 on Debian/Ubuntu).
- Install
arca-dnspackage (agent + controller binaries). - Configure
/etc/arca-dns/agent.yaml(based onconfigs/agent.example.yaml). - Start service:
systemctl enable --now arca-dns-agent
See docs/deployment.md and docs/operations.md for day-2 operations.
make install-toolsmake help # Show available targets
make install-tools # Install development tools (golangci-lint)
make deps # Download dependencies (go mod download/tidy)
make build # Build controller + agent binaries
make test # Run tests (-race + coverage.out)
make test-coverage # Generate coverage.html
make lint # Run linters
make fmt # Format code
make vet # Run go vet
make run-controller# Build + run controller (serve)
make run-agent # Build + run agent (daemon)
make docker-build # Build Docker images
make clean # Remove build artifactsSee docs/packaging.md.
make testmake lintmake test-coverageSee configs/controller.example.yaml and configs/agent.example.yaml, plus docs/deployment.md / docs/operations.md.
api:
listen: "0.0.0.0:8080"
# TLS is typically terminated by a reverse proxy / ingress.
backend:
type: "sqlite" # Options: sqlite, postgres, mysql, git, etcd, memory
dnssec:
enabled: true
algorithm: 13 # ECDSA-P256
key_directory: "/var/lib/arca-dns/keys"controller:
url: "http://localhost:8080"
sync_interval: "30s"
nsd:
config_path: "/etc/nsd/nsd.conf"
zone_directory: "/var/lib/nsd/zones"
unbound:
config_path: "/etc/unbound/unbound.conf"
edns_buffer_size: 1232 # ECMP fragment prevention
bird:
socket_path: "/var/run/bird/bird.ctl"
anycast_prefixes:
- "203.0.113.53/32"
health:
check_interval: "10s"
failure_threshold: 3
recovery_threshold: 5
nsd_server: "127.0.0.1:5353"
unbound_server: "127.0.0.1:53"Prerequisites:
- Go (see
go.mod)
Build:
make buildRun controller:
./bin/arca-dns-controller serve --config configs/controller.example.yamlRun agent (requires NSD/Unbound/BIRD installed on the host if enabled in config):
./bin/arca-dns-agent daemon --config configs/agent.example.yamlAPI documentation is available in OpenAPI format: api/openapi.yaml Contributing guide: docs/contributing.md
Create Zone (JSON):
curl -X POST http://localhost:8080/api/v1/zones \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"name": "example.com",
"soa": {
"mname": "ns1.example.com",
"rname": "admin.example.com",
"refresh": 3600,
"retry": 1800,
"expire": 604800,
"minimum": 86400
},
"records": [
{"name": "@", "type": "NS", "ttl": 3600, "value": "ns1.example.com."},
{"name": "@", "type": "NS", "ttl": 3600, "value": "ns2.example.com."},
{"name": "@", "type": "A", "ttl": 300, "value": "203.0.113.1"}
]
}'Create Zone (Raw BIND):
curl -X POST http://localhost:8080/api/v1/zones/raw \
-H "Content-Type: text/plain" \
-H "X-API-Key: your-api-key" \
--data-binary @example.com.zoneAdd 1 record (update zone via ETag / If-Match):
This API does not have a dedicated /records endpoint; update the full zone document with PUT /api/v1/zones/:name.
BASE="http://localhost:8080/api/v1"
API_KEY="your-api-key" # only if auth is enabled
zone_json="$(curl -s "${BASE}/zones/example.com." -H "X-API-Key: ${API_KEY}")"
etag="$(curl -sI "${BASE}/zones/example.com." -H "X-API-Key: ${API_KEY}" | awk -F': ' 'tolower($1)=="etag"{print $2}' | tr -d '\r')"
updated="$(printf '%s' "${zone_json}" | jq '.records += [{"name":"www","type":"A","ttl":300,"value":"203.0.113.2"}]')"
curl -i -X PUT "${BASE}/zones/example.com." \
-H "X-API-Key: ${API_KEY}" \
-H 'Content-Type: application/json' \
-H "If-Match: ${etag}" \
--data-binary "${updated}"Add multiple records at once:
updated="$(printf '%s' "${zone_json}" | jq '.records += [
{"name":"www","type":"A","ttl":300,"value":"203.0.113.2"},
{"name":"api","type":"AAAA","ttl":300,"value":"2001:db8::1"},
{"name":"@","type":"MX","ttl":3600,"value":"10 mail.example.com."}
]')"
curl -i -X PUT "${BASE}/zones/example.com." \
-H "X-API-Key: ${API_KEY}" \
-H 'Content-Type: application/json' \
-H "If-Match: ${etag}" \
--data-binary "${updated}"See docs/api.md for record value formats and more examples.
arca-dns/
├── cmd/
│ ├── arca-dns-controller/ # Controller main
│ └── arca-dns-agent/ # Agent main
├── pkg/
│ ├── config/ # Configuration structures
│ ├── model/ # Domain models
│ ├── backend/ # Storage backends
│ ├── dnssec/ # DNSSEC signing
│ ├── parser/ # Zone file parsing
│ └── protocol/ # API protocols
├── internal/
│ ├── agent/
│ │ ├── bird/ # BIRD BGP control
│ │ ├── nsd/ # NSD orchestration
│ │ ├── dnstap/ # DNSTap logging
│ │ └── sync/ # Zone synchronization
│ └── controller/
│ ├── api/ # API handlers
│ └── service/ # Business logic
├── api/ # OpenAPI specifications
├── configs/ # Example configurations
├── deployments/ # Docker/K8s manifests
└── test/ # Integration tests
Contributions are welcome! See docs/contributing.md.
For inquiries, open an issue on GitHub Issues. For security reports, use GitHub Security Advisories.
Apache License 2.0
Roadmap is tracked via GitHub issues/milestones.
For issues and questions, please use the GitHub issue tracker.