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
- DNS query comes in for a hostname (e.g.,
gitea.srv.renner.uno) - Server strips the configured suffix and looks up
gitea - Queries Proxmox API across all nodes for a VM/LXC named
gitea - Returns the IP address from the VM's guest agent or LXC's network config
- 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
- Log into Proxmox web interface
- Go to Datacenter > Permissions > API Tokens
- Click "Add" to create a new token
- User:
root@pam, Token ID:dns - Uncheck "Privilege Separation" for full access
- 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:
- Deploy the DNS server on a static IP (e.g.,
10.2.40.10) - 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:
- Check cache first (fast)
- If cache miss or expired, trigger immediate API query
- Update cache with fresh data
- Return IP address or NXDOMAIN
This hybrid approach ensures low latency while keeping data fresh.