Skip to content

settings.dns

Local DNS records, published to Pi-hole v6.

There is no record list. Every host, VM and LXC in the config becomes <name>.<suffix> -> ip, so a device that exists only to have a DNS entry is written as an ordinary node with os: unmanaged. DNS therefore cannot drift from the inventory — they are the same declaration.

See the Pi-hole DNS guide for how the commands fit together.

Dns

Local DNS records, and the server they are published to.

Records are derived from the config tree — every host/VM/LXC becomes <name>.<suffix> -> ip — so there is no record list here. A device that needs a record is a node like any other (os: unmanaged and an ip is the whole of it); dns_name renames it and dns: false excludes it. See src/dns/find.py.

Only suffix is required. Without a server block records are still derived, so dns list works; diff, sync and upgrade fail with a message naming what is missing.

One server at a time: a second block here would be two sources of truth for the same records, and nothing decides which one wins.

Key Type Default Description
suffix string required Appended to each node's name to form its hostname. A leading dot is optional — .lab and lab both yield cprox.lab.
pihole Pihole unset Where Pi-hole is and how to reach it. Optional — omit it and records are still derived, so dns list works, while diff, sync and upgrade fail naming this block.

Pihole

The Pi-hole instance labops publishes records to, and how to reach it.

Where Pi-hole is stays a single answer rather than an address plus a machine: sync needs an address and upgrade needs the thing behind it, and two independent fields could disagree about which Pi-hole is meant.

What is split out is whether Pi-hole is installed on a machine or running in a container on one, because that is the one thing the address cannot tell you. Both resolve to an address the same way — a container's records go to its host — but only an installation can be upgraded by running a command, so the two cases are separate keys rather than one string labops has to interpret.

Only one instance is supported. The secret store holds a single PIHOLE_PASSWORD, so a list would quietly assume they all share it. With a replicating setup (nebula-sync), point labops at the primary and let it propagate to the rest.

Key Type Default Description
target string unset The machine Pi-hole is installed on — a host, VM or LXC in this config, by name or IP. It must be a node here: declare a Pi-hole labops does not otherwise manage with os: unmanaged, which publishes records but refuses dns upgrade. An LXC needs no sshd; it is reached with pct through its Proxmox parent. Use docker_stack instead for a containerised Pi-hole.
docker_stack string unset The docker stack running Pi-hole, when it is containerised. Records go to the address of the node hosting the stack, so you do not repeat it here. dns upgrade refuses, because a container is upgraded by pulling a new image — labops docker stack --stack <name> update.
port integer 443 The port Pi-hole's admin interface and API listen on. Follows the scheme unless you set it — 443 for https, 80 for http — so neither has to be repeated after changing the other.
scheme http | https "https" How to reach the API. Defaults to https because the API password is sent in the request body, and http puts it on the network in clear text. Pi-hole v6 serves both out of the box. Certificate verification is skipped either way — Pi-hole's own certificate is self-signed — so https protects against eavesdropping rather than against a machine-in-the-middle.
password string unset The API password, inline. Prefer leaving this unset and putting PIHOLE_PASSWORD in the secret store (see settings.env_file) — either the web-interface password or an app password from Settings → Web interface / API. Setting it here puts the secret in clear text in your config, and dns sync warns.