{"id":19158482,"url":"https://github.com/zebradil/cloudflare-dynamic-dns","last_synced_at":"2026-03-04T04:07:07.992Z","repository":{"id":39879075,"uuid":"398911074","full_name":"Zebradil/cloudflare-dynamic-dns","owner":"Zebradil","description":"Dynamic DNS client for Cloudflare with IPv6/IPv4 support","archived":false,"fork":false,"pushed_at":"2025-04-09T03:20:32.000Z","size":548,"stargazers_count":76,"open_issues_count":2,"forks_count":10,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T20:08:03.672Z","etag":null,"topics":["cloudflare","dns","dynamic-dns","dynamic-dns-client","systemd-timer"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Zebradil.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-22T22:08:32.000Z","updated_at":"2025-04-09T03:20:35.000Z","dependencies_parsed_at":"2023-02-18T20:30:38.219Z","dependency_job_id":"ac1425fb-89c5-4a9f-9b30-ef1a99a4d770","html_url":"https://github.com/Zebradil/cloudflare-dynamic-dns","commit_stats":null,"previous_names":[],"tags_count":79,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zebradil%2Fcloudflare-dynamic-dns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zebradil%2Fcloudflare-dynamic-dns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zebradil%2Fcloudflare-dynamic-dns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zebradil%2Fcloudflare-dynamic-dns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Zebradil","download_url":"https://codeload.github.com/Zebradil/cloudflare-dynamic-dns/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103872,"owners_count":21048245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","dns","dynamic-dns","dynamic-dns-client","systemd-timer"],"created_at":"2024-11-09T08:44:06.796Z","updated_at":"2026-03-04T04:07:07.931Z","avatar_url":"https://github.com/Zebradil.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dynamic DNS client for Cloudflare\n\nA CLI tool for updating A/AAAA record at Cloudflare DNS with the currently detected address of the specified network interface.\n\n## Features\n\n- Supports:\n  - IPv4 and IPv6\n  - Multiple domains with the same address\n  - Multiple hosts in the same domain\n- Tries to be smart about selecting the address to use\n- Includes systemd service and timer files for automation\n- Can be run in a Docker container\n- Configuration via command line arguments, config file or environment variables\n\n## Usage\n\nThe rest of this section is the output of `cloudflare-dynamic-dns --help`.\n\n\u003c!-- BEGIN CFDDNS_USAGE --\u003e\n\u003cpre\u003e\n\nSelects an address from the specified network interface or via an external\ncommand and updates A or AAAA records at Cloudflare for the configured domains.\nSupports both IPv4 and IPv6.\n\nRequired configuration options\n--------------------------------------------------------------------------------\n\n--iface:   network interface name to look up for an address\n  or\n--ipcmd:   shell command to run to get the address, should return one address\n           per line. Uses https://github.com/mvdan/sh as the shell.\n           Examples:\n             - curl -fsSL https://api6.ipify.org\n             - echo -e \"127.0.0.1\\n127.0.0.2\"\n\n--domains: one or more domain names to assign the address to\n--token:   Cloudflare API token with edit access rights to the DNS zone\n\nIPv6 address selection\n--------------------------------------------------------------------------------\n\nWhen multiple IPv6 addresses are found on the interface or received from the\nexternal command (e.g., when using --ipcmd), the following rules are used to\nselect the one to use:\n    1. Only global unicast addresses (GUA) and unique local addresses (ULA) are\n       considered.\n    2. GUA addresses are preferred over ULA addresses.\n    3. Unique EUI-64 addresses are preferred over randomly generated addresses.\n    4. If priority subnets are specified, addresses from the subnet with the\n       highest priority are selected. The priority is determined by the order of\n       subnets specified on the command line or in the config file.\n\nIPv4 address selection\n--------------------------------------------------------------------------------\n\nWhen multiple IPv4 addresses are found on the interface or received from the\nexternal command (e.g., when using --ipcmd), the following rules are used to\nselect the one to use:\n    1. All IPv4 addresses are considered.\n    2. Public addresses are preferred over Shared Address Space (RFC 6598)\n       addresses.\n    3. Shared Address Space addresses are preferred over private addresses.\n    4. Private addresses are preferred over loopback addresses.\n    5. If priority subnets are specified, addresses from the subnet with the\n       highest priority are selected. The priority is determined by the order of\n       subnets specified on the command line or in the config file.\n\nNon-public addresses are logged as warnings but are still used. They can be\nuseful in private networks or when using a VPN.\n\nNOTE: Cloudflare doesn't allow proxying of records with non-public addresses.\n\nDaemon mode\n--------------------------------------------------------------------------------\n\nBy default, the program runs once and exits. This mode of operation can be\nchanged by setting the --run-every flag to a duration greater than 1m. In this\ncase, the program will run repeatedly, waiting the duration between runs. It\nwill stop if killed or if failed.\n\nState file\n--------------------------------------------------------------------------------\n\nSetting --state-file makes the program to retain the previously used address\nbetween runs to avoid unnecessary calls to the Cloudflare API.\n\nThe value is used as the state file path. When used with an empty value, the\nstate file is named after the interface name and the domains, and is stored\neither in the current directory or in the directory specified by the\nSTATE_DIRECTORY environment variable.\n\nThe STATE_DIRECTORY environment variable is automatically set by systemd. It\ncan be set manually when running the program outside of systemd.\n\nMultihost mode (EXPERIMENTAL)\n--------------------------------------------------------------------------------\n\nIn this mode, it is possible to assign multiple addresses to a single or\nmultiple domains. For correct operation, this mode must be enabled on all hosts\nparticipating in the same domain and different host-ids must be specified for\neach host (see --host-id option). This mode is enabled by passing --multihost\nflag.\n\nIn the multihost mode, the program will manage only the DNS records that have\nthe same host-id as the one specified on the command line or in the config file.\nIf an existing record has no host-id but has the same address as the target one,\nit will be claimed by this host via setting the corresponding host-id. Any other\nrecords will be ignored. This allows multiple hosts to share the same domain\nwithout interfering with each other. The host-id is stored in the Cloudflare DNS\ncomments field (see https://developers.cloudflare.com/dns/manage-dns-records/reference/record-attributes/).\n\nPersistent configuration\n--------------------------------------------------------------------------------\n\nThe program can be configured using a config file. The default location is\n$HOME/.cloudflare-dynamic-dns.yaml. The config file location can be overridden\nusing the --config flag. The config file format is YAML. The following options\nare supported (with example values):\n\n    # === required fields\n    # either iface or ipcmd must be specified\n    iface: eth0\n    # ipcmd: curl -fsSL https://api6.ipify.org\n    token: cloudflare-api-token\n    domains:\n      - example.com\n      - \"*.example.com\"\n    # === optional fields\n    # --- mode\n    stack: ipv6\n    # --- UI\n    log-level: info\n    # --- logic\n    priority-subnets:\n      - 2001:db8::/32\n      - 2001:db8:1::/48\n    multihost: true\n    host-id: homelab-node-1\n    # --- DNS record details\n    proxy: enabled\n    ttl: 180\n    # --- daemon mode\n    run-every: 10m\n    state-file: /tmp/cfddns-eth0.state\n\nEnvironment variables\n--------------------------------------------------------------------------------\n\nThe configuration options can be specified as environment variables. To make an\nenvironment variable name, prefix a flag name with CFDDNS_, replace dashes with\nunderscores, and convert to uppercase. List values are specified as a single\nstring containing elements separated by spaces.\nFor example:\n\n    CFDDNS_CONFIG=/path/to/config.yaml\n    CFDDNS_IFACE=eth0\n    CFDDNS_IPCMD='curl -fsSL https://api6.ipify.org'\n    CFDDNS_TOKEN=cloudflare-api-token\n    CFDDNS_DOMAINS='example.com *.example.com'\n    CFDDNS_STACK=ipv6\n    CFDDNS_LOG_LEVEL=info\n    CFDDNS_PRIORITY_SUBNETS='2001:db8::/32 2001:db8:1::/48'\n    CFDDNS_MULTIHOST=true\n    CFDDNS_HOST_ID=homelab-node-1\n    CFDDNS_PROXY=enabled\n    CFDDNS_TTL=180\n    CFDDNS_RUN_EVERY=10m\n    CFDDNS_STATE_FILE=/tmp/cfddns-eth0.state\n\nUsage:\n  cloudflare-dynamic-dns [flags]\n\nFlags:\n      --config string              config file (default is $HOME/.cloudflare-dynamic-dns.yaml)\n      --domains strings            Domain names to assign the address to.\n  -h, --help                       help for cloudflare-dynamic-dns\n      --host-id string             Unique host identifier. Must be specified in multihost mode.\n                                   Must be a valid DNS label. It is stored in the Cloudflare DNS comments field in\n                                   the format: \"host-id (managed by cloudflare-dynamic-dns)\"\n      --iface string               Network interface to look up for an address.\n      --ipcmd string               External command to run to get the address.\n      --log-level string           Sets logging level: trace, debug, info, warning, error, fatal, panic. (default \"info\")\n      --multihost                  Enable multihost mode.\n                                   In this mode it is possible to assign multiple addresses to a single domain.\n                                   For correct operation, this mode must be enabled on all participating hosts and\n                                   different host-ids must be specified for each host (see --host-id option).\n      --priority-subnets strings   Subnets to prefer over others.\n                                   If multiple addresses are found on the interface,\n                                   the one from the subnet with the highest priority is used.\n      --proxy string               Override proxy setting for created or updated DNS records.\n                                   If set to \"auto\", preserves the current state of an updated record.\n                                   Allowed values: \"enabled\", \"disabled\", \"auto\". (default \"auto\")\n      --run-every string           Re-run the program every N duration until it's killed.\n                                   The format is described at https://pkg.go.dev/time#ParseDuration.\n                                   The minimum duration is 1m. Examples: 4h30m15s, 5m.\n      --stack string               IP stack version: ipv4 or ipv6 (default \"ipv6\")\n      --state-file string          Enables usage of a state file.\n                                   In this mode, the previously used address is preserved\n                                   between runs to avoid unnecessary calls to Cloudflare API.\n                                   Automatically selects where to store the state file if no\n                                   value is specified. See the State file section in usage.\n      --token string               Cloudflare API token with DNS edit access rights.\n      --ttl int                    Time to live, in seconds, of the DNS record.\n                                   Must be between 60 and 86400, or 1 for 'automatic'. (default 1)\n  -v, --version                    version for cloudflare-dynamic-dns\n\u003c/pre\u003e\n\u003c!-- END CFDDNS_USAGE --\u003e\n\n## Installation\n\n### AUR\n\nThere are two packages in AUR (\n[1](https://aur.archlinux.org/packages/cloudflare-dynamic-dns/),\n[2](https://aur.archlinux.org/packages/cloudflare-dynamic-dns-bin/)\n), that can be used on Arch-based distros:\n\n```shell\nyay -S cloudflare-dynamic-dns\n# OR\nyay -S cloudflare-dynamic-dns-bin\n```\n\n### Docker\n\nSee the\n[container registry page](https://github.com/Zebradil/cloudflare-dynamic-dns/pkgs/container/cloudflare-dynamic-dns)\nfor details.\n\n```shell\ndocker pull ghcr.io/zebradil/cloudflare-dynamic-dns:latest\n```\n\n### DEB, RPM, APK\n\nSee the [latest release page](https://github.com/Zebradil/cloudflare-dynamic-dns/releases/latest) for the full list of\npackages.\n\n### Nix\n\nThe package is available in the Nixpkgs repository under the name\n[`cloudflare-dynamic-dns`](https://search.nixos.org/packages?channel=unstable\u0026show=cloudflare-dynamic-dns\u0026from=0\u0026size=50\u0026sort=relevance\u0026type=packages\u0026query=cloudflare-dynamic-dns).\n\n```shell\nnix-shell -p cloudflare-dynamic-dns\n```\n\n\u003e [!NOTE]  \n\u003e The version in Nixpkgs is falling behind the latest release. If you need the latest version, use the flake.\n\u003e\n\u003e ```shell\n\u003e nix shell 'github:Zebradil/cloudflare-dynamic-dns/main#cloudflare-dynamic-dns'\n\u003e ```\n\n### Manual\n\nDownload the archive for your OS from the [releases page](https://github.com/Zebradil/cloudflare-dynamic-dns/releases).\n\nOr get the source code and build the binary:\n\n```shell\ngit clone https://github.com/Zebradil/cloudflare-dynamic-dns.git\n# OR\ncurl -sL https://github.com/Zebradil/cloudflare-dynamic-dns/archive/refs/heads/master.tar.gz | tar xz\n\ncd cloudflare-dynamic-dns-master\ngo build -o cloudflare-dynamic-dns main.go\n```\n\nNow you can run `cloudflare-dynamic-dns` manually (see [Usage](#usage) section).\n\nIf you want to do some automation with systemd, `cloudflare-dynamic-dns` has to be installed system-wide\n(it _is_ possible to run systemd timer and service without root privileges, but I do not provide ready-to-use configuration for this yet):\n\n```shell\nsudo install -Dm755 cloudflare-dynamic-dns -t /usr/bin\nsudo install -Dm644 systemd/* -t /usr/lib/systemd/system\nsudo install -m700 -d /etc/cloudflare-dynamic-dns/config.d\n```\n\n## Usage examples\n\n### Run manually\n\n0. Follow the steps from the [Installation](#installation) section.\n1. Run `./cloudflare-dynamic-dns --domains 'example.com,*.example.com' --iface eth0 --token cloudflare-api-token`\n   - NOTE: instead of compiling `cloudflare-dynamic-dns` binary, it can be replaced with `go run main.go` in the command above.\n\nInstead of specifying command line arguments, it is possible to create `~/.cloudflare-dynamic-dns.yaml` with the following structure:\n\n```yaml\niface: eth0\ntoken: cloudflare-api-token\ndomains:\n  - example.com\n  - '*.example.com'\n```\n\nAnd then run `./cloudflare-dynamic-dns` (or `go run main.go`) without arguments.\nOr put the configuration in any place and specify it with `--config` flag:\n\n```shell\n./cloudflare-dynamic-dns --config /any/place/config.yaml\n```\n\n#### Run in a Docker container\n\nFor the binary, the usage is the same as for the manual run.\nFor the Docker container, we need to mount the configuration file into the container and provide access to the network stack of the host machine:\n\n```shell\ndocker run --rm \\\n  --volume=\"/any/place/config.yaml:/config.yaml\" \\\n  --network=host \\\n  ghcr.io/zebradil/cloudflare-dynamic-dns:latest \\\n    --config=/config.yaml\n```\n\nTo run the program in daemon mode, add `--run-every` flag (and `--detach` if you want to run it in the background):\n\n```shell\ndocker run --rm \\\n  --volume=\"/any/place/config.yaml:/config.yaml\" \\\n  --network=host \\\n  --detach \\\n  ghcr.io/zebradil/cloudflare-dynamic-dns:latest \\\n    --config=/config.yaml \\\n    --run-every=5m\n```\n\n### Systemd service and timer\n\nIt is possible to run `cloudflare-dynamic-dns` periodically via systemd.\nThis requires privileged access to the system.\nMake sure that required systemd files are installed (see [Installation](#installation) section for details).\n\n```shell\n# 1. Create configuration file `/etc/cloudflare-dynamic-dns/config.d/\u003cname\u003e.yaml`\n#    For example (I use \"example.com\" as \u003cname\u003e, replace the values according to your needs):\nsudo tee -a /etc/cloudflare-dynamic-dns/config.d/example.com.yaml \u003c\u003cEOF\niface: eth0\ntoken: cloudflare-api-token\ndomains:\n  - example.com\n  - \"*.example.com\"\nEOF\n\n# 3. Enable systemd timer\nsudo systemctl enable --now cloudflare-dynamic-dns@example.com.timer\n```\n\nThis way (via running multiple timers) you can use multiple configurations at the same time.\n\nBy default, a timer is triggered one minute after boot and then every 5 minutes. It is not configurable currently.\n\nState files are used to avoid unnecessary requests to Cloudflare API.\nThey're created in `/var/lib/cloudflare-dynamic-dns/` and named using configuration variables in corresponding config files (`iface` and a hash of `domains`).\nA state file contains an address which was set in a Cloudflare DNS AAAA or A record during the last successful run.\nIf the current address is the same as the one in the state file, no additional API requests are done.\n\n## Development\n\nBuilds and releases are done with [goreleaser](https://goreleaser.com/).\n\nThere are several ways to build the application:\n\n```shell\n# Build with go for the current platform\ngo build -o cloudflare-dynamic-dns main.go\n\n# Build with GoReleaser for all configured platforms\ntask go:build\n\n# Use Docker\ndocker build -t cloudflare-dynamic-dns -f dev.Dockerfile .\n```\n\nCheck the [Taskfile.yml](./Taskfile.yml) for more details.\n\n### GeReleaser\n\n:warning: Do not change `.goreleaser.yml` manually, do changes in `.goreleaser.ytt.yml` and run\n`task misc:build:goreleaser-config` instead (requires [`ytt`](https://carvel.dev/ytt/) installed).\n\n### Documentation\n\nThe [usage](#usage) section is generated by the [update_readme](./scripts/update_readme) script.\nFor convenience, `task docs:update-readme` can be used to run it.\n\n### Vendor dependencies\n\nMost of the dependencies are managed with Go modules, but there is one exception: the [execext](internal/execext)\npackage, which is an internal package of the [go-task](https://github.com/go-task/task) project and is vendored using\n[vendir](https://github.com/carvel-dev/vendir).\n\nTo update the vendored dependencies, run `vendir sync` in the root of the project. Then commit `vendir.lock.yml` and the\nupdated dependencies.\n\n## License\n\n[MIT](LICENSE)\n\nCode of the [execext](internal/execext) package is taken from the [go-task](https://github.com/go-task/task) project and\nis licensed under the [MIT license](https://github.com/go-task/task/blob/0941de3318ba2cd8e8611acc4e225b0bea5c817b/LICENSE).\n\n## Disclaimer\n\nThis project is not affiliated with Cloudflare.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzebradil%2Fcloudflare-dynamic-dns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzebradil%2Fcloudflare-dynamic-dns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzebradil%2Fcloudflare-dynamic-dns/lists"}