A command-line tool to interact with the Hetzner Cloud DNS API, allowing you to manage DNS records by editing a set of YAML files.
It is designed for small to medium installations with up to 100 zones. Function scope is limited to managing records within existing zones for now.
- Import zones and records from the Hetzner Cloud DNS API
- Check DNS records against actual DNS entries
- Create missing and update mismatched DNS records
- Delete DNS records
- Stores zone data in YAML files for easy manual editing
After initial import, add records to an RRSet in a zone YAML file:
Then run update to push the change to the API:
The same way you can update records and push those.
The script uses PEP 723 for dependency management, so it's self-contained. You'll need Python 3.10 or higher and a working installation of uv, installed via your OS's package manager or via pip.
# Clone the repository
git clone https://github.com/serpent213/hetzner-dns-manager.git
cd hetzner-dns-manageror
# Download raw script directly
curl -LO https://github.com/serpent213/hetzner-dns-manager/raw/refs/heads/master/hdem
chmod +x hdemAlternatively install from PyPI:
# Install using pip
pip install hetzner-dns-manager
# Or with pipx for isolated installation
pipx install hetzner-dns-managerAfter installation, you'll have access to the hdem command in your terminal.
Set your Hetzner Cloud API token as an environment variable:
export HCLOUD_TOKEN="your_api_token_here"You may want to add this to your shell profile file (.bashrc, .zshrc, etc.) for persistence.
By default, hdem reads and writes zone files in ./zones. Use --zones-dir or HDEM_ZONES_DIR
to select a different inventory:
hdem --zones-dir ./live-zones check --all
export HDEM_ZONES_DIR="$HOME/infrastructure/dns"The database consists of one YAML file per zone in the configured zones directory.
Import a specific zone:
hdem import example.comImport all zones:
hdem import --allThis will create YAML files in the configured zones directory.
Segmented TXT records (like "abc" "def") will be concatenated (to "abcdef") by default. This might be undesirable and can be disabled by passing --no-txt-concat.
Check a specific zone against actual DNS entries using one of the domain's authoritative servers:
hdem check --verbose example.comCheck all zones:
hdem check --allSOA records will be ignored as they are updated automatically by Hetzner.
Check and update mismatched records for a specific zone:
hdem update example.comCheck and update mismatched records for all zones:
hdem update --allTo create new records, add them to the relevant RRSet in your zone YAML. To create a new name/type pair, add a new RRSet.
Delete a specific record by name:
hdem delete example.com wwwIf there is more than one candidate, hdem will ask you which records to delete.
Rewrite a legacy flat records: file into the native RRSet format:
hdem migrate example.comRewrite all local zone files:
hdem migrate --allThe YAML files in the configured zones directory follow this RRSet-based structure:
version: 2
id: 123456
name: example.com
ttl: 86400
rrsets:
- name: www
type: A
records:
- value: 192.0.2.1
- name: '@'
type: MX
records:
- value: '10 mail.example.com.'Older flat records: files are still accepted when reading. Normal writes preserve the file format that was read. Use hdem migrate example.com or hdem migrate --all to rewrite local files in the RRSet format.
Some other tools dealing with Hetzner DNS (that are not dynamic DNS updaters):

