{"id":13581479,"url":"https://github.com/favonia/cloudflare-ddns","last_synced_at":"2025-05-15T04:04:25.147Z","repository":{"id":37039628,"uuid":"373865136","full_name":"favonia/cloudflare-ddns","owner":"favonia","description":"🌟 A small, feature-rich, and robust Cloudflare DDNS updater","archived":false,"fork":false,"pushed_at":"2025-05-05T23:08:22.000Z","size":1968,"stargazers_count":1615,"open_issues_count":43,"forks_count":66,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-05-15T04:04:04.477Z","etag":null,"topics":["cloudflare","ddns","ddns-client","ddns-updater","dns","dns-over-https","docker","docker-compose","docker-image","dynamic-dns","go","golang","healthchecks","ipv6","selfhosted"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/favonia.png","metadata":{"files":{"readme":"README.markdown","changelog":"CHANGELOG.markdown","contributing":"docs/CONTRIBUTING.markdown","funding":null,"license":"LICENSE","code_of_conduct":"docs/CODE_OF_CONDUCT.markdown","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-06-04T14:20:16.000Z","updated_at":"2025-05-14T21:34:55.000Z","dependencies_parsed_at":"2023-10-12T11:28:57.348Z","dependency_job_id":"3504b3ea-8487-4597-ae48-3f8c30f2e5ec","html_url":"https://github.com/favonia/cloudflare-ddns","commit_stats":{"total_commits":740,"total_committers":4,"mean_commits":185.0,"dds":0.4297297297297298,"last_synced_commit":"6413a4346406252c073828f56e55070b36936d66"},"previous_names":["favonia/cloudflare-ddns-go"],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/favonia%2Fcloudflare-ddns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/favonia%2Fcloudflare-ddns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/favonia%2Fcloudflare-ddns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/favonia%2Fcloudflare-ddns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/favonia","download_url":"https://codeload.github.com/favonia/cloudflare-ddns/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270641,"owners_count":22042858,"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","ddns","ddns-client","ddns-updater","dns","dns-over-https","docker","docker-compose","docker-image","dynamic-dns","go","golang","healthchecks","ipv6","selfhosted"],"created_at":"2024-08-01T15:02:03.021Z","updated_at":"2025-05-15T04:04:25.088Z","avatar_url":"https://github.com/favonia.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# 🌟 Cloudflare DDNS\n\n[![Github Source](https://img.shields.io/badge/source-github-orange)](https://github.com/favonia/cloudflare-ddns)\n[![Go Reference](https://pkg.go.dev/badge/github.com/favonia/cloudflare-ddns/.svg)](https://pkg.go.dev/github.com/favonia/cloudflare-ddns/)\n[![Codecov](https://img.shields.io/codecov/c/github/favonia/cloudflare-ddns)](https://app.codecov.io/gh/favonia/cloudflare-ddns)\n[![Docker Image Size](https://img.shields.io/docker/image-size/favonia/cloudflare-ddns/latest)](https://hub.docker.com/r/favonia/cloudflare-ddns)\n[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/6680/badge)](https://bestpractices.coreinfrastructure.org/projects/6680)\n[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/favonia/cloudflare-ddns/badge)](https://securityscorecards.dev/viewer/?uri=github.com/favonia/cloudflare-ddns)\n\nA feature-rich and robust Cloudflare DDNS updater with a small footprint. The program will detect your machine’s public IP addresses and update DNS records using the Cloudflare API.\n\n## 📜 Highlights\n\n### ⚡ Efficiency\n\n- 🤏 The Docker image takes less than 5 MB after compression.\n- 🔁 The Go runtime re-uses existing HTTP connections.\n- 🗃️ Cloudflare API responses are cached to reduce the API usage.\n\n### 💯 Complete Support of Domain Names\n\n- 😌 You can simply list domains (_e.g._, `www.a.org, hello.io`) without knowing their DNS zones.\n- 🌍 [Internationalized domain names](https://en.wikipedia.org/wiki/Internationalized_domain_name) (_e.g._, `🐱.example.org` and `日本｡co｡jp`) are fully supported.\n- 🃏 [Wildcard domains](https://en.wikipedia.org/wiki/Wildcard_DNS_record) (_e.g._, `*.example.org`) are also supported.\n- 🕹️ You can toggle IPv4 (`A` records) and IPv6 (`AAAA` records) for each domain.\n\n### 🌥️ Cloudflare-specific Features\n\n- 😶‍🌫️ You can toggle [Cloudflare proxying](https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/) for each domain.\n- 📝 You can set [comments](https://developers.cloudflare.com/dns/manage-dns-records/reference/record-attributes/) for new DNS records.\n- 📜 The updater can maintain [lists](https://developers.cloudflare.com/waf/tools/lists/custom-lists/) of detected IP addresses. These lists can then be referenced in any Cloudflare product that uses [Cloudflare’s Rules language](https://developers.cloudflare.com/ruleset-engine/), such as [Cloudflare Web Application Firewall (WAF)](https://developers.cloudflare.com/waf/) and [Cloudflare Rules](https://developers.cloudflare.com/rules/). (We call the lists “WAF lists”, but their use is not limited to Cloudflare WAF.)\n\n### 👁️ Integration with Notification Services\n\n- 🩺 The updater can report to [Healthchecks](https://healthchecks.io) or [Uptime Kuma](https://uptime.kuma.pet) so that you receive notifications when it fails to update IP addresses.\n- 📣 The updater can also actively update you via any service supported by the [shoutrrr library](https://containrrr.dev/shoutrrr/), including emails, major notification services, major messaging platforms, and generic webhooks.\n\n### 🕵️ Minimum Privacy Impact\n\nBy default, public IP addresses are obtained via [Cloudflare’s debugging page](https://one.one.one.one/cdn-cgi/trace). This minimizes the impact on privacy because we are already using the Cloudflare API to update DNS records. Moreover, if Cloudflare servers are not reachable, chances are you cannot update DNS records anyways.\n\n### 🛡️ Attention to Security\n\n- 🛡️ The updater uses only HTTPS or [DNS over HTTPS](https://en.wikipedia.org/wiki/DNS_over_HTTPS) to detect IP addresses. This makes it harder for someone else to trick the updater into updating your DNS records with wrong IP addresses. See the [Security Model](docs/DESIGN.markdown#network-security-threat-model) for more information.\n- \u003cdetails\u003e\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ✍️ You can verify the Docker images were built from this repository using the cosign tool.\u003c/summary\u003e\n\n  ```bash\n  cosign verify favonia/cloudflare-ddns:latest \\\n    --certificate-identity-regexp https://github.com/favonia/cloudflare-ddns/ \\\n    --certificate-oidc-issuer https://token.actions.githubusercontent.com\n  ```\n\n  Note: this only proves that the Docker image is from this repository, assuming that no one hacks into GitHub or the repository. It does not prove that the code itself is secure.\n\n- \u003cdetails\u003e\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📚 The updater uses only established open-source Go libraries.\u003c/summary\u003e\n\n  - [cloudflare-go](https://github.com/cloudflare/cloudflare-go):\\\n    The official Go binding of Cloudflare API v4.\n  - [cron](https://github.com/robfig/cron):\\\n    Parsing of Cron expressions.\n  - [go-retryablehttp](https://github.com/hashicorp/go-retryablehttp):\\\n    HTTP clients with automatic retries and exponential backoff.\n  - [go-querystring](https://github.com/google/go-querystring):\\\n    A library to construct URL query parameters.\n  - [shoutrrr](https://github.com/containrrr/shoutrrr):\\\n    A notification library for sending general updates.\n  - [ttlcache](https://github.com/jellydator/ttlcache):\\\n    In-memory cache to hold Cloudflare API responses.\n  - [mock](https://go.uber.org/mock) (for testing only):\\\n    A comprehensive, semi-official framework for mocking.\n  - [testify](https://github.com/stretchr/testify) (for testing only):\\\n    A comprehensive tool set for testing Go programs.\n\n  \u003c/details\u003e\n\n## ⛷️ Quick Start\n\n\u003cdetails\u003e\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🐋 Directly run the Docker image\u003c/summary\u003e\n\n```bash\ndocker run \\\n  --network host \\\n  -e CLOUDFLARE_API_TOKEN=YOUR-CLOUDFLARE-API-TOKEN \\\n  -e DOMAINS=example.org,www.example.org,example.io \\\n  -e PROXIED=true \\\n  favonia/cloudflare-ddns:latest\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🧬 Directly run the updater from its source\u003c/summary\u003e\n\nYou need the [Go tool](https://golang.org/doc/install) to run the updater from its source.\n\n```bash\nCLOUDFLARE_API_TOKEN=YOUR-CLOUDFLARE-API-TOKEN \\\n  DOMAINS=example.org,www.example.org,example.io \\\n  PROXIED=true \\\n  go run github.com/favonia/cloudflare-ddns/cmd/ddns@latest\n```\n\n\u003c/details\u003e\n\n## 🏁 Deployment as a System Service\n\nSee [community-contributed sample configurations](./contrib/README.markdown) for OpenBSD.\n\n## 🐋 Deployment with Docker Compose\n\n### 📦 Step 1: Updating the Compose File\n\nIncorporate the following fragment into the compose file (typically `docker-compose.yml` or `docker-compose.yaml`). The template may look a bit scary, but only because it includes various optional flags for extra security protection.\n\n```yaml\nservices:\n  cloudflare-ddns:\n    image: favonia/cloudflare-ddns:latest\n    # Choose the appropriate tag based on your need:\n    # - \"latest\" for the latest stable version (which could become 2.x.y\n    #   in the future and break things)\n    # - \"1\" for the latest stable version whose major version is 1\n    # - \"1.x.y\" to pin the specific version 1.x.y\n    network_mode: host\n    # This bypasses network isolation and makes IPv6 easier (optional; see below)\n    restart: always\n    # Restart the updater after reboot\n    user: \"1000:1000\"\n    # Run the updater with specific user and group IDs (in that order).\n    # You can change the two numbers based on your need.\n    read_only: true\n    # Make the container filesystem read-only (optional but recommended)\n    cap_drop: [all]\n    # Drop all Linux capabilities (optional but recommended)\n    security_opt: [no-new-privileges:true]\n    # Another protection to restrict superuser privileges (optional but recommended)\n    environment:\n      - CLOUDFLARE_API_TOKEN=YOUR-CLOUDFLARE-API-TOKEN\n        # Your Cloudflare API token\n      - DOMAINS=example.org,www.example.org,example.io\n        # Your domains (separated by commas)\n      - PROXIED=true\n        # Tell Cloudflare to cache webpages and hide your IP (optional)\n#networks:\n#  LAN0:\n#    external: true\n#    name: LAN0\n# Introduce custom Docker networks to the 'services' in this file. A common use case\n# for this is binding one of the 'services' to a specific network interface available at\n# Docker's host. This section is required for the 'networks' section of each 'services'.\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🔑 \u003ccode\u003eCLOUDFLARE_API_TOKEN\u003c/code\u003e is your Cloudflare API token\u003c/summary\u003e\n\nThe value of `CLOUDFLARE_API_TOKEN` should be an API **token** (_not_ an API key), which can be obtained from the [API Tokens page](https://dash.cloudflare.com/profile/api-tokens). Use the **Edit zone DNS** template to create a token. The less secure API key authentication is deliberately _not_ supported.\n\nThere is an optional feature (available since version 1.14.0) that lets you maintain a [WAF list](https://developers.cloudflare.com/waf/tools/lists/custom-lists/) of detected IP addresses. To use this feature, edit the token and grant it the **Account - Account Filter Lists - Edit** permission. If you only need to update WAF lists, not DNS records, you can remove the **Zone - DNS - Edit** permission. Refer to the detailed documentation below for information on updating WAF lists.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📍 \u003ccode\u003eDOMAINS\u003c/code\u003e is the list of domains to update\u003c/summary\u003e\n\nThe value of `DOMAINS` should be a list of [fully qualified domain names (FQDNs)](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) separated by commas. For example, `DOMAINS=example.org,www.example.org,example.io` instructs the updater to manage the domains `example.org`, `www.example.org`, and `example.io`. These domains do not have to share the same DNS zone---the updater will take care of the DNS zones behind the scene.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🚨 Remove \u003ccode\u003ePROXIED=true\u003c/code\u003e if you are \u003cem\u003enot\u003c/em\u003e running a web server\u003c/summary\u003e\n\nThe setting `PROXIED=true` instructs Cloudflare to cache webpages and hide your IP addresses. If you wish to bypass that and expose your actual IP addresses, remove `PROXIED=true`. If your traffic is not HTTP(S), then Cloudflare cannot proxy it and you should probably turn off the proxying by removing `PROXIED=true`. The default value of `PROXIED` is `false`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📴 Add \u003ccode\u003eIP6_PROVIDER=none\u003c/code\u003e if you want to disable IPv6 completely\u003c/summary\u003e\n\nThe updater, by default, will attempt to update DNS records for both IPv4 and IPv6, and there is no harm in leaving the automatic detection on even if your network does not work for one of them. However, if you want to disable IPv6 entirely (perhaps to avoid seeing the detection errors), add `IP6_PROVIDER=none`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📡 Use IPv6 without bypassing network isolation (without \u003ccode\u003enetwork_mode: host\u003c/code\u003e)\u003c/summary\u003e\n\nThe easiest way to enable IPv6 is to use `network_mode: host` so that the updater can access the host IPv6 network directly. This has the downside of bypassing the network isolation. If you wish to keep the updater isolated from the host network, remove `network_mode: host` and follow the steps in the [official Docker documentation to enable IPv6](https://docs.docker.com/config/daemon/ipv6/). Do use newer versions of Docker that come with much better IPv6 support!\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🛜 Bind to a specific network interface for updates\u003c/summary\u003e\n\n📜 This method uses a MacVLAN sub-device to bind to a specific network interface and may bypass your custom `iptables` and `nftables` configurations.\n\nTo be able to use a specific network interface when detecting the IP in the DDNS updates, the following Docker network must be created before running a Docker container with a custom network:\n\n```bash\ndocker network create\n    -d macvlan\n    -o parent=eth0 # host network interface name to bind to\n    --subnet=192.168.1.0/24 # IP space for running containers within this network\n    --gateway=192.168.1.1 # IP address of the gateway/router\n    --ip-range=192.168.1.128/25 # communication IP range for containers in this network\n    LAN0 # name that will be used in the docker-compose.yml\n```\n\nOnce the new Docker network is created, add the following to the Docker Compose that will start the `cloudflare-ddns` service. This enforces all requests from this service to go through the mentioned network, e.g. 'LAN0'.\n\n```yaml\nnetworks:\n  LAN0:\n  #  ipv4_address: 192.168.1.131 # A static IP within subnet (line can be removed for a random IP)\n```\n\nIf a static IP is preferred, an `ipv4_address` section like the example can be added. NOTE: this IP must be within the `--subnet` of the Docker network.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🛡️ Change \u003ccode\u003euser: \"1000:1000\"\u003c/code\u003e to the user and group IDs you want to use\u003c/summary\u003e\n\nChange `1000:1000` to `USER:GROUP` for the `USER` and `GROUP` IDs you wish to use to run the updater. The settings `cap_drop`, `read_only`, and `no-new-privileges` in the template provide additional protection, especially when you run the container as a non-superuser.\n\n\u003c/details\u003e\n\n### 🚀 Step 2: Building and Running the Container\n\n```bash\ndocker-compose pull cloudflare-ddns\ndocker-compose up --detach --build cloudflare-ddns\n```\n\n## ❓ Frequently Asked Questions\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ❔ I simulated an IP address change by editing the DNS records, but the updater never picked it up!\u003c/summary\u003e\n\nPlease rest assured that the updater is working as expected. **It will update the DNS records _immediately_ for a real IP change.** Here is a detailed explanation. There are two causes of an IP mismatch:\n\n1. A change of your actual IP address (a _real_ change), or\n2. A change of the IP address in the DNS records (a _simulated_ change).\n\nThe updater assumes no one will actively change the DNS records. In other words, it assumes simulated changes will not happen. It thus caches the DNS records and cannot detect your simulated changes. However, when your actual IP address changes, the updater will immediately update the DNS records. Also, the updater will eventually check the DNS records and detect simulated changes after `CACHE_EXPIRATION` (six hours by default) has passed.\n\nIf you really wish to test the updater with simulated IP changes in the DNS records, you can set `CACHE_EXPIRATION=1ns` (all cache expiring in one nanosecond), effectively disabling the caching. However, it is recommended to keep the default value (six hours) to reduce your network traffic.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ❔ How can I see the timestamps of the IP checks and/or updates?\u003c/summary\u003e\n\nThe updater does not itself add timestamps because all major systems already timestamp everything:\n\n- If you are using Docker Compose, Kubernetes, or Docker directly, add the option `--timestamps` when viewing the logs.\n- If you are using Portainer, [enable “Show timestamp” when viewing the logs](https://docs.portainer.io/user/docker/containers/logs).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ❔ Why did the updater detect a public IP address different from the WAN IP address on my router?\u003c/summary\u003e\n\nIs your “public” IP address on your router between `100.64.0.0` and `100.127.255.255`? If so, you are within your ISP’s [CGNAT (Carrier-grade NAT)](https://en.wikipedia.org/wiki/Carrier-grade_NAT). In practice, there is no way for DDNS to work with CGNAT, because your ISP does not give you a real public IP address, nor does it allow you to forward IP packages to your router using cool protocols such as [Port Control Protocol](https://en.wikipedia.org/wiki/Port_Control_Protocol). You have to give up DDNS or switch to another ISP. You may consider other services such as [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) that can work around CGNAT.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ❔ How should I install this updater in ☸️ Kubernetes?\u003c/summary\u003e\n\nDue to high maintenance costs, the Kubernetes instructions have been removed. However, you can still generate Kubernetes configurations from the provided Docker Compose template using a conversion tool like [Kompose](https://kompose.io/). **Important:** Only use Kompose version 1.35.0 or later, as these versions support the `user: \"UID:GID\"` attribute with `:GID`.\n\nNote that a simple [Kubernetes Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) will suffice here. Since there’s no inbound network traffic, a [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) isn’t required.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ❔ Help! I got \u003ccode\u003eexec /bin/ddns: operation not permitted\u003c/code\u003e\u003c/summary\u003e\n\nCertain Docker installations may have issues with the `no-new-privileges` security option. If you cannot run Docker images with this option (including this updater), removing it might be necessary. This will slightly compromise security, but it’s better than not running the updater at all. If _only_ this updater is affected, please [report this issue on GitHub](https://github.com/favonia/cloudflare-ddns/issues/new).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ❔ I am getting \u003ccode\u003eerror code: 1034\u003c/code\u003e\u003c/summary\u003e\n\nWe have received reports of recent issues with the default IP provider, `cloudflare.trace`. Some users are encountering an \"error code: 1034,\" likely due to internal problems with Cloudflare's servers. To work around this, please upgrade the updater to version 1.15.1 or later. Alternatively, you may switch to a different IP provider.\n\n\u003c/details\u003e\n\n## 🎛️ Further Customization\n\n### ⚙️ All Settings\n\nThe emoji “🧪” indicates experimental features and the emoji “🤖” indicates technical details.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🔑 The Cloudflare API token\u003c/summary\u003e\n\n\u003e Starting with version 1.15.0, the updater supports environment variables that begin with `CLOUDFLARE_*`. Multiple environment variables can be used at the same time, provided they all specify the same token.\n\n| Name                                                      | Meaning                                                                                                                                |\n| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `CLOUDFLARE_API_TOKEN`                                    | The [Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens) to access the Cloudflare API                                |\n| `CLOUDFLARE_API_TOKEN_FILE`                               | A path to a file that contains the [Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens) to access the Cloudflare API |\n| `CF_API_TOKEN` (will be deprecated in version 2.0.0)      | Same as `CLOUDFLARE_API_TOKEN`                                                                                                         |\n| `CF_API_TOKEN_FILE` (will be deprecated version in 2.0.0) | Same as `CLOUDFLARE_API_TOKEN_FILE`                                                                                                    |\n\n\u003e 🚂 Cloudflare is updating its tools to use environment variables starting with `CLOUDFLARE_*` instead of `CF_*`. It is recommended to align your setting with this new convention. However, the updater will fully support both `CLOUDFLARE_*` and `CF_*` environment variables until version 2.0.0.\n\u003e\n\u003e 🔑 To update DNS records, the updater needs the **Zone - DNS - Edit** permission.\n\u003e\n\u003e 🔑 To manipulate WAF lists, the updater needs the **Account - Account Filter Lists - Edit** permission.\n\u003e\n\u003e 💡 `CLOUDFLARE_API_TOKEN_FILE` works well with [Docker secrets](https://docs.docker.com/compose/how-tos/use-secrets/) where secrets will be mounted as files at `/run/secrets/\u003cSECRET NAME\u003e`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📍 DNS domains and WAF lists to update\u003c/summary\u003e\n\n\u003e You need to specify at least one thing in `DOMAINS`, `IP4_DOMAINS`, `IP6_DOMAINS`, or 🧪 `WAF_LISTS` (since version 1.14.0) for the updater to update.\n\n| Name                                  | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `DOMAINS`                             | Comma-separated fully qualified domain names or wildcard domain names that the updater should manage for both `A` and `AAAA` records. Listing a domain in `DOMAINS` is equivalent to listing the same domain in both `IP4_DOMAINS` and `IP6_DOMAINS`.                                                                                                                                                                                                                                                                                                                                                                                                       |\n| `IP4_DOMAINS`                         | Comma-separated fully qualified domain names or wildcard domain names that the updater should manage for `A` records                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| `IP6_DOMAINS`                         | Comma-separated fully qualified domain names or wildcard domain names that the updater should manage for `AAAA` records                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n| 🧪 `WAF_LISTS` (since version 1.14.0) | \u003cp\u003e🧪 Comma-separated references of [WAF lists](https://developers.cloudflare.com/waf/tools/lists/custom-lists/) the updater should manage. A list reference is written in the format `\u003caccount-id\u003e/\u003clist-name\u003e` where `account-id` is your account ID and `list-name` is the list name; it should look like `0123456789abcdef0123456789abcdef/mylist`. If the referenced WAF list does not exist, the updater will try to create it.\u003c/p\u003e\u003cp\u003e🔑 The API token needs the **Account - Account Filter Lists - Edit** permission.\u003cbr/\u003e💡 See [how to find your account ID](https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/).\u003c/p\u003e |\n\n\u003e 🃏🤖 **Wildcard domains** (`*.example.org`) represent all subdomains that _would not exist otherwise._ Therefore, if you have another subdomain entry `sub.example.org`, the wildcard domain is independent of it, because it only represents the _other_ subdomains which do not have their own entries. Also, you can only have one layer of `*`---`*.*.example.org` would not work.\n\n\u003e 🌐🤖 **Internationalized domain names** are handled using the _nontransitional processing_ (fully compatible with IDNA2008). At this point, all major browsers and whatnot have switched to the same nontransitional processing. See [this useful FAQ on internationalized domain names](https://www.unicode.org/faq/idn.html).\n\n\u003e 🤖 Technical notes on WAF lists:\n\u003e\n\u003e 1. [Cloudflare does not allow single IPv6 addresses in a WAF list](https://developers.cloudflare.com/waf/tools/lists/custom-lists/#lists-with-ip-addresses-ip-lists), and thus the updater will use the smallest IP range allowed by Cloudflare that contains the detected IPv6 address.\n\u003e 2. The updater will delete IP addresses belonging to unmanaged IP families from the specified WAF lists (_e.g.,_ if you disable IPv6 with `IP6_PROVIDER=none`, then existing IPv6 addresses or IPv6 ranges in the lists will be deleted). The idea is that the list should contain only detected IP addresses.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🔍 IP address providers\u003c/summary\u003e\n\n| Name           | Meaning                                                                                                                                                                                                                                                                       | Default Value      |\n| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |\n| `IP4_PROVIDER` | This specifies how to detect the current IPv4 address. Available providers include `cloudflare.doh`, `cloudflare.trace`, `local`, `local.iface:\u003ciface\u003e`, `url:\u003cURL\u003e`, and `none`. The special `none` provider disables IPv4 completely. See below for a detailed explanation. | `cloudflare.trace` |\n| `IP6_PROVIDER` | This specifies how to detect the current IPv6 address. Available providers include `cloudflare.doh`, `cloudflare.trace`, `local`, `local.iface:\u003ciface\u003e`, `url:\u003cURL\u003e`, and `none`. The special `none` provider disables IPv6 completely. See below for a detailed explanation. | `cloudflare.trace` |\n\n\u003e 👉 The option `IP4_PROVIDER` governs `A`-type DNS records and IPv4 addresses in WAF lists, while the option `IP6_PROVIDER` governs `AAAA`-type DNS records and IPv6 addresses in WAF lists. The two options act independently of each other. You can specify different address providers for IPv4 and IPv6.\n\n| Provider Name                                                                            | Explanation                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `cloudflare.doh`                                                                         | Get the IP address by querying `whoami.cloudflare.` against [Cloudflare via DNS-over-HTTPS](https://developers.cloudflare.com/1.1.1.1/dns-over-https).                                                                                                                                                                                                                                                                                                                                                                                      |\n| `cloudflare.trace`                                                                       | Get the IP address by parsing the [Cloudflare debugging page](https://api.cloudflare.com/cdn-cgi/trace). **This is the default provider.**                                                                                                                                                                                                                                                                                                                                                                                                  |\n| `local`                                                                                  | \u003cp\u003eGet the IP address via local network interfaces and routing tables. The updater will use the local address that _would have_ been used for outbound UDP connections to Cloudflare servers. (No data will be transmitted.)\u003c/p\u003e\u003cp\u003e⚠️ The updater needs access to the host network (such as `network_mode: host` in Docker Compose) for this provider, for otherwise the updater will detect the addresses inside [the default bridge network in Docker](https://docs.docker.com/network/bridge/) instead of those in the host network.\u003c/p\u003e |\n| 🧪 `local.iface:\u003ciface\u003e` (available since version 1.15.0 but not finalized until 1.16.0) | \u003cp\u003e🧪 Get the IP address via the specific local network interface `iface`. The updater will choose the first global unicast IP address of the matching IP family (IPv4 or IPv6).\u003c/p\u003e\u003cp\u003e⚠️ The updater needs access to the host network (such as `network_mode: host` in Docker Compose) for this provider, for otherwise the updater cannot access host network interfaces.\u003c/p\u003e                                                                                                                                                             |\n| `url:\u003cURL\u003e`                                                                              | Fetch the IP address from a URL. The provider format is `url:` followed by the URL itself. For example, `IP4_PROVIDER=url:https://api4.ipify.org` will fetch the IPv4 address from \u003chttps://api4.ipify.org\u003e. Since version 1.15.0, the updater will enforce the matching protocol (IPv4 or IPv6) when connecting to the provided URL. Currently, only HTTP(S) is supported.                                                                                                                                                                 |\n| `none`                                                                                   | \u003cp\u003eStop the DNS updating for the specified IP version completely. For example `IP4_PROVIDER=none` will disable IPv4 completely. Existing DNS records will not be removed.\u003c/p\u003e\u003cp\u003e🧪 The IP addresses of the disabled IP version will be removed from WAF lists; so `IP4_PROVIDER=none` will remove all IPv4 addresses from all managed WAF lists. As the support of WAF lists is still experimental, this behavior is subject to changes and please [provide feedback](https://github.com/favonia/cloudflare-ddns/issues/new).\u003c/p\u003e           |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📅 Scheduling of IP detections and updates\u003c/summary\u003e\n\n| Name               | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Default Value                 |\n| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |\n| `CACHE_EXPIRATION` | The expiration of cached Cloudflare API responses. It can be any positive time duration accepted by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), such as `1h` or `10m`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | `6h0m0s` (6 hours)            |\n| `DELETE_ON_STOP`   | Whether managed DNS records and WAF lists should be deleted on exit. It can be any boolean value accepted by [strconv.ParseBool](https://pkg.go.dev/strconv#ParseBool), such as `true`, `false`, `0` or `1`. If a WAF list is used in a rule expression, the list cannot be deleted (for otherwise the rule expression would be broken), but the updater will try to remove all IP addresses from the list.                                                                                                                                                                                                                                                                                                    | `false`                       |\n| `TZ`               | \u003cp\u003eThe timezone used for logging messages and parsing `UPDATE_CRON`. It can be any timezone accepted by [time.LoadLocation](https://pkg.go.dev/time#LoadLocation), including any IANA Time Zone.\u003c/p\u003e\u003cp\u003e🤖 The pre-built Docker images come with the embedded timezone database via the [time/tzdata](https://pkg.go.dev/time/tzdata) package.\u003c/p\u003e                                                                                                                                                                                                                                                                                                                                                              | `UTC`                         |\n| `UPDATE_CRON`      | \u003cp\u003eThe schedule to re-check IP addresses and update DNS records and WAF lists (if needed). The format is [any cron expression accepted by the `cron` library](https://pkg.go.dev/github.com/robfig/cron/v3#hdr-CRON_Expression_Format) or the special value `@once`. The special value `@once` means the updater will terminate immediately after updating the DNS records or WAF lists, effectively disabling the scheduling feature.\u003c/p\u003e\u003cp\u003e🤖 The update schedule _does not_ take the time to update records into consideration. For example, if the schedule is `@every 5m`, and if the updating itself takes 2 minutes, then the actual interval between adjacent updates is 3 minutes, not 5 minutes.\u003c/p\u003e | `@every 5m` (every 5 minutes) |\n| `UPDATE_ON_START`  | Whether to check IP addresses (and possibly update DNS records and WAF lists) _immediately_ on start, regardless of the update schedule specified by `UPDATE_CRON`. It can be any boolean value accepted by [strconv.ParseBool](https://pkg.go.dev/strconv#ParseBool), such as `true`, `false`, `0` or `1`.                                                                                                                                                                                                                                                                                                                                                                                                    | `true`                        |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e ⏳ Timeouts of various operations\u003c/summary\u003e\n\n| Name                | Meaning                                                                                                                                                                                                                                       | Default Value      |\n| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |\n| `DETECTION_TIMEOUT` | The timeout of each attempt to detect IP address, per IP version (IPv4 and IPv6). It can be any positive time duration accepted by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), such as `1h` or `10m`.                   | `5s` (5 seconds)   |\n| `UPDATE_TIMEOUT`    | The timeout of each attempt to update DNS records, per domain and per record type, or per WAF list. It can be any positive time duration accepted by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), such as `1h` or `10m`. | `30s` (30 seconds) |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 🐣 Parameters of new DNS records and WAF lists (proxy status, TTL, and comments)\u003c/summary\u003e\n\n\u003e 👉 The updater will preserve existing parameters (TTL, proxy statuses, DNS record comments, etc.). Only when it creates new DNS records and new WAF lists, the following settings will apply. To change existing parameters, you can go to your [Cloudflare Dashboard](https://dash.cloudflare.com) and change them directly. If you think you have a use case where the updater should actively overwrite existing parameters in addition to IP addresses, please [let me know](https://github.com/favonia/cloudflare-ddns/issues/new). 🐞🧪 **KNOWN ISSUE: comments of stale WAF list items (not WAF lists themselves) will not be kept** because the Cloudflare API does not provide an easy way to update list items. The comments will be lost when the updater deletes stale list items and create new ones.\n\n| Name                                             | Meaning                                                                                                                                                                                                                                                                                                   | Default Value                              |\n| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |\n| `PROXIED`                                        | \u003cp\u003eWhether new DNS records should be proxied by Cloudflare. It can be any boolean value accepted by [strconv.ParseBool](https://pkg.go.dev/strconv#ParseBool), such as `true`, `false`, `0` or `1`.\u003c/p\u003e\u003cp\u003e🤖 Advanced usage: it can also be a domain-dependent boolean expression as described below.\u003c/p\u003e | `false`                                    |\n| `TTL`                                            | The time-to-live (TTL) (in seconds) of new DNS records.                                                                                                                                                                                                                                                   | `1` (This means “automatic” to Cloudflare) |\n| `RECORD_COMMENT`                                 | The [record comment](https://developers.cloudflare.com/dns/manage-dns-records/reference/record-attributes/) of new DNS records.                                                                                                                                                                           | `\"\"`                                       |\n| 🧪 `WAF_LIST_DESCRIPTION` (since version 1.14.0) | 🧪 The text description of new WAF lists.                                                                                                                                                                                                                                                                 | `\"\"`                                       |\n\n\u003e 🤖 For advanced users: the `PROXIED` can be a boolean expression involving domains! This allows you to enable Cloudflare proxying for some domains but not the others. Here are some example expressions:\n\u003e\n\u003e - `PROXIED=is(example.org)`: proxy only the domain `example.org`\n\u003e - `PROXIED=is(example1.org) || sub(example2.org)`: proxy only the domain `example1.org` and subdomains of `example2.org`\n\u003e - `PROXIED=!is(example.org)`: proxy every managed domain _except for_ `example.org`\n\u003e - `PROXIED=is(example1.org) || is(example2.org) || is(example3.org)`: proxy only the domains `example1.org`, `example2.org`, and `example3.org`\n\u003e\n\u003e A boolean expression must be one of the following forms (all whitespace is ignored):\n\u003e\n\u003e | Syntax                                                                                                                 | Meaning                                                                                                                                             |\n\u003e | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |\n\u003e | Any string accepted by [strconv.ParseBool](https://pkg.go.dev/strconv#ParseBool), such as `true`, `false`, `0`, or `1` | Logical truth or falsehood                                                                                                                          |\n\u003e | `is(d)`                                                                                                                | Matching the domain `d`. Note that `is(*.a)` only matches the wildcard domain `*.a`; use `sub(a)` to match all subdomains of `a` (including `*.a`). |\n\u003e | `sub(d)`                                                                                                               | Matching subdomains of `d`, such as `a.d`, `b.c.d`, and `*.d`. It does not match the domain `d` itself.                                             |\n\u003e | `! e`                                                                                                                  | Logical negation of the boolean expression `e`                                                                                                      |\n\u003e | \u003ccode\u003ee1 \u0026#124;\u0026#124; e2\u003c/code\u003e                                                                                        | Logical disjunction of the boolean expressions `e1` and `e2`                                                                                        |\n\u003e | `e1 \u0026\u0026 e2`                                                                                                             | Logical conjunction of the boolean expressions `e1` and `e2`                                                                                        |\n\u003e\n\u003e One can use parentheses to group expressions, such as `!(is(a) \u0026\u0026 (is(b) || is(c)))`. For convenience, the parser also accepts these short forms:\n\u003e\n\u003e | Short Form             | Equivalent Full Form                                                            |\n\u003e | ---------------------- | ------------------------------------------------------------------------------- |\n\u003e | `is(d1, d2, ..., dn)`  | \u003ccode\u003eis(d1) \u0026#124;\u0026#124; is(d2) \u0026#124;\u0026#124; ... \u0026#124;\u0026#124; is(dn)\u003c/code\u003e    |\n\u003e | `sub(d1, d2, ..., dn)` | \u003ccode\u003esub(d1) \u0026#124;\u0026#124; sub(d2) \u0026#124;\u0026#124; ... \u0026#124;\u0026#124; sub(dn)\u003c/code\u003e |\n\u003e\n\u003e For example, these two settings are equivalent:\n\u003e\n\u003e - `PROXIED=is(example1.org) || is(example2.org) || is(example3.org)`\n\u003e - `PROXIED=is(example1.org,example2.org,example3.org)`\n\u003e \u003c/details\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 👁️ Message logging options\u003c/summary\u003e\n\n| Name    | Meaning                                                                                                                                                                                       | Default Value |\n| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |\n| `EMOJI` | Whether the updater should use emojis in the logging. It can be any boolean value accepted by [strconv.ParseBool](https://pkg.go.dev/strconv#ParseBool), such as `true`, `false`, `0` or `1`. | `true`        |\n| `QUIET` | Whether the updater should reduce the logging. It can be any boolean value accepted by [strconv.ParseBool](https://pkg.go.dev/strconv#ParseBool), such as `true`, `false`, `0` or `1`.        | `false`       |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e 📣 Notification services (Healthchecks, Uptime Kuma, and shoutrrr)\u003c/summary\u003e\n\n\u003e 💡 If your network doesn’t support IPv6, set `IP6_PROVIDER=none` to disable IPv6. This will prevent the updater from reporting failures in detecting IPv6 addresses to monitoring services. Similarly, set `IP4_PROVIDER=none` if your network doesn’t support IPv4.\n\n| Name                                 | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\n| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `HEALTHCHECKS`                       | \u003cp\u003eThe [Healthchecks ping URL](https://healthchecks.io/docs/) to ping when the updater successfully updates IP addresses, such as `https://hc-ping.com/\u003cuuid\u003e` or `https://hc-ping.com/\u003cproject-ping-key\u003e/\u003cname-slug\u003e`\u003c/p\u003e\u003cp\u003e⚠️ The ping schedule should match the update schedule specified by `UPDATE_CRON`.\u003cbr/\u003e🤖 The updater can work with _any_ server following the [same Healthchecks protocol](https://healthchecks.io/docs/http_api/), including self-hosted instances of [Healthchecks](https://github.com/healthchecks/healthchecks). Both UUID and Slug URLs are supported, and the updater works regardless whether the POST-only mode is enabled.\u003c/p\u003e |\n| `UPTIMEKUMA`                         | \u003cp\u003eThe Uptime Kuma’s Push URL to ping when the updater successfully updates IP addresses, such as `https://\u003chost\u003e/push/\u003cid\u003e`. You can directly copy the “Push URL” from the Uptime Kuma configuration page.\u003c/p\u003e\u003cp\u003e⚠️ The “Heartbeat Interval” should match the update schedule specified by `UPDATE_CRON`.\u003c/p\u003e                                                                                                                                                                                                                                                                                                                                                       |\n| 🧪 `SHOUTRRR` (since version 1.12.0) | Newline-separated [shoutrrr URLs](https://containrrr.dev/shoutrrr/latest/services/overview/) to which the updater sends notifications of IP address changes and other events. Each shoutrrr URL represents a notification service; for example, `discord://\u003ctoken\u003e@\u003cid\u003e` means sending messages to Discord.                                                                                                                                                                                                                                                                                                                                                          |\n\n\u003c/details\u003e\n\n### 🔂 Restarting the Container\n\nIf you are using Docker Compose, run `docker-compose up --detach` to reload settings.\n\n## 🚵 Migration Guides\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e I am migrating from oznu/cloudflare-ddns (now archived)\u003c/summary\u003e\n\n⚠️ [oznu/cloudflare-ddns](https://github.com/oznu/docker-cloudflare-ddns) relies on the insecure DNS protocol to obtain public IP addresses; a malicious hacker could more easily forge DNS responses and trick it into updating your domain with any IP address. In comparison, we use only verified responses from Cloudflare, which makes the attack much more difficult. See the [design document](docs/DESIGN.markdown) for more information on security.\n\n| Old Parameter                          |     | Note                                                                                                                                                                                                                                          |\n| -------------------------------------- | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `API_KEY=key`                          | ✔️  | Use `CLOUDFLARE_API_TOKEN=key`                                                                                                                                                                                                                |\n| `API_KEY_FILE=file`                    | ✔️  | Use `CLOUDFLARE_API_TOKEN_FILE=file`                                                                                                                                                                                                          |\n| `ZONE=example.org` and `SUBDOMAIN=sub` | ✔️  | Use `DOMAINS=sub.example.org` directly                                                                                                                                                                                                        |\n| `PROXIED=true`                         | ✔️  | Same (`PROXIED=true`)                                                                                                                                                                                                                         |\n| `RRTYPE=A`                             | ✔️  | Both IPv4 and IPv6 are enabled by default; use `IP6_PROVIDER=none` to disable IPv6                                                                                                                                                            |\n| `RRTYPE=AAAA`                          | ✔️  | Both IPv4 and IPv6 are enabled by default; use `IP4_PROVIDER=none` to disable IPv4                                                                                                                                                            |\n| `DELETE_ON_STOP=true`                  | ✔️  | Same (`DELETE_ON_STOP=true`)                                                                                                                                                                                                                  |\n| `INTERFACE=name`                       | ✔️  | To automatically select the local address, use `IP4/6_PROVIDER=local`. 🧪 To select the first address of a specific network interface, use `IP4/6_PROVIDER=local.iface:name` (available since version 1.15.0 but not finalized until 1.16.0). |\n| `CUSTOM_LOOKUP_CMD=cmd`                | ❌  | Custom commands are not supported because there are no other programs in the minimal Docker image                                                                                                                                             |\n| `DNS_SERVER=server`                    | ❌  | The updater only supports secure DNS queries using Cloudflare’s DNS over HTTPS (DoH) server. To enable this, set `IP4/6_PROVIDER=cloudflare.doh`.                                                                                             |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eClick to expand:\u003c/em\u003e I am migrating from timothymiller/cloudflare-ddns\u003c/summary\u003e\n\n| Old JSON Key                          |     | Note                                                                                                                                                                                                                                     |\n| ------------------------------------- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `cloudflare.authentication.api_token` | ✔️  | Use `CLOUDFLARE_API_TOKEN=key`                                                                                                                                                                                                           |\n| `cloudflare.authentication.api_key`   | ❌  | Please use the newer, more secure [API tokens](https://dash.cloudflare.com/profile/api-tokens)                                                                                                                                           |\n| `cloudflare.zone_id`                  | ✔️  | Not needed; automatically retrieved from the server                                                                                                                                                                                      |\n| `cloudflare.subdomains[].name`        | ✔️  | Use `DOMAINS` with [**fully qualified domain names (FQDNs)**](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) directly; for example, if your zone is `example.org` and your subdomain is `sub`, use `DOMAINS=sub.example.org` |\n| `cloudflare.subdomains[].proxied`     | ✔️  | Write boolean expressions for `PROXIED` to specify per-domain settings; see above for the detailed documentation for this advanced feature                                                                                               |\n| `load_balancer`                       | ❌  | Not supported yet; please [make a request](https://github.com/favonia/cloudflare-ddns/issues/new) if you want it                                                                                                                         |\n| `a`                                   | ✔️  | Both IPv4 and IPv6 are enabled by default; use `IP4_PROVIDER=none` to disable IPv4                                                                                                                                                       |\n| `aaaa`                                | ✔️  | Both IPv4 and IPv6 are enabled by default; use `IP6_PROVIDER=none` to disable IPv6                                                                                                                                                       |\n| `proxied`                             | ✔️  | Use `PROXIED=true` or `PROXIED=false`                                                                                                                                                                                                    |\n| `purgeUnknownRecords`                 | ❌  | The updater never deletes unmanaged DNS records                                                                                                                                                                                          |\n\n\u003e 📜 Some historical notes: This updater was originally written as a Go clone of the Python program [timothymiller/cloudflare-ddns](https://github.com/timothymiller/cloudflare-ddns) because the Python program always purged unmanaged DNS records back then and it was not configurable via environment variables. There were feature requests to address these issues but the author [timothymiller](https://github.com/timothymiller/) seemed to ignore them; I thus made my Go clone after unsuccessful communications. Understandably, [timothymiller](https://github.com/timothymiller/) did not seem happy with my cloning and my other critical comments towards other aspects of the Python updater. Eventually, an option `purgeUnknownRecords` was added to the Python program to disable the unwanted purging, and it became configurable via environment variables, but my Go clone already went on its way. I believe my Go clone is now a much better choice, but my opinions are biased and you should check the technical details by yourself. 😉\n\n\u003c/details\u003e\n\n## 💖 Feedback\n\nQuestions, suggestions, feature requests, and contributions are all welcome! Feel free to [open a GitHub issue](https://github.com/favonia/cloudflare-ddns/issues/new).\n\n## 📜 License\n\nThe code is licensed under [Apache 2.0 with LLVM exceptions](./LICENSE). (The LLVM exceptions provide better compatibility with GPL 2.0 and other license exceptions.)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffavonia%2Fcloudflare-ddns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffavonia%2Fcloudflare-ddns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffavonia%2Fcloudflare-ddns/lists"}