
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
You need to set up a BIND DNS server on Linux when public resolvers cannot answer queries for your private infrastructure or when you require full control over authoritative records. While cloud-managed DNS is convenient, self-hosted BIND remains the standard for air-gapped networks, internal service discovery, and compliance-bound environments where data residency matters. This guide walks through installing, configuring, and hardening BIND 9 on Ubuntu 24.04 LTS, drawing from patterns I use when securing fresh VPS deployments for regulated workloads.
bind9 package, define trusted networks in named.conf.options, create forward and reverse zone files in /etc/bind/zones/, validate syntax with named-checkconf and named-checkzone, then enable and start the service. Always restrict recursion and apply least-privilege file permissions before going live.How do you install and prepare BIND 9 on Ubuntu?
Before you configure zones, you must install BIND and establish a secure baseline. On Ubuntu 24.04 LTS, the package is mature and receives security backports. Avoid compiling from source unless you have specific patch requirements; distribution packages integrate with AppArmor and systemd out of the box.
- Update package indices and install BIND 9 with utilities:
sudo apt update && sudo apt upgrade -y sudo apt install bind9 bind9utils bind9-doc dnsutils -y - Create a dedicated directory for zone files with restrictive ownership:
sudo mkdir -p /etc/bind/zones sudo chown bind:bind /etc/bind/zones sudo chmod 750 /etc/bind/zones - Verify the installed version supports current standards:
named -v # Expected output: BIND 9.18.x or newer
A common mistake is leaving default world-readable permissions on zone directories. In SOC 2 audits, this surfaces as a finding because zone files can leak internal hostnames. Always enforce bind:bind ownership and 750 permissions before adding any records.
How do you configure global options securely in named.conf?
The named.conf.options file controls recursion, access lists, and listening interfaces. For an authoritative-only server serving internal zones, disable recursion entirely to prevent amplification attacks. If you also need recursive resolution for trusted clients, define explicit ACLs.
// /etc/bind/named.conf.options
acl "trusted" {
10.10.0.0/24;
192.168.100.0/24;
localhost;
};
options {
directory "/var/cache/bind";
// Authoritative-only: disable recursion
recursion no;
allow-query { any; };
allow-transfer { none; };
// Listen only on required interfaces
listen-on { 10.10.0.5; 127.0.0.1; };
listen-on-v6 { none; };
// Security hardening
version none;
hostname none;
server-id none;
// DNSSEC validation (if acting as resolver)
dnssec-validation auto;
}; Key directives explained:
- recursion no; — Prevents the server from resolving external queries, eliminating open-resolver abuse vectors.
- allow-transfer { none; }; — Blocks unauthorized zone transfers. Add secondary IPs explicitly if you run slave servers.
- version none; — Hides BIND version in responses, reducing reconnaissance surface.
- listen-on — Binds only to specific IPs instead of all interfaces, critical for multi-homed hosts.
If managing infrastructure as code, consider defining these options via templates in Terraform or Ansible rather than editing files manually. Drift detection catches accidental changes that reintroduce recursion or expose ports.
How do you create authoritative forward and reverse zone files?
Zone files map hostnames to IPs (forward) and IPs to hostnames (reverse). Both are essential for internal tooling, monitoring systems, and PTR-based authentication checks. Store them in /etc/bind/zones/ with serial numbers following YYYYMMDDNN format for predictable increments.
Forward Zone Example
; /etc/bind/zones/db.internal.example.com
$TTL 3600
@ IN SOA ns1.internal.example.com. admin.internal.example.com. (
2026080901 ; Serial
3600 ; Refresh
900 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
IN NS ns1.internal.example.com.
ns1 IN A 10.10.0.5
app IN A 10.10.0.10
db IN A 10.10.0.20 Reverse Zone Example
; /etc/bind/zones/db.10.10.0
$TTL 3600
@ IN SOA ns1.internal.example.com. admin.internal.example.com. (
2026080901 ; Serial
3600 ; Refresh
900 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
IN NS ns1.internal.example.com.
5 IN PTR ns1.internal.example.com.
10 IN PTR app.internal.example.com.
20 IN PTR db.internal.example.com. Register both zones in named.conf.local:
zone "internal.example.com" {
type master;
file "/etc/bind/zones/db.internal.example.com";
allow-update { none; };
};
zone "0.10.10.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.10.10.0";
allow-update { none; };
}; Always set allow-update { none; } unless you have dynamic DNS clients with TSIG keys. Unrestricted updates let attackers inject malicious records. For environments needing automation, pair DDNS with HashiCorp Vault for key rotation.
How do you validate and troubleshoot BIND configuration errors?
Never restart BIND without validation. Syntax errors cause immediate service failure, breaking name resolution across dependent systems. Use built-in tools to catch issues pre-deployment.
- Validate main configuration syntax:
No output means success. Errors include line numbers and descriptions.sudo named-checkconf /etc/bind/named.conf - Validate each zone file individually:
Expected:sudo named-checkzone internal.example.com /etc/bind/zones/db.internal.example.com sudo named-checkzone 0.10.10.in-addr.arpa /etc/bind/zones/db.10.10.0OK. Fix any warnings about missing glue records or invalid TTLs. - Test live queries after reload:
sudo systemctl reload bind9 dig @10.10.0.5 app.internal.example.com +short dig @10.10.0.5 -x 10.10.0.10 +short
Common pitfalls include forgetting trailing dots on FQDNs in zone files (ns1.internal.example.com. not ns1.internal.example.com) and mismatched serial numbers preventing secondaries from syncing. Increment serials atomically during maintenance windows, not ad-hoc.
| Issue | Symptom | Resolution |
|---|---|---|
| Open resolver abuse | High outbound UDP traffic, ISP complaints | Set recursion no; or restrict via ACLs |
| Zone transfer leaks | Unauthorized hosts receiving full zone data | Add explicit allow-transfer IPs only |
| Missing reverse DNS | Email rejected, SSH delays, log noise | Create matching PTR records in reverse zone |
| Stale cache entries | Clients see old IPs after migration | Lower TTL 24h before change, increment serial |
Production Readiness Checklist
Setting up BIND is straightforward; keeping it reliable under audit requires discipline. Before declaring production readiness, verify these items:
- Firewall rules permit UDP/TCP 53 only from trusted sources (configure UFW accordingly).
- AppArmor profile is enforced, not disabled:
sudo aa-status | grep named. - Log rotation configured for
/var/log/syslogand query logs to prevent disk exhaustion. - Monitoring alerts on process death, high query latency, and unexpected TCP connections.
- Backup strategy includes zone files and configuration, tested quarterly.
- TLS encryption for zone transfers if traversing untrusted networks (TSIG minimum).
In Nepal’s growing tech sector, teams often skip hardening due to time pressure, but regulators increasingly scrutinize DNS infrastructure during compliance reviews. The extra hour spent securing BIND prevents days of incident response later.
Next Steps After Deployment
A properly configured BIND DNS server forms the foundation for internal service mesh, certificate authority validation, and zero-trust network policies. Document your zone structure, automate deployments with CI/CD pipelines, and integrate health checks into your Prometheus monitoring stack. If you’re designing DNS for a regulated environment or need help auditing an existing deployment, reach out to discuss your architecture.