Dynamic DNS Automation Auditable Open Source • Cloudflare API

Cirrusync: Cloudflare Dynamic DNS Client

A lightweight, auditable Dynamic DNS client for Cloudflare. Automatically discovers public IPv4 and IPv6 addresses, compares them against target DNS zone records, and modifies only out-of-date entries under systemd or automated cron schedules.

Provider: Cloudflare v4 API
Record Types: A (IPv4) • AAAA (IPv6)
Target Platform: Linux / systemd (Debian 12, Ubuntu 22.04+) • Rust Daemon

Designed for Transparency & Least Privilege

Unlike opaque monolithic DDNS daemons, Cirrusync is built with clean, auditable logic focusing exclusively on Cloudflare DNS state synchronization:

SYNCHRONIZATION CYCLE

Dynamic DNS State Reconciliation Cycle

Rust • Cloudflare API v4 • Least-Privilege Scoped Tokens
Cirrusync Dynamic DNS State Reconciliation Cycle Diagram showing the four-stage reconciliation cycle of Cirrusync: discovering public IP addresses, querying Cloudflare DNS records via API v4, evaluating managed record differences, and executing conditional record updates. 1. DUAL-STACK PROBES IP Discovery External WAN endpoints Redundant Probes Cloudflare trace, ipify, icanhazip Public IPv4 WAN Detects NAT gateway address Global IPv6 GUA Global unicast interface IP 2. ZONE QUERY Cloudflare API v4 GET Current State Scoped Bearer Auth DNS Edit + Zone Read GET A & AAAA Records Fetches published DNS entries TTL & Proxy Attributes Configured TTL & proxy state 3. RECONCILIATION Delta Comparison Drift Detection Compare Managed State IP, TTL & proxy differences Zero-Op Bypass No API mutations if matching Mutation Dispatch PATCH update when different 4. RECORD SYNC API Mutation PATCH Record & Sleep PATCH Update Updates A / AAAA Audit Log Structured events Daemon Interval Idles until next tick
State Reconciliation Cycle: Cirrusync runs an auditable, four-stage reconciliation loop. It discovers enabled public address families, reads Cloudflare DNS state with scoped tokens, and updates records when managed content, TTL, or proxy state differs. This reduces unnecessary writes; API rate limits still apply.

Dual-Stack Discovery

Determines the host's true public IPv4 and IPv6 addresses via redundant external endpoints, preventing incorrect LAN address updates.

Scoped API Token Security

Uses a Cloudflare API token with Zone Read and DNS Edit permissions limited to the managed zones. Global API Keys are not supported.

Differential Updates Only

Reads existing Cloudflare records and writes when managed content, TTL, or proxy state differs. Missing records can optionally be created, and retry logic handles rate limits and transient failures.

Multi-Zone & Multi-Record

Synchronizes multiple domain names across different Cloudflare DNS zones in a single execution loop with isolated per-zone error reporting.

systemd Native Service

Includes production systemd service definitions with security hardening (sandboxing, private temporary directories, and non-root execution).

One-Shot Test Mode

Supports manual single-run sync via command line for verification, CI pipelines, or lightweight cron-based task runners.

Operational Boundary Notice
Technical Scope Limitation: Dynamic DNS updates public DNS records to match an external IP address; it does NOT circumvent Carrier-Grade NAT (CGNAT), solve missing router port-forwarding rules, bypass ISP port blocks, or adjust local router firewall filters. For hosts behind CGNAT, combine with a VPN tunnel, overlay network, or relay.

Configuration Workflow

Use the repository installer to prepare the service and restricted token file, then edit its TOML configuration. The example below manages an existing A record. AAAA records require IPv6 discovery to be enabled and a separate record entry:

/etc/cirrusync/config.toml

# Scoped token: Zone Read + DNS Edit for the target zone

[cloudflare]

api_token_file = "/etc/cirrusync/token"

# Account-owned tokens also require cloudflare.account_id.


# Target DNS records to synchronize

[[records]]

zone = "example.com"

name = "vpn.example.com"

type = "A"

proxied = false


# Run a single synchronization test

cirrusync once --config /etc/cirrusync/config.toml