A CoreDNS plugin for managing custom DNS records via HTTP API. This service is containerized and works with Kubernetes.
- Custom DNS Records API: Manage A and CNAME records via HTTP API
- Forward to External DNS: Forward unresolved queries to external DNS servers (e.g., Cloudflare, Google DNS)
- Docker Support: Containerized deployment with Docker Compose
- Kubernetes Support: Designed to run in Kubernetes environments
- Health Endpoint:
/healthendpoint for monitoring and K8s compatibility - High Availability Compatible: Designed for multi-instance deployments
- Docker and Docker Compose (for local testing)
- A domain for DNS resolution
- Clone the repository:
git clone https://github.com/christian-deleon/netbird-coredns.git
cd netbird-coredns/docker- Create environment file:
cp example.env .env- Edit
.envwith your configuration:
# Required settings
NBDNS_DOMAINS=mydomain.com
NBDNS_SETUP_KEY=your-netbird-setup-key
# Optional settings
NBDNS_FORWARD_TO=8.8.8.8
NBDNS_API_PORT=8080
NBDNS_DNS_PORT=5053
NBDNS_REFRESH_INTERVAL=15- Start the service:
docker compose up -dOr using the Justfile:
just start- Check logs:
docker compose logs -fOr:
just logsAll environment variables are prefixed with NBDNS_:
| Variable | Required | Default | Description |
|---|---|---|---|
NBDNS_DOMAINS |
Yes | - | Comma-separated domains for DNS resolution |
NBDNS_SETUP_KEY |
Yes | - | NetBird setup key for peer registration |
NBDNS_MANAGEMENT_URL |
No | https://api.netbird.io |
NetBird Management server URL (use custom URL for self-hosted) |
NBDNS_HOSTNAME |
No | nb-dns |
Hostname for NetBird peer registration |
NBDNS_DNS_LABELS |
No | nb-dns |
DNS labels for service discovery (comma-separated) |
NBDNS_FORWARD_TO |
No | 8.8.8.8 |
Forward server for unresolved queries |
NBDNS_DNS_PORT |
No | 5053 |
DNS server port (use different port if 53 is in use) |
NBDNS_API_PORT |
No | 8080 |
API server port |
NBDNS_REFRESH_INTERVAL |
No | 15 |
Refresh interval in seconds |
NBDNS_RECORDS_FILE |
No | /etc/nb-dns/records/records.json |
Path to DNS records file |
NBDNS_LOG_LEVEL |
No | info |
Log level for the entire service (debug, info, warn, error) |
The NBDNS_DOMAINS environment variable specifies which domains this DNS server will handle. The configured domains determine which DNS queries will be processed by this service. Queries for other domains will be forwarded to the external DNS server specified in NBDNS_FORWARD_TO.
Note: The domain configured in NBDNS_DOMAINS is independent of any NetBird peer configuration. If you're using NetBird, the peer domain (determined by your NetBird Management server - whether official or self-hosted) can be different from NBDNS_DOMAINS.
The service provides an HTTP API for managing custom DNS records.
GET /healthReturns 200 OK when the service is healthy.
Example:
curl http://localhost:8080/healthResponse:
{
"status": "ok"
}GET /api/v1/recordsReturns all DNS records organized by domain.
Example:
curl http://localhost:8080/api/v1/recordsResponse:
{
"example.com": {
"web": {
"name": "web",
"domain": "example.com",
"type": "A",
"value": "192.168.1.100",
"ttl": 60
},
"api": {
"name": "api",
"domain": "example.com",
"type": "CNAME",
"value": "web.example.com",
"ttl": 60
}
}
}POST /api/v1/records
Content-Type: application/json
{
"name": "web",
"domain": "example.com",
"type": "A",
"value": "192.168.1.100"
}Supported record types: A, CNAME
Example:
# Create A record
curl -X POST http://localhost:8080/api/v1/records \
-H "Content-Type: application/json" \
-d '{
"name": "web",
"domain": "example.com",
"type": "A",
"value": "192.168.1.100"
}'
# Create CNAME record
curl -X POST http://localhost:8080/api/v1/records \
-H "Content-Type: application/json" \
-d '{
"name": "www",
"domain": "example.com",
"type": "CNAME",
"value": "web.example.com"
}'PUT /api/v1/records/{domain}/{name}
Content-Type: application/json
{
"type": "A",
"value": "192.168.1.101"
}Example:
curl -X PUT http://localhost:8080/api/v1/records/example.com/web \
-H "Content-Type: application/json" \
-d '{
"type": "A",
"value": "192.168.1.101"
}'DELETE /api/v1/records/{domain}/{name}Example:
curl -X DELETE http://localhost:8080/api/v1/records/example.com/webOnce the service is running, you can resolve DNS queries using the configured DNS port (default 5053).
Testing with localhost:
# Resolve custom A record
dig +short web.example.com @localhost -p 5053
# Resolve CNAME record
dig +short www.example.com @localhost -p 5053 CNAMETesting with DNS hostname:
If the service is accessible via a hostname (e.g., in a Kubernetes cluster or via DNS), you can query it directly:
# Query using the DNS server hostname
dig +short web.example.com @dns-server.example.com -p 5053To test the service:
- Ensure you have a valid NetBird setup key from your NetBird Management console
- Configure the required environment variables (
NBDNS_DOMAINSandNBDNS_SETUP_KEY) - The service will register as a NetBird peer and become discoverable via the DNS label (default:
nb-dns) - Access the DNS service via:
- localhost:
dig +short web.example.com @localhost -p 5053 - NetBird DNS label:
dig +short web.example.com @nb-dns.<netbird-domain>(from other NetBird peers)
- localhost:
- Manage DNS records via the API on port 8080
Deploy multiple instances for high availability:
- Each instance serves the same DNS records
- Load balance DNS queries across instances
- Use a shared persistent volume for the records file in Kubernetes
The project includes a Justfile with convenient commands. See just list for all available commands.
Using Docker Compose directly:
# Start services
docker compose up -d
# View logs
docker compose logs -f
# Stop services
docker compose down
# Rebuild and restart
docker compose up -d --build
# Remove everything including volumes
docker compose down -vUsing Justfile:
# Start services
just start
# View logs
just logs
# Stop services
just stop
# View processes
just ps
# Clean everything
just clean
# Build binary
just build
# Run locally
just run- Verify domain configuration matches
- Test with the correct port:
dig +short @localhost -p 5053 hostname.domain.com - Check records exist:
curl http://localhost:8080/api/v1/records
If port 53 is already in use:
- Change the DNS port in
.env:NBDNS_DNS_PORT=5053 - Update the port mapping in
docker/compose.ymlto match - Test with:
dig +short @localhost -p 5053 hostname.domain.com
- Check if API server is running:
curl http://localhost:8080/health - Verify
NBDNS_API_PORTis not in use - Check firewall rules for port access
- Verify records file path:
NBDNS_RECORDS_FILE - Check volume mount in
compose.ymlor Kubernetes PersistentVolume - Ensure write permissions on records directory
- CoreDNS: DNS server with custom plugin
- API Server: HTTP API for managing custom DNS records
- Process Manager: Orchestrates all components and handles shutdown
- Custom CNAME records (from API)
- Custom A records (from API)
- Forward to external DNS (configured forward server)
DNS Query → CoreDNS → Custom Plugin → Check Custom Records
↓
Forward to External DNS
# Clone repository
git clone https://github.com/christian-deleon/netbird-coredns.git
cd netbird-coredns
# Build binary
go build -o netbird-coredns ./cmd/netbird-coredns
# Or use Justfile
just build# Set required environment variables
export NBDNS_DOMAINS=mydomain.com
# Run the service
./netbird-coredns
# Or use Justfile
just runnetbird-coredns/
├── cmd/netbird-coredns/ # Main application
├── internal/
│ ├── api/ # HTTP API server
│ ├── config/ # Configuration management
│ ├── plugin/ # CoreDNS plugin
│ ├── process/ # Process management
│ └── template/ # Corefile generation
├── pkg/
│ └── dns/ # DNS record types
├── docker/ # Docker deployment files
└── Justfile # Build and development commands
This service is containerized and works with Kubernetes. It includes:
- Health endpoint:
/healthfor liveness/readiness probes - Graceful shutdown: Handles SIGTERM properly
- Container best practices: Proper signal handling and resource management
A Helm chart is available for easy deployment to Kubernetes clusters. The chart includes:
- Automated secret management for NetBird setup keys
- Persistent volume for DNS records storage
- Configurable probes and resource limits
- Support for self-hosted NetBird Management servers
For detailed Helm installation and configuration instructions, see chart/README.md.
Quick start with Helm:
# Install from local chart
cd chart
helm install netbird-coredns . \
--set config.domains="mydomain.com" \
--namespace netbird-coredns \
--create-namespace- Authentication for API endpoints
- Metrics and Prometheus integration
- Comprehensive test suite
- Web UI for managing DNS records
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License. See the LICENSE file for details.
Created by Christian De Leon
- CoreDNS - DNS server in Go
- tailscale-coredns - Similar project for Tailscale
For issues and questions: