Skip to main content

cloudflare-dns-updater

CI Release PyPI Python ≥ 3.12 License: MIT

Update Cloudflare DNS A and AAAA records when your external IP address changes.

Install from PyPI: pipx install cloudflare-dns-updater.

pipx installs the CLI in an isolated environment and puts cloudflare-dns-updater on your PATH (usually ~/.local/bin).

# install pipx once (Debian/Ubuntu example)
sudo apt install pipx
pipx ensurepath
# open a new shell, or: source ~/.bashrc

pipx install cloudflare-dns-updater
cloudflare-dns-updater --help

Upgrade or reinstall later:

pipx upgrade cloudflare-dns-updater
# or pin a version:
pipx install cloudflare-dns-updater==0.1.0 --force

Other installers:

uv tool install cloudflare-dns-updater
pip install --user cloudflare-dns-updater   # not isolated; prefer pipx

Quick start

mkdir -p ~/.config/cloudflare-dns-updater
curl -fsSL https://raw.githubusercontent.com/the-hcma/cloudflare-dns-updater/main/config.example.json \
  -o ~/.config/cloudflare-dns-updater/config.json
# edit config.json — set cloudflare_api_token, zone, and dns_entries
cloudflare-dns-updater -v -d    # dry-run: discover IPs, no Cloudflare writes
cloudflare-dns-updater          # update DNS when IPv4 or IPv6 changed

Create a Cloudflare API token at https://dash.cloudflare.com/profile/api-tokens with permission to edit DNS records for your zone.

Before scheduling: run a dry-run and confirm exit code 0 or 1. If config is missing, the tool may write a starter file but still exits with code 2 until you replace the placeholder token and zone settings.

Run on a schedule (cron)

Cron runs with a minimal environment: set HOME (and usually PATH) so the tool finds ~/.config/cloudflare-dns-updater/config.json and your pipx-installed binary. Create and edit config before enabling the job — do not rely on the auto-created starter file in production.

Exit codes

Code Meaning
0 No update needed (addresses unchanged).
1 Success — DNS records were updated (or dry-run completed).
2 Hard failure — missing/invalid config, discovery error, Cloudflare API error, etc.

Exit 1 is a successful run that changed DNS; exit 2 is what you should alert on.

Logging and alerts

Every run appends output to a log file (default ~/.local/state/cloudflare-dns-updater/updater.log, or $XDG_STATE_HOME/cloudflare-dns-updater/updater.log; override with --log). On a TTY, output is also shown live. Under cron (non-TTY), stderr is written only on non-success exits (anything other than 0 or 1), including the failure text so cron mail is actionable without redirect gymnastics.

Recommended crontab (no shell wrapper, no output redirect):

PATH=/usr/bin:/bin
HOME=/home/you
*/5 * * * * /home/you/.local/bin/cloudflare-dns-updater

Successful DNS updates (exit 1) stay quiet on stderr so cron does not mail on every address change. Failures mail the captured error body (and still land in the log).

Alternative — mail on any non-zero exit (includes exit 1 when records were updated):

HOME=/home/you
*/5 * * * * /usr/bin/chronic /home/you/.local/bin/cloudflare-dns-updater

Do not wrap chronic with >>log 2>&1 — that captures chronic's failure output into the log and cron will not mail you.

Use -f if you want to recheck Cloudflare even when local state files show no change. For non-standard home paths or multiple configs, pass -c /full/path/to/config.json explicitly.

Configuration

Settings live in config.json. Search order:

  1. -c / --config path
  2. CLOUDFLARE_DNS_UPDATER_CONFIG environment variable
  3. ./config.json in the current working directory
  4. ~/.config/cloudflare-dns-updater/config.json

Copy from config.example.json:

{
  "cloudflare_api_token": "your-cloudflare-api-token",
  "zone": "example.com",
  "dns_entries": ["example.com", "home.example.com"],
  "record_ttl": 120,
  "ipv6_enabled": true
}
Field Required Description
cloudflare_api_token Yes Cloudflare API token.
zone Yes Cloudflare zone name.
dns_entries Yes Hostnames to update (A and optional AAAA).
record_ttl No TTL in seconds (default 120).
ipv6_enabled No Set false to skip AAAA updates (default true).
nest_router_url No Nest / Google Wifi base URL. Omitted = http://<LAN>.1 from your default route. Set null to skip Nest.

CLOUDFLARE_API_TOKEN in the environment overrides only the token field in the file. In cron, set HOME (and optionally CLOUDFLARE_API_TOKEN) in the crontab — cron does not load your shell profile.

IP discovery

IPv4

  1. Google Nest / Wifi — GET {nest_router_url}/api/v1/status → wan.localIpAddress
  2. Fallback — https://checkip.amazonaws.com

IPv6

When ipv6_enabled is true, the tool reads globally addressable IPv6 addresses from local network interfaces via netifaces.

Usage

cloudflare-dns-updater          # update when IPv4 or IPv6 changed
cloudflare-dns-updater -f       # recheck Cloudflare even if local state is unchanged
cloudflare-dns-updater -d       # dry-run (no Cloudflare calls, no state file writes)
cloudflare-dns-updater -v       # verbose logging and discovery details
cloudflare-dns-updater -c /path/to/config.json

Run cloudflare-dns-updater -h for full option descriptions.

Exit codes: 0 no update needed, 1 records updated (success), 2 configuration or execution failure. See Run on a schedule (cron) for alerting patterns.

State is stored under ~/.local/state/cloudflare-dns-updater/ (override with XDG_STATE_HOME).

Run from a git checkout

./bin/cloudflare-dns-updater -v -d

The wrapper runs uv sync --group dev when .venv is missing or uv.lock has changed, then invokes the CLI. You can still use uv run cloudflare-dns-updater directly after a manual sync.

Development

uv sync --group dev
uv run ruff check .
uv run ruff format .
uv run mypy src/dns_updater
uv run pytest
uv run pytest -m integration   # live network checks

Releases

Versioning and PyPI publish are documented in RELEASING.md. End users install with pipx install cloudflare-dns-updater.

Repository setup

This repo follows the-hcma conventions. Validate with:

scripts/check-repo-practices --repo the-hcma/cloudflare-dns-updater --suggest

See GRAPHITE.md for stacked PRs and the merge-it merge queue.

Metadata

Release files for cloudflare-dns-updater 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cloudflare-dns-updater 0.6.1
File Size Uploaded Provenance Actions
cloudflare_dns_updater-0.6.1.tar.gz 87.3 kB With provenance Details for cloudflare_dns_updater-0.6.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cloudflare-dns-updater 0.6.1
File Interpreter ABI Platform Actions
cloudflare_dns_updater-0.6.1-py3-none-any.whl Python 3 none any Details for cloudflare_dns_updater-0.6.1-py3-none-any.whl

Total release size: 110.5 kB

Release files / cloudflare_dns_updater-0.6.1.tar.gz

Download URL cloudflare_dns_updater-0.6.1.tar.gz
Size 87.3 kB
Tags Source
SHA-256 checksum
How to use checksums
e3fa6d8ac54b84dec172edfcd42a664ce3c7c0ab98cb0485e4eb34605138f06f
BLAKE2b-256 checksum
How to use checksums
207a29e55e98791a94797000de066fb99c907b2cd05fe6a8d8dc679dca31568d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

Release files / cloudflare_dns_updater-0.6.1-py3-none-any.whl

Download URL cloudflare_dns_updater-0.6.1-py3-none-any.whl
Size 23.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
667a56b60c9c88fd50b0235db9aeb1ca17a2f7ecee722c1c860062947577ecb0
BLAKE2b-256 checksum
How to use checksums
c437be5c7d8ffe661bb7582ddff9a17b8adb63aec87261a45425bf34537842b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

Security for cloudflare-dns-updater 0.6.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cloudflare_dns_updater-0.6.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page