{"id":13548771,"url":"https://github.com/qdm12/ddns-updater","last_synced_at":"2026-05-01T04:02:02.638Z","repository":{"id":37746106,"uuid":"152297176","full_name":"qdm12/ddns-updater","owner":"qdm12","description":"Container to update DNS records periodically with WebUI for many DNS providers","archived":false,"fork":false,"pushed_at":"2026-04-21T15:37:36.000Z","size":7679,"stargazers_count":2979,"open_issues_count":223,"forks_count":265,"subscribers_count":19,"default_branch":"master","last_synced_at":"2026-04-21T17:33:51.675Z","etag":null,"topics":["cloudflare","ddns","ddns-client","ddns-manager","ddns-updater","ddnss","dnspod","docker","dreamhost","duckdns","dynamic-dns","dyndns","godaddy","golang","gotify","infomaniak","namecheap","noip","webui"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/qmcgaw/ddns-updater/","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/qdm12.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["qdm12"]}},"created_at":"2018-10-09T18:00:05.000Z","updated_at":"2026-04-21T15:37:42.000Z","dependencies_parsed_at":"2024-01-31T16:06:29.749Z","dependency_job_id":"10ff977d-252e-47dd-9c0d-1c4b9705b7b1","html_url":"https://github.com/qdm12/ddns-updater","commit_stats":{"total_commits":845,"total_committers":56,"mean_commits":"15.089285714285714","dds":"0.15384615384615385","last_synced_commit":"71191fa70749bc760ddb43dbb0a2a6c4be3dc96a"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/qdm12/ddns-updater","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fddns-updater","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fddns-updater/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fddns-updater/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fddns-updater/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qdm12","download_url":"https://codeload.github.com/qdm12/ddns-updater/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fddns-updater/sbom","scorecard":{"id":753028,"data":{"date":"2025-08-11","repo":{"name":"github.com/qdm12/ddns-updater","commit":"20ac11075322b1f0bee1f9b91913406777721ae8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Code-Review","score":1,"reason":"Found 4/21 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/build-skip.yml:32","Info: jobLevel 'actions' permission set to 'read': .github/workflows/build.yml:35","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build.yml:36","Info: jobLevel 'actions' permission set to 'read': .github/workflows/build.yml:71","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build.yml:72","Info: jobLevel 'actions' permission set to 'read': .github/workflows/build.yml:95","Info: jobLevel 'actions' permission set to 'read': .github/workflows/markdown-skip.yml:18","Info: jobLevel 'contents' permission set to 'read': .github/workflows/markdown.yml:19","Info: jobLevel 'actions' permission set to 'read': .github/workflows/markdown.yml:18","Warn: no topLevel permission defined: .github/workflows/build-skip.yml:1","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/labels.yml:1","Warn: no topLevel permission defined: .github/workflows/markdown-skip.yml:1","Warn: no topLevel permission defined: .github/workflows/markdown.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: GoBuiltInFuzzer integration found: pkg/ipextract/ipextract_test.go:80"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v2.9.0 not signed: https://api.github.com/repos/qdm12/ddns-updater/releases/178825698","Warn: release artifact v2.8.2 not signed: https://api.github.com/repos/qdm12/ddns-updater/releases/186758501","Warn: release artifact v2.8.1 not signed: https://api.github.com/repos/qdm12/ddns-updater/releases/176042723","Warn: release artifact v2.8.0 not signed: https://api.github.com/repos/qdm12/ddns-updater/releases/175543761","Warn: release artifact v2.7.1 not signed: https://api.github.com/repos/qdm12/ddns-updater/releases/175391003","Warn: release artifact v2.9.0 does not have provenance: https://api.github.com/repos/qdm12/ddns-updater/releases/178825698","Warn: release artifact v2.8.2 does not have provenance: https://api.github.com/repos/qdm12/ddns-updater/releases/186758501","Warn: release artifact v2.8.1 does not have provenance: https://api.github.com/repos/qdm12/ddns-updater/releases/176042723","Warn: release artifact v2.8.0 does not have provenance: https://api.github.com/repos/qdm12/ddns-updater/releases/175543761","Warn: release artifact v2.7.1 does not have provenance: https://api.github.com/repos/qdm12/ddns-updater/releases/175391003"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'","Warn: branch protection not enabled for branch 'v2.8'","Warn: branch protection not enabled for branch 'v2.7'","Warn: branch protection not enabled for branch 'v2.6'","Warn: branch protection not enabled for branch 'branch-v2.4'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/build.yml:85"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":9,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 12 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:75: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:76: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:79: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:82: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:83: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:100: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:108: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:125: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:126: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:128: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:133: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:144: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:156: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:161: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/labels.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/labels.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/labels.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/labels.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/markdown.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/markdown.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/markdown.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/markdown.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/markdown.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/markdown.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/markdown.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/markdown.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/markdown.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/qdm12/ddns-updater/markdown.yml/master?enable=pin","Warn: containerImage not pinned by hash: .devcontainer/Dockerfile:1: pin your Docker image by updating qmcgaw/godevcontainer:v0.20-alpine to qmcgaw/godevcontainer:v0.20-alpine@sha256:1ee5bcfdb33b78fc40d9f9aff4a35fb74ecc7811a2e4688d0a046fc14f218e6c","Warn: containerImage not pinned by hash: Dockerfile:8","Warn: containerImage not pinned by hash: Dockerfile:9","Warn: containerImage not pinned by hash: Dockerfile:10","Warn: containerImage not pinned by hash: Dockerfile:12","Warn: containerImage not pinned by hash: Dockerfile:27","Warn: containerImage not pinned by hash: Dockerfile:36","Warn: containerImage not pinned by hash: Dockerfile:40","Warn: containerImage not pinned by hash: Dockerfile:52","Info:   0 out of  10 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of  13 third-party GitHubAction dependencies pinned","Info:   0 out of   9 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":6,"reason":"4 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2025-3770 / GHSA-vrw8-fxc6-2r93","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw","Warn: Project is vulnerable to: GO-2025-3488 / GHSA-6v2p-p543-phr9"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T20:51:10.832Z","repository_id":37746106,"created_at":"2025-08-22T20:51:10.832Z","updated_at":"2025-08-22T20:51:10.832Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32484353,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-manager","ddns-updater","ddnss","dnspod","docker","dreamhost","duckdns","dynamic-dns","dyndns","godaddy","golang","gotify","infomaniak","namecheap","noip","webui"],"created_at":"2024-08-01T12:01:14.266Z","updated_at":"2026-05-01T04:02:02.631Z","avatar_url":"https://github.com/qdm12.png","language":"Go","funding_links":["https://github.com/sponsors/qdm12","https://www.paypal.me/qmcgaw"],"categories":["HarmonyOS","Go","golang"],"sub_categories":["Windows Manager"],"readme":"# Lightweight universal DDNS Updater program\n\nProgram to keep DNS A and/or AAAA records updated for multiple DNS providers\n\n\u003cimg height=\"200\" alt=\"DDNS Updater logo\" src=\"https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/ddnsgopher.svg\"\u003e\n\n[![Build status](https://github.com/qdm12/ddns-updater/actions/workflows/build.yml/badge.svg)](https://github.com/qdm12/ddns-updater/actions/workflows/build.yml)\n\n![Last release](https://img.shields.io/github/release/qdm12/ddns-updater?label=Last%20release)\n![Last Docker tag](https://img.shields.io/github/v/release/qdm12/ddns-updater?sort=semver\u0026label=Last%20Docker%20tag)\n[![Last release size](https://img.shields.io/docker/image-size/qmcgaw/ddns-updater?sort=semver\u0026label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/ddns-updater/tags?page=1\u0026ordering=last_updated)\n![GitHub last release date](https://img.shields.io/github/release-date/qdm12/ddns-updater?label=Last%20release%20date)\n![Commits since release](https://img.shields.io/github/commits-since/qdm12/ddns-updater/latest?sort=semver)\n\n[![Latest size](https://img.shields.io/docker/image-size/qmcgaw/ddns-updater/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/ddns-updater/tags)\n\n[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/commits/main)\n[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/graphs/contributors)\n[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/pulls?q=is%3Apr+is%3Aclosed)\n[![GitHub issues](https://img.shields.io/github/issues/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues)\n[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues?q=is%3Aissue+is%3Aclosed)\n\n![Code size](https://img.shields.io/github/languages/code-size/qdm12/ddns-updater)\n![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/ddns-updater)\n![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/ddns-updater)\n\n[![MIT](https://img.shields.io/github/license/qdm12/ddns-updater)](LICENSE)\n![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=ddns-updater.readme)\n\n## Versioned documentation\n\nThis readme and the [docs/](docs/) directory are **versioned** to match the program version:\n\n| Version | Readme link | Docs link |\n| --- | --- | --- |\n| Latest | [README](https://github.com/qdm12/ddns-updater/blob/master/README.md) | [docs/](https://github.com/qdm12/ddns-updater/tree/master/docs) |\n| `v2.8` | [README](https://github.com/qdm12/ddns-updater/blob/v2.8.0/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.8.0/docs) |\n| `v2.7` | [README](https://github.com/qdm12/ddns-updater/blob/v2.7.1/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.7.1/docs) |\n| `v2.6` | [README](https://github.com/qdm12/ddns-updater/blob/v2.6.1/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.6.1/docs) |\n| `v2.5` | [README](https://github.com/qdm12/ddns-updater/blob/v2.5.0/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.5.0/docs) |\n\n## Features\n\n- Available as a Docker image [`ghcr.io/qdm12/ddns-updater`]((https://github.com/qdm12/ddns-updater/pkgs/container/ddns-updater)) and [`qmcgaw/ddns-updater`](https://hub.docker.com/r/qmcgaw/ddns-updater)\n- Available as [zero-dependency binaries for Linux, Windows and MacOS](https://github.com/qdm12/ddns-updater/releases)\n- 🆕 Available in the AUR as [`ddns-updater`](https://aur.archlinux.org/packages/ddns-updater) - see [#808](https://github.com/qdm12/ddns-updater/discussions/808)\n- Updates periodically A records for different DNS providers:\n  - Aliyun\n  - AllInkl\n  - ChangeIP\n  - Cloudflare\n  - DD24\n  - DDNSS.de\n  - deSEC\n  - DigitalOcean\n  - Domeneshop\n  - DonDominio\n  - DNSOMatic\n  - DNSPod\n  - Dreamhost\n  - DuckDNS\n  - DynDNS\n  - Dynu\n  - DynV6\n  - EasyDNS\n  - FreeDNS\n  - Gandi\n  - GCP\n  - GoDaddy\n  - GoIP.de\n  - He.net\n  - Hetzner (legacy API)\n  - Hetzner Cloud\n  - Infomaniak\n  - INWX\n    - Ionos\n    - ipv64\n  - Linode\n  - Loopia\n  - LuaDNS\n  - Myaddr\n  - Name.com\n  - Namecheap\n  - NameSilo\n  - Netcup\n  - NoIP\n  - Now-DNS\n  - Njalla\n  - OpenDNS\n  - OVH\n  - Porkbun\n  - Route53\n  - Scaleway\n  - Selfhost.de\n  - Servercow.de\n  - Spaceship\n  - Spdyn\n  - Strato.de\n  - Variomedia.de\n  - Vultr\n  - Zoneedit\n  - **Want more?** [Create an issue for it](https://github.com/qdm12/ddns-updater/issues/new/choose)!\n- Web user interface (Desktop)\n\n    ![Web UI](readme/webui-desktop.gif)\n\n- Web user interface (Mobile)\n\n    ![Mobile Web UI](readme/webui-mobile.png)\n\n- Send notifications with [**Shoutrrr**](https://containrrr.dev/shoutrrr/v0.8/services/overview/) using `SHOUTRRR_ADDRESSES`\n- Container (Docker/K8s) specific features:\n  - Lightweight 12MB Docker image based on the Scratch Docker image\n  - Docker healthcheck verifying the DNS resolution of your domains\n  - Images compatible with `amd64`, `386`, `arm64`, `armv7`, `armv6`, `s390x`, `ppc64le`, `riscv64` CPU architectures\n- Persistence with a JSON file *updates.json* to store old IP addresses with change times for each record\n\n## Setup\n\n### Binary programs\n\n1. Download the pre-built program for your platform from the assets of a release in the [releases page](https://github.com/qdm12/ddns-updater/releases). You can alternatively download, build and install the latest version of the program by installing [Go](https://golang.org/doc/install) and then run `go install github.com/qdm12/ddns-updater/cmd/ddns-updater@latest`.\n1. For Linux and MacOS, make the program executable with `chmod +x ddns-updater`.\n1. In the directory where the program is saved, create a directory `data`.\n1. Write a JSON configuration in `data/config.json`, for example:\n\n    ```json\n    {\n        \"settings\": [\n            {\n                \"provider\": \"namecheap\",\n                \"domain\": \"sub.example.com\",\n                \"password\": \"e5322165c1d74692bfa6d807100c0310\"\n            }\n        ]\n    }\n    ```\n\n    You can find more information in the [configuration section](#configuration) to customize it.\n1. Run the program with `./ddns-updater` (`./ddns-updater.exe` on Windows) or by double-clicking on it.\n1. The following is **optional**.\n    - You can customize the program behavior using either [environment variables](#environment-variables) or flags. For flags, there is a flag corresponding to each environment variable, where it's all lowercase and underscores are replaced with dashes. For example the environment variable `LOG_LEVEL` translates into `--log-level`.\n\n### Container\n\n[➡️ Qnap guide by @Araminta](https://github.com/qdm12/ddns-updater/issues/708)\n\n1. Create a directory, for example, *data* which is:\n    - owned by user id `1000`, which is the built-in user ID of the ddns-updater container\n    - has user read+write+execute permissions\n\n    ```sh\n    mkdir data\n    chown 1000 data\n    chmod u+r+w+x data\n    ```\n\n    If you want to use another user ID, [build the image yourself](#build-the-image) with `--build-arg UID=\u003cyour-uid\u003e`. You could also just run the container as root with `--user=\"0\"` but this is not advised security wise.\n\n1. Similarly, create a *data/config.json* file which is:\n    - owned by user id `1000`\n    - has user read permissions\n\n    ```sh\n    touch data/config.json\n    chmod u+r data/config.json\n    ```\n\n1. Edit *data/config.json*, for example:\n\n    ```json\n    {\n        \"settings\": [\n            {\n                \"provider\": \"namecheap\",\n                \"domain\": \"sub.example.com\",\n                \"password\": \"e5322165c1d74692bfa6d807100c0310\"\n            }\n        ]\n    }\n    ```\n\n    You can find more information in the [configuration section](#configuration) to customize it.\n\n1. Run the container with\n\n    ```sh\n    docker run -d -p 8000:8000/tcp -v \"$(pwd)\"/data:/updater/data ghcr.io/qdm12/ddns-updater\n    ```\n\n1. The following is **optional**.\n    - You can customize the program behavior using [environment variables](#environment-variables)\n    - You can use [docker-compose.yml](docker-compose.yml) with `docker-compose up -d`\n    - **Kubernetes**: check out the [k8s directory](k8s) for an installation guide and examples.\n    - Other [Docker image tags are available](https://github.com/qdm12/ddns-updater/pkgs/container/ddns-updater)\n    - You can update the image with `docker pull ghcr.io/qdm12/ddns-updater`\n    - You can set your JSON configuration as a single environment variable line (i.e. `{\"settings\": [{\"provider\": \"namecheap\", ...}]}`), which takes precedence over config.json. Note however that if you don't bind mount the `/updater/data` directory, there won't be a persistent database file `/updater/updates.json` but it will still work.\n\n## Configuration\n\nStart by having the following content in *config.json*, or in your `CONFIG` environment variable:\n\n```json\n{\n    \"settings\": [\n        {\n            \"provider\": \"\",\n        },\n        {\n            \"provider\": \"\",\n        }\n    ]\n}\n```\n\nFor each setting, you need to fill in parameters.\nCheck the documentation for your DNS provider:\n\n- [Aliyun](docs/aliyun.md)\n- [Allinkl](docs/allinkl.md)\n- [ChangeIP](docs/changeip.md)\n- [Cloudflare](docs/cloudflare.md)\n- [DD24](docs/dd24.md)\n- [DDNSS.de](docs/ddnss.de.md)\n- [deSEC](docs/desec.md)\n- [DigitalOcean](docs/digitalocean.md)\n- [Domeneshop](docs/domeneshop.md)\n- [DonDominio](docs/dondominio.md)\n- [DNSOMatic](docs/dnsomatic.md)\n- [DNSPod](docs/dnspod.md)\n- [Dreamhost](docs/dreamhost.md)\n- [DuckDNS](docs/duckdns.md)\n- [DynDNS](docs/dyndns.md)\n- [Dynu](docs/dynu.md)\n- [DynV6](docs/dynv6.md)\n- [EasyDNS](docs/easydns.md)\n- [FreeDNS](docs/freedns.md)\n- [Gandi](docs/gandi.md)\n- [GCP](docs/gcp.md)\n- [GoDaddy](docs/godaddy.md)\n- [GoIP.de](docs/goip.md)\n- [He.net](docs/he.net.md)\n- [Hetzner](docs/hetzner.md)\n- [HetznerCloud](docs/hetznercloud.md)\n- [Infomaniak](docs/infomaniak.md)\n- [INWX](docs/inwx.md)\n- [Ionos](docs/ionos.md)\n- [IPv64](docs/ipv64.md)\n- [Linode](docs/linode.md)\n- [Loopia](docs/loopia.md)\n- [LuaDNS](docs/luadns.md)\n- [Myaddr](docs/myaddr.md)\n- [Name.com](docs/name.com.md)\n- [Namecheap](docs/namecheap.md)\n- [NameSilo](docs/namesilo.md)\n- [Netcup](docs/netcup.md)\n- [NoIP](docs/noip.md)\n- [Now-DNS](docs/nowdns.md)\n- [Njalla](docs/njalla.md)\n- [OpenDNS](docs/opendns.md)\n- [OVH](docs/ovh.md)\n- [Porkbun](docs/porkbun.md)\n- [Route53](docs/route53.md)\n- [Scaleway](docs/scaleway.md)\n- [Selfhost.de](docs/selfhost.de.md)\n- [Servercow.de](docs/servercow.md)\n- [Spaceship](docs/spaceship.md)\n- [Spdyn](docs/spdyn.md)\n- [Strato.de](docs/strato.md)\n- [Variomedia.de](docs/variomedia.md)\n- [Vultr](docs/vultr.md)\n- [Zoneedit](docs/zoneedit.md)\n- [Custom](docs/custom.md)\n\nNote that:\n\n- you can specify multiple owners/hosts for the same domain using a comma separated list. For example with `\"domain\": \"example.com,sub.example.com,sub2.example.com\",`.\n⚠️ this is a bit different for DuckDNS and GoIP, see their respective documentation.\n\n### Environment variables\n\n🆕 There are now flags equivalent for each variable below, for example `--log-level`.\n\n| Environment variable | Default | Description |\n| --- | --- | --- |\n| `CONFIG` | | One line JSON object containing the entire config (takes precedence over config.json file) if specified |\n| `PERIOD` | `5m` | Default period of IP address check, following [this format](https://golang.org/pkg/time/#ParseDuration) |\n| `PUBLICIP_FETCHERS` | `all` | Comma separated fetcher types to obtain the public IP address from `http` and `dns` |\n| `PUBLICIP_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (ipv4 or ipv6). See the [Public IP section](#public-ip) |\n| `PUBLICIPV4_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv4 address only. See the [Public IP section](#public-ip) |\n| `PUBLICIPV6_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv6 address only. See the [Public IP section](#public-ip) |\n| `PUBLICIP_DNS_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (IPv4 and/or IPv6). See the [Public IP section](#public-ip) |\n| `PUBLICIP_DNS_TIMEOUT` | `3s` | Public IP DNS query timeout |\n| `UPDATE_COOLDOWN_PERIOD` | `5m` | Duration to cooldown between updates for each record. This is useful to avoid being rate limited or banned. |\n| `HTTP_TIMEOUT` | `10s` | Timeout for all HTTP requests |\n| `SERVER_ENABLED` | `yes` | Enable the web server and web UI |\n| `LISTENING_ADDRESS` | `:8000` | Internal TCP listening port for the web UI |\n| `ROOT_URL` | `/` | URL path to append to all paths to the webUI (i.e. `/ddns` for accessing `https://example.com/ddns` through a proxy) |\n| `HEALTH_SERVER_ADDRESS` | `127.0.0.1:9999` | Health server listening address |\n| `HEALTH_HEALTHCHECKSIO_BASE_URL` | `https://hc-ping.com` | Base URL for the [healthchecks.io](https://healthchecks.io) server |\n| `HEALTH_HEALTHCHECKSIO_UUID` | | UUID to idenfity with the [healthchecks.io](https://healthchecks.io) server |\n| `DATADIR` | `/updater/data` | Directory to read and write data files from internally |\n| `CONFIG_FILEPATH` | `/updater/data/config.json` | Path to the JSON configuration file |\n| `BACKUP_PERIOD` | `0` | Set to a period (i.e. `72h15m`) to enable zip backups of data/config.json and data/updates.json in a zip file |\n| `BACKUP_DIRECTORY` | `/updater/data` | Directory to write backup zip files to if `BACKUP_PERIOD` is not `0`. |\n| `RESOLVER_ADDRESS` | Your network DNS | A plaintext DNS address to use to resolve your domain names defined in your settings only. For example it can be `1.1.1.1:53`. This is useful for split dns, see [#389](https://github.com/qdm12/ddns-updater/issues/389) |\n| `LOG_LEVEL` | `info` | Level of logging, `debug`, `info`, `warning` or `error` |\n| `LOG_CALLER` | `hidden` | Show caller per log line, `hidden` or `short` |\n| `SHOUTRRR_ADDRESSES` | | (optional) Comma separated list of [Shoutrrr addresses](https://containrrr.dev/shoutrrr/v0.8/services/overview/) (notification services) |\n| `SHOUTRRR_DEFAULT_TITLE` | `DDNS Updater` | Default title for Shoutrrr notifications |\n| `TZ` | | Timezone to have accurate times, i.e. `America/Montreal` |\n| `UMASK` | System current umask | Umask to set for the program in octal, i.e. `0022` |\n\n#### Public IP\n\nBy default, all public IP fetching types are used and cycled (over DNS and over HTTPs).\n\nOn top of that, for each fetching method, all echo services available are cycled on each request.\n\nThis allows you not to be blocked for making too many requests.\n\nYou can otherwise customize it with the following:\n\n- `PUBLICIP_HTTP_PROVIDERS` gets your public IPv4 or IPv6 address. It can be one or more of the following:\n  - `ipify` using [https://api64.ipify.org](https://api64.ipify.org)\n  - `ifconfig` using [https://ifconfig.io/ip](https://ifconfig.io/ip)\n  - `ipinfo` using [https://ipinfo.io/ip](https://ipinfo.io/ip)\n  - `spdyn` using [https://checkip.spdyn.de](https://checkip.spdyn.de/)\n  - `ipleak` using [https://ipleak.net/json](https://ipleak.net/json)\n  - `icanhazip` using [https://icanhazip.com](https://icanhazip.com)\n  - `ident` using [https://ident.me](https://ident.me)\n  - `nnev` using [https://ip.nnev.de](https://ip.nnev.de)\n  - `wtfismyip` using [https://wtfismyip.com/text](https://wtfismyip.com/text)\n  - `seeip` using [https://api.seeip.org](https://api.seeip.org)\n  - `changeip` using [https://ip.changeip.com](https://ip.changeip.com)\n  - You can also specify an HTTPS URL with prefix `url:` for example `url:https://ipinfo.io/ip`\n- `PUBLICIPV4_HTTP_PROVIDERS` gets your public IPv4 address only. It can be one or more of the following:\n  - `ipleak` using [https://ipv4.ipleak.net/json](https://ipv4.ipleak.net/json)\n  - `ipify` using [https://api.ipify.org](https://api.ipify.org)\n  - `icanhazip` using [https://ipv4.icanhazip.com](https://ipv4.icanhazip.com)\n  - `ident` using [https://v4.ident.me](https://v4.ident.me)\n  - `nnev` using [https://ip4.nnev.de](https://ip4.nnev.de)\n  - `wtfismyip` using [https://ipv4.wtfismyip.com/text](https://ipv4.wtfismyip.com/text)\n  - `seeip` using [https://ipv4.seeip.org](https://ipv4.seeip.org)\n  - You can also specify an HTTPS URL with prefix `url:` for example `url:https://ipinfo.io/ip`\n- `PUBLICIPV6_HTTP_PROVIDERS` gets your public IPv6 address only. It can be one or more of the following:\n  - `ipleak` using [https://ipv6.ipleak.net/json](https://ipv6.ipleak.net/json)\n  - `ipify` using [https://api6.ipify.org](https://api6.ipify.org)\n  - `icanhazip` using [https://ipv6.icanhazip.com](https://ipv6.icanhazip.com)\n  - `ident` using [https://v6.ident.me](https://v6.ident.me)\n  - `nnev` using [https://ip6.nnev.de](https://ip6.nnev.de)\n  - `wtfismyip` using [https://ipv6.wtfismyip.com/text](https://ipv6.wtfismyip.com/text)\n  - `seeip` using [https://ipv6.seeip.org](https://ipv6.seeip.org)\n  - You can also specify an HTTPS URL with prefix `url:` for example `url:https://ipinfo.io/ip`\n- `PUBLICIP_DNS_PROVIDERS` gets your public IPv4 address only or IPv6 address only or one of them (see [#136](https://github.com/qdm12/ddns-updater/issues/136)). It can be one or more of the following:\n  - `cloudflare`\n  - `opendns`\n\n### Host firewall\n\nIf you have a host firewall in place, this container needs the following ports:\n\n- TCP 443 outbound for outbound HTTPS\n- UDP 53 outbound for outbound DNS resolution\n- TCP 8000 inbound (or other) for the WebUI\n\n## Architecture\n\nAt program start and every period (5 minutes by default):\n\n1. Fetch your public IP address\n1. For each record:\n    1. DNS resolve it to obtain its current IP address(es)\n        - If the resolution fails, update the record with your public IP address by calling the DNS provider API and finish\n    1. Check if your public IP address is within the resolved IP addresses\n        - Yes: skip the update\n        - No: update the record with your public IP address by calling the DNS provider API\n\n💡 We do DNS resolution every period so it detects a change made to the record manually, for example on the DNS provider web UI\n💡 As DNS resolutions are essentially free and without rate limiting, these are great to avoid getting banned for too many requests.\n\n### Special case: Cloudflare\n\nFor Cloudflare records with the `proxied` option, the following is done.\n\nAt program start and every period (5 minutes by default), for each record:\n\n1. Fetch your public IP address\n1. For each record:\n    1. Check the last IP address (persisted in `updates.json`) for that record\n        - If it doesn't exist, update the record with your public IP address by calling the DNS provider API and finish\n    1. Check if your public IP address matches the last IP address you updated the record with\n        - Yes: skip the update\n        - No: update the record with your public IP address by calling the DNS provider API\n\nThis is the only way as doing a DNS resolution on the record will give the IP address of a Cloudflare server instead of your server.\n\n⚠️ This has the disadvantage that if the record is changed manually, the program will not detect it.\nWe could do an API call to get the record IP address every period, but that would get you banned especially with a low period duration.\n\n## Testing\n\n- The automated healthcheck verifies all your records are up to date [using DNS lookups](internal/health/check.go#L42)\n- You can also manually check, by:\n    1. Going to your DNS management webpage\n    1. Setting your record to `127.0.0.1`\n    1. Run the container\n    1. Refresh the DNS management webpage and verify the update happened\n\n## Build the image\n\nYou can build the image yourself with:\n\n```sh\ndocker build -t ghcr.io/qdm12/ddns-updater https://github.com/qdm12/ddns-updater.git\n```\n\nYou can use optional build arguments with `--build-arg KEY=VALUE` from the table below:\n\n| Build argument | Default | Description |\n| --- | --- | --- |\n| `UID` | `1000` | User ID running the container |\n| `GID` | `1000` | User group ID running the container |\n| `VERSION` | `unknown` | Version of the program and Docker image |\n| `CREATED` | `an unknown date` | Build date of the program and Docker image |\n| `COMMIT` | `unknown` | Commit hash of the program and Docker image |\n\n## Development and contributing\n\n- [Contribute with code](.github/CONTRIBUTING.md)\n- [Github workflows to know what's building](https://github.com/qdm12/ddns-updater/actions)\n- [List of issues and feature requests](https://github.com/qdm12/ddns-updater/issues)\n\n## License\n\nThis repository is under an [MIT license](LICENSE)\n\n## Used in external projects\n\n- [Starttoaster/docker-traefik](https://github.com/Starttoaster/docker-traefik#home-networks-extra-credit-dynamic-dns)\n\n## Support\n\nSponsor me on [Github](https://github.com/sponsors/qdm12) or donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)\n\nMany thanks to J. Famiglietti for supporting me financially 🥇👍\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqdm12%2Fddns-updater","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqdm12%2Fddns-updater","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqdm12%2Fddns-updater/lists"}