Proxmox VE Source¶
The Proxmox VE source creates DNS A records for running VMs and LXC containers on your Proxmox cluster. It polls the PVE API to discover workloads and resolves each resource's IP address — via the QEMU guest agent (VMs) or the LXC network configuration — then registers an A record mapping <vm-name>.<domain> to that IP.
How It Works¶
flowchart LR
A["PVE API<br/>/cluster/resources"] -->|"Poll interval"| B["List VMs / LXC"]
B -->|"Filter: node / tag / state"| C["Resolve IP"]
C -->|"QEMU guest agent (VM)<br/>or net0 config (LXC)"| D["Hostname + IP"]
D --> E["Proxmox Source"]
E -->|"A record"| F["Reconciler → DNS"]
- Lister polls
/cluster/resourcesand applies node, tag, and state filters - IP resolver calls the QEMU guest agent for VMs, or parses
net0config for LXC containers - Source maps the VM name + configured domain suffix to a fully-qualified hostname
- Reconciler creates or updates A records via the matching DNS provider
Configuration¶
Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
DNSWEAVER_PROXMOX_URL |
Yes | — | PVE API base URL, e.g. https://pve-00:8006 |
DNSWEAVER_PROXMOX_TOKEN_ID |
Yes | — | API token ID, e.g. root@pam!dnsweaver |
DNSWEAVER_PROXMOX_TOKEN_SECRET |
Yes | — | API token secret (UUID). Supports _FILE suffix. |
DNSWEAVER_PROXMOX_TOKEN_SECRET_FILE |
Alt | — | Path to a file containing the token secret (Docker secrets) |
DNSWEAVER_PROXMOX_VERIFY_TLS |
No | false |
Set true to verify the PVE API TLS certificate |
DNSWEAVER_PROXMOX_NODE_FILTER |
No | (all nodes) | Restrict discovery to a single PVE node name |
DNSWEAVER_PROXMOX_TAG_FILTER |
No | (all tags) | Only include resources with this tag (prefix match) |
DNSWEAVER_PROXMOX_STATE_FILTER |
No | running |
PVE resource status filter (running, stopped, etc.) |
DNSWEAVER_PROXMOX_DOMAIN_SUFFIX |
No | — | Domain suffix appended to VM names, e.g. home.example.com |
DNSWEAVER_PROXMOX_TARGET_MODE |
No | guest-ip |
Target resolution mode. guest-ip (default) emits an A record per VM IP. instance defers record type and target to the matching provider instance — useful for pointing all VMs at a reverse proxy via CNAME. |
Source Registration¶
Add proxmox to DNSWEAVER_SOURCES:
Auto-registration
When DNSWEAVER_PROXMOX_URL is set, the Proxmox source is automatically registered
even if not listed in DNSWEAVER_SOURCES. You only need to list it explicitly if
you want to control source ordering relative to other sources.
Hostname Resolution¶
The source determines the DNS hostname for each workload using this logic:
- VM name contains a dot — used directly as an FQDN (e.g.,
webserver.home.example.com) - Domain suffix configured — appended to the VM name (e.g.,
webserver+home.example.com→webserver.home.example.com) - Neither condition — the workload is skipped; a debug log entry is emitted
Domain suffix is strongly recommended
Without a domain suffix, only VMs whose names already contain a dot will
produce DNS records. Set DNSWEAVER_PROXMOX_DOMAIN_SUFFIX to ensure all
workloads are registered.
IP Address Resolution¶
IP resolution differs by resource type:
| Type | Method | Notes |
|---|---|---|
| VM (QEMU) | QEMU guest agent (/agent/network-get-interfaces) |
Requires qemu-guest-agent installed and running in the VM |
| LXC container | net0 config field (ip=<address>/prefix) |
Reads directly from the PVE API; no agent required |
VMs without a running guest agent are skipped (a debug log entry is emitted). To include VMs, install and enable qemu-guest-agent inside the guest.
Target Mode¶
DNSWEAVER_PROXMOX_TARGET_MODE controls what the source emits for each discovered workload:
| Mode | Record Type | Target | Use Case |
|---|---|---|---|
guest-ip (default) |
A |
VM's resolved IP | Direct DNS resolution to each VM/LXC |
instance |
from instance | from instance | Point all Proxmox-discovered hostnames at a reverse proxy |
In instance mode, the source emits the hostname only (no record-type or target hints).
The matching provider instance's RECORD_TYPE and TARGET drive the resulting record,
so a CNAME instance pointed at NPMplus / Traefik / Caddy will create CNAME records for
every Proxmox workload that matches its DOMAINS filter.
IP is still required
A VM with no resolved IP is skipped in both modes — the IP existence acts as a
liveness gate. Don't treat instance mode as a way to register records for
powered-off VMs.
Example: CNAME everything to a reverse proxy¶
DNSWEAVER_SOURCES=proxmox
DNSWEAVER_PROXMOX_URL=https://pve-00.home.example.com:8006
DNSWEAVER_PROXMOX_TOKEN_ID=dnsweaver@pve!dnsweaver
DNSWEAVER_PROXMOX_TOKEN_SECRET_FILE=/run/secrets/pve_token
DNSWEAVER_PROXMOX_DOMAIN_SUFFIX=home.example.com
DNSWEAVER_PROXMOX_TARGET_MODE=instance # opt in
DNSWEAVER_INSTANCES=npmplus
DNSWEAVER_NPMPLUS_TYPE=technitium
DNSWEAVER_NPMPLUS_RECORD_TYPE=CNAME
DNSWEAVER_NPMPLUS_TARGET=npmplus.home.example.com # all VMs point here
DNSWEAVER_NPMPLUS_DOMAINS=*.home.example.com
DNSWEAVER_NPMPLUS_URL=https://technitium.home.example.com
DNSWEAVER_NPMPLUS_TOKEN_FILE=/run/secrets/technitium_token
Every Proxmox VM/LXC matching *.home.example.com will get a CNAME pointing to
npmplus.home.example.com instead of an A record pointing at the guest's own IP.
PVE API Token¶
dnsweaver requires a token with read-only permissions covering both VM
listing and the QEMU guest agent. The built-in PVEAuditor role is not
sufficient on its own — it grants VM.Audit (lists VMs) but not
VM.Monitor (queries the guest agent for IP addresses). Without VM.Monitor,
VM IP resolution returns 403 Permission check failed and only LXC containers
get DNS records.
Create a dedicated minimal role and bind it to a token:
# On any PVE node (or via Datacenter → Permissions → Roles in the web UI)
pveum role add DNSWeaver -privs "VM.Audit,VM.Monitor,Pool.Audit"
pveum user add dnsweaver@pve --comment "dnsweaver read-only"
pveum aclmod / -user dnsweaver@pve -role DNSWeaver
pveum user token add dnsweaver@pve dnsweaver --privsep=0
| Privilege | Why it is required |
|---|---|
VM.Audit |
List VMs and LXC containers via /cluster/resources |
VM.Monitor |
Query the QEMU guest agent (/agent/network-get-interfaces) for VM IPs |
Pool.Audit |
Required for /cluster/resources to enumerate pool-scoped resources |
The token ID format is <user>@<realm>!<tokenname>, for example:
dnsweaver@pve!dnsweaver
Privilege separation
--privsep=0 propagates the user's role to the token directly. If you set
--privsep=1, you must also explicitly grant the role to the token itself
via pveum aclmod / -token 'dnsweaver@pve!dnsweaver' -role DNSWeaver —
otherwise the token will have no permissions.
Verify the token
Confirm the token has the expected privileges:
Workload Labels¶
The Proxmox source exposes PVE tags as workload labels with the prefix proxmox.tag/.
For example, a VM tagged web will have the label proxmox.tag/web=true.
These labels are available for filtering and can be used to route records to specific DNS providers via provider label selectors (if supported by your provider configuration).
Example: Docker Compose¶
services:
dnsweaver:
image: ghcr.io/maxfield-allison/dnsweaver:latest
environment:
DNSWEAVER_SOURCES: proxmox
DNSWEAVER_PROXMOX_URL: https://pve-00.home.example.com:8006
DNSWEAVER_PROXMOX_TOKEN_ID: dnsweaver@pve!dnsweaver
DNSWEAVER_PROXMOX_TOKEN_SECRET_FILE: /run/secrets/pve_token
DNSWEAVER_PROXMOX_VERIFY_TLS: "true"
DNSWEAVER_PROXMOX_DOMAIN_SUFFIX: home.example.com
DNSWEAVER_PROXMOX_TAG_FILTER: dnsweaver
secrets:
- pve_token
secrets:
pve_token:
file: ./secrets/pve_token.txt
Example: Kubernetes Secret¶
apiVersion: v1
kind: Secret
metadata:
name: dnsweaver-proxmox
namespace: dnsweaver
type: Opaque
stringData:
token-secret: "<your-token-secret>"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dnsweaver
namespace: dnsweaver
spec:
template:
spec:
containers:
- name: dnsweaver
env:
- name: DNSWEAVER_SOURCES
value: proxmox
- name: DNSWEAVER_PROXMOX_URL
value: https://pve-00.home.example.com:8006
- name: DNSWEAVER_PROXMOX_TOKEN_ID
value: dnsweaver@pve!dnsweaver
- name: DNSWEAVER_PROXMOX_TOKEN_SECRET
valueFrom:
secretKeyRef:
name: dnsweaver-proxmox
key: token-secret
- name: DNSWEAVER_PROXMOX_VERIFY_TLS
value: "true"
- name: DNSWEAVER_PROXMOX_DOMAIN_SUFFIX
value: home.example.com
Filtering Workloads¶
By Node¶
Only include resources from a specific PVE node:
By Tag¶
Only include resources that have a specific tag (prefix match):
# Include only resources tagged with "dnsweaver" (or any tag starting with "dnsweaver")
DNSWEAVER_PROXMOX_TAG_FILTER=dnsweaver
Tag resources in PVE under Options → Tags in the web UI, or via the API:
By State¶
Only include resources in a specific state (default: running):
Troubleshooting¶
VM has no resolved IP¶
- Ensure
qemu-guest-agentis installed and running inside the VM - Check with:
pvesh get /nodes/<node>/qemu/<vmid>/agent/network-get-interfaces - If the agent is not available, the VM is silently skipped (check debug logs)
TLS certificate errors¶
If the PVE API uses a self-signed certificate (common in homelabs):
To enforce certificate verification with a valid cert:
No records created¶
- Verify
DNSWEAVER_PROXMOX_URLis reachable from dnsweaver - Confirm the token has
VM.Audit,VM.Monitor, andPool.Auditvia:pveum user token permissions dnsweaver@pve dnsweaver --path / - Check that VMs are in the
runningstate (or adjustDNSWEAVER_PROXMOX_STATE_FILTER) - Confirm
DNSWEAVER_PROXMOX_DOMAIN_SUFFIXis set if VM names are not already FQDNs - Enable debug logging:
DNSWEAVER_LOG_LEVEL=debug
Only LXC records appear, no VMs¶
This is the classic symptom of a missing VM.Monitor privilege. LXC IPs are
read directly from the PVE config (covered by VM.Audit), but VM IPs require
the guest agent endpoint which is gated by VM.Monitor. Add it to the role: