Files
homelab/dns-server

Proxmox DNS Server

A lightweight DNS server that dynamically resolves VM and LXC container hostnames by querying the Proxmox API.

Features

  • Dynamic Resolution: Queries Proxmox API in real-time to resolve hostnames
  • Automatic Discovery: Discovers both VMs (with QEMU guest agent) and LXC containers
  • Caching: Built-in caching to reduce API load
  • DHCP Compatible: Works with DHCP-assigned IPs by reading from Proxmox state
  • Configurable: YAML-based configuration

How It Works

  1. DNS query comes in for a hostname (e.g., gitea.srv.renner.uno)
  2. Server strips the configured suffix and looks up gitea
  3. Queries Proxmox API across all nodes for a VM/LXC named gitea
  4. Returns the IP address from the VM's guest agent or LXC's network config
  5. Caches the result for configured TTL

Requirements

  • Python 3.8+
  • Access to Proxmox API (token or password)
  • QEMU guest agent installed on VMs (for IP detection)

Installation

Manual Installation

# Install dependencies
pip3 install -r requirements.txt

# Create config directory
sudo mkdir -p /etc/proxmox-dns

# Copy and edit configuration
sudo cp config.yaml.example /etc/proxmox-dns/config.yaml
sudo nano /etc/proxmox-dns/config.yaml

# Run the server (requires root for port 53)
sudo python3 proxmox_dns.py -c /etc/proxmox-dns/config.yaml

Systemd Service

# Copy service file
sudo cp proxmox-dns.service /etc/systemd/system/

# Reload systemd
sudo systemctl daemon-reload

# Enable and start
sudo systemctl enable proxmox-dns
sudo systemctl start proxmox-dns

# Check status
sudo systemctl status proxmox-dns

Configuration

Edit /etc/proxmox-dns/config.yaml:

proxmox:
  host: "pve.renner.uno"
  user: "root@pam"
  token_name: "dns"
  token_value: "your-token-here"
  verify_ssl: false

dns_suffix: ".renner.uno"
dns_ttl: 300
cache_ttl: 60

Creating a Proxmox API Token

  1. Log into Proxmox web interface
  2. Go to Datacenter > Permissions > API Tokens
  3. Click "Add" to create a new token
  4. User: root@pam, Token ID: dns
  5. Uncheck "Privilege Separation" for full access
  6. Copy the token secret to your config file

Testing

# Test DNS resolution
dig @localhost gitea.srv.renner.uno

# Test with verbose logging
sudo python3 proxmox_dns.py -c /etc/proxmox-dns/config.yaml -v

Integration with UniFi

To use this as your primary DNS server:

  1. Deploy the DNS server on a static IP (e.g., 10.2.40.10)
  2. In UniFi Controller:
    • Go to Settings > Networks > LAN
    • Set DHCP Name Server to the DNS server IP
    • Keep UniFi DNS for CNAME records (pointing to ingress, etc.)

Troubleshooting

VMs not resolving

  • Ensure QEMU guest agent is installed and running in the VM
  • Check that the VM is in "running" state
  • Verify the guest agent can communicate with Proxmox

LXCs not resolving

  • For DHCP LXCs: The container must be running long enough for DHCP to assign an IP
  • For static IPs: Check the container's network configuration in Proxmox

Permission denied on port 53

  • Run as root: sudo python3 proxmox_dns.py
  • OR use a non-privileged port and forward: sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353
  • OR grant capability: sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python3

Architecture

The DNS server uses a background thread that periodically queries the Proxmox API and caches results. When a DNS query arrives:

  1. Check cache first (fast)
  2. If cache miss or expired, trigger immediate API query
  3. Update cache with fresh data
  4. Return IP address or NXDOMAIN

This hybrid approach ensures low latency while keeping data fresh.