Set Up a BIND DNS Server on Linux

Khimananda Oli 6 min read Database
Set Up a BIND DNS Server on Linux

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.

Internal ClientsBIND DNS Server(Authoritative)External InternetZone QueriesBlocked / Filtered
Authoritative BIND DNS server architecture isolating internal zone queries from external recursion

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.

  1. 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
  2. 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
  3. 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.

Edit Confignamed-checkconfSyntax OK?named-checkzoneZone Valid?systemctl reloadFix Errors
Validation workflow ensuring BIND config and zone integrity before applying changes

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.

  1. Validate main configuration syntax:
    sudo named-checkconf /etc/bind/named.conf
    No output means success. Errors include line numbers and descriptions.
  2. Validate each zone file individually:
    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.0
    Expected: OK. Fix any warnings about missing glue records or invalid TTLs.
  3. 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.

IssueSymptomResolution
Open resolver abuseHigh outbound UDP traffic, ISP complaintsSet recursion no; or restrict via ACLs
Zone transfer leaksUnauthorized hosts receiving full zone dataAdd explicit allow-transfer IPs only
Missing reverse DNSEmail rejected, SSH delays, log noiseCreate matching PTR records in reverse zone
Stale cache entriesClients see old IPs after migrationLower TTL 24h before change, increment serial
Default / Insecure• Recursion enabled globally• Listens on all interfaces• Version string exposed• World-readable zone files• No allow-transfer restrictionsHardened Production• Recursion disabled / ACL-limited• Bound to specific IPs only• Version/hostname hidden• bind:bind 750 permissions• Explicit transfer allowlistHarden
Security posture comparison between default and hardened BIND DNS server configurations

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/syslog and 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.

Frequently Asked Questions

BIND 9.20 runs comfortably on 512MB RAM for small zones, but allocate at least 2GB for production servers handling recursive queries or large zone files to prevent swapping.

Run sudo apt update followed by sudo apt install bind9 bind9utils bind9-doc. This installs the latest stable BIND 9.x package from official repositories along with essential utilities and documentation.

The /etc/bind/named.conf.local file defines local zones and ACLs, keeping custom configurations separate from the default named.conf to simplify upgrades and maintenance tasks.

Execute named-checkconf to verify named.conf syntax and named-checkzone for individual zone files. Both tools return specific error line numbers without affecting the running service.

Port 53 UDP/TCP.

Set recursion yes and allow-query { any; } in options block within named.conf.options. Remove all zone statements to disable authoritative serving while enabling recursive resolution for internal clients.

Check allow-query directives in named.conf and ensure firewall rules permit port 53 traffic. Default configurations often restrict queries to localhost only for security reasons.

Add dnssec-validation auto; to the options block. BIND automatically fetches root trust anchors and validates signed responses without manual key management in current stable releases.

Usually upstream resolver timeouts, DNSSEC validation failures, or malformed zone data. Check journalctl -u bind9 for specific error messages indicating which validation step failed.

Yes, BIND listens on both address families by default when configured with listen-on-v6 { any; }; alongside standard IPv4 settings in named.conf.options.

Use allow-transfer { ip_address; }; within each zone definition. Never leave this directive unset in production as it exposes complete zone data to unauthorized parties.

Configure a dedicated query logging channel in named.conf.logging with severity info and print-time yes. Disable when not debugging due to significant disk I/O overhead.

Weekly.

Yes, using TSIG keys defined via tsig-keygen and referenced in update-policy statements. This authenticates DHCP servers or CI/CD pipelines without exposing plaintext credentials over the network.

Use dig @localhost example.com +short to query your local server directly. Compare results against public resolvers to verify cache accuracy and zone propagation status.