Keeps a DNS record pointed at a cloud instance whose public IP changes on every restart. Supports Cloudflare and Google Cloud DNS, A and (optionally) AAAA records.
On each run it:
- Detects the public IP: the cloud metadata server first (GCE, AWS, Azure, DigitalOcean, Hetzner), then public echo services (ipify, icanhazip, Cloudflare). Private or wrong-family answers are rejected.
- Reads the current record from the provider and writes only if the IP or TTL differs.
On the VM, from a checkout of this repo:
sudo ./install.sh # builds, asks for hostname/provider/credentials, enables at bootOr non-interactively (e.g. from cloud-init / a startup script):
sudo ./install.sh --hostname vm1.example.com --provider cloudflare --cf-token-file /root/cf-token
sudo ./install.sh --hostname vm1.example.com --provider gcloud # uses the VM's service account
sudo ./install.sh --binary ./dnssense ... # skip the build, use a prebuilt binary
sudo ./install.sh --mode watch ... # keep running instead of once per bootThe script:
- Builds with cargo, running as the user who invoked sudo, or uses
--binary. - Installs
/usr/local/bin/dnssense. - Writes
/etc/dnssense/config.toml, with secrets in mode 600 files. An existing config is kept. - Enables
dnssense.service(boot) ordnssense-watch.service(watch). - Does a dry run to check the config and credentials, then starts the service.
Re-running it upgrades the binary and switches mode safely. sudo ./install.sh --uninstall [--purge] removes it.
See ./install.sh --help for all options.
Useful afterwards:
dnssense detect # what IP would be used, and where it came from
sudo dnssense --dry-run # what would change in DNS
journalctl -u dnssense # logs| Command | |
|---|---|
dnssense [sync] [--retries N] |
Update once and exit (default). Retries with backoff (default 5 attempts) while the network comes up. |
dnssense watch [--interval S] |
Loop forever. Only calls the provider when the IP changes, plus an hourly re-check that repairs hand-edited records. |
dnssense detect [--source auto|metadata|public] |
Print detected IPv4/IPv6 and exit. Doesn't need a config. |
Global flags: -c/--config PATH (or DNSSENSE_CONFIG), -n/--dry-run, -v / -vv.
Create an API token with Zone → DNS → Edit on the zone. Also grant Zone → Zone → Read
unless you set zone_id. Supply it as api_token, api_token_file, or CLOUDFLARE_API_TOKEN.
Credentials are tried in this order:
credentials_filein the configGOOGLE_APPLICATION_CREDENTIALS- The VM's attached service account, via the metadata server. The VM needs the
cloud-platformorndev.clouddns.readwriteaccess scope.
Supported files are service account keys and gcloud auth application-default login
credentials. The identity needs roles/dns.admin on the project, or a custom role with
dns.changes.create, dns.resourceRecordSets.* and dns.managedZones.list.
project defaults to the key file's project or the VM's project. managed_zone defaults to the
public zone whose DNS name contains hostname.
- If several records of the same type exist for the hostname, only one is managed (a warning is logged).
- Cloudflare proxied records always get TTL "auto".
- Building needs a C compiler and CMake (used by
aws-lc-rs, the TLS/crypto backend).
MIT. See LICENSE.