qdm12/ddns-updater
Container to update DNS records periodically with WebUI for many DNS providers
✨ Features
- Support for `scaleway.com` (#899)
- Support for `ipv64.net` (#937)
- Support for `vercel.com` (#1065)
- Support for `hetznercloud` (#1046, #1129) - credits to @legend813 @likt0r @qdm12
- Support for `spaceship.com` (#907)
🐛 Fixes
- DNS resolution: prevent program hanging if all DNS resolution fail consecutively (#1099)
- Route53: don't replace wildcard character `*` with `any` when sending update request (#1036)
- Ionos: use filepath/path for joining URL paths to avoid errors in Windows (#984)
- DonDominio: change key JSON parameter from `apikey` to `password` (#969)
- deSEC: only update specified ip version (#961)
- Njalla: add trailing slash to URL path (#978)
- Update logic: reuse HTTP client transport instead of cloning it (#1095)
📝 Documentation
- Log: log out `<none>` when no IP is given to `ipsToString`
- Porkbun: align name as `Porkbun` in the UI (#1066)
- All providers: add detail on `ipv6_suffix` about temporary IPv6 addresses
- Readme:
- add missing link to Hetzner doc (#977)
- fix url to `#136` to work outside Github (#1075)
- fix invalid URLs
- remove no longer working dockeri.co badge
- + 7 more
📦 Code quality
- Config tests: take into account the OS specific file separator (#985)
📦 Dependencies
- Bump github.com/breml/rootcerts from 0.2.19 to 0.3.5 (#1080, #1118, #1126)
- Bump github.com/go-chi/chi/v5 from 5.2.0 to 5.2.3 (#933, #1078, #1081)
- Bump github.com/miekg/dns from 1.1.62 to 1.1.72 (#962, #1117)
- Bump github.com/stretchr/testify from 1.10.0 to 1.11.1 (#1082)
- Bump golang.org/x/mod from 0.31.0 to 0.32.0 (#1079)
- Bump golang.org/x/net from 0.33.0 to 0.53.0 (#968, #1116)
- Bump golang.org/x/oauth2 from 0.24.0 to 0.36.0 (#952, #1115)
- Upgrade transitive dependencies (#1071)
📦 CI
- Use Go version from go.mod in Github actions
- Add 429 and 403 status code as valid code for markdown link check
- Bump actions/checkout from v4 to v6
- Bump actions/setup-go from 5 to 6 (#1084)
- Bump crazy-max/ghaction-github-labeler from 5 to 6 (#1103)
- Bump DavidAnson/markdownlint-cli2-action from 17 to 23 (#1083, #1120)
- Bump docker/build-push-action from 6 to 7 (#1119)
- Bump docker/login-action from 3 to 4 (#1122)
- + 8 more
📦 Development setup
- Prefer using ghcr.io images
- Linter:
- upgraded from v2.4.0 to v2.11.4
- add linters godoclint, iotamixing, modernize
- Build:
- bump Alpine from 3.20 to 3.23
- bump Go version from 1.23 to 1.26
- upgrade golang/mock to uber-go/mock v0.6.0
- + 6 more
📦 Contributors
- @amroessam @eshirvana @foegra @floriantraber @francesco086 @jccint @jorge-carrasco @legend813 @likt0r @michaelkoelle @MrNuggelz @Mynacol @similicious @simonmarty @teranex @TheZoker @williambout
🐛 Fixes
- fix program hanging if all dns resolution fail consecutively (#1099)
- route53: don't replace wildcard character * with any when sending update request (#1036)
- ionos: use filepath/path for joining URL paths to avoid errors in Windows (#984)
- dondominio: change key JSON paramater from apikey to password (#969)
- desec: only update specified ip version (#961)
- njalla: add trailing slash to URL path (#978)
✨ Features
- Support for `domene.shop` (#810)
- Support for `loopia.se` (#842)
- Support for `vultr.com` (#829)
- Support `myaddr.tools` (#885)
- Support for `namesilo.com` (#866)
- Custom provider: allow empty ipv4 and ipv6 keys (see issue #875)
- Possibility to run health server outside Docker and to disable it in a container
- disable health server if the health listening address:port is empty
- + 2 more
🐛 Fixes
- Common update mechanism:
- do not update if public IP address is within multiple IP addresses resolved
- IPv6 hostname resolution fixed (it was previously only resolving IPv4)
- Porkbun:
- update API endpoint (#837)
- delete default parked DNS entries if needed (#774)
- delete default `@` ALIAS entry when creating a record for the root domain or the wildcard domain
- delete default `*` CNAME entry when creating a record for the wildcard domain
- + 16 more
📝 Documentation
- Readme: specify duckdns and goip are handled differently for multiple domains
- GoIP: fix domain field documentation and add domain examples
- eTLD must be goip.de or goip.it, not eTLD+1
- remove old `domain` optional parameter documentation
- DuckDNS: fix domain documentation on eTLD being duckdns.org and add domain examples
📦 Code quality
- do not re-check the owner is not empty within provider specific code
- format code using `gofumpt`
- Upgrade linter from v1.56.2 to v1.61.0
- add new linters fatcontext, intrange and mirror
- `internal/update`: use newer `LookupNetIP` resolver function to use `netip.Addr` directly
📦 Dependencies
- Upgrade Go from 1.22 to 1.23
- Bump github.com/breml/rootcerts from 0.2.18 to 0.2.19 (#870)
- Bump golang.org/x/net from 0.29.0 to 0.31.0
- Bump github.com/qdm12/gosettings from 0.4.4-rc1 to 0.4.4
- Bump golang.org/x/oauth2 from 0.23.0 to 0.24.0
- Bump golang.org/x/mod from 0.21.0 to 0.22.0
- Bump github.com/go-chi/chi/v5 from 5.1.0 to 5.2.0 (#881)
- Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#871)
📦 CI
- Bump DavidAnson/markdownlint-cli2-action from 17 to 18
📦 Development setup
- change field source.organizeImports from true to "always"
- migrate docker-compose.yml settings to devcontainer.json for codespaces support
- update devcontainer readme
- pin `qmcgaw/godevcontainer` image tag to v0.20-alpine
🐛 Fixes
- Common update mechanism:
- do not update if public IP address is within multiple IP addresses resolved
- IPv6 hostname resolution fixed (it was previously only resolving IPv4)
- Porkbun:
- update API endpoint (#837)
- delete default parked DNS entries if needed (#774)
- delete default `@` ALIAS entry when creating a record for the root domain or the wildcard domain
- delete default `*` CNAME entry when creating a record for the wildcard domain
- + 10 more
📝 Documentation
- GoIP: fix domain field documentation
- eTLD must be goip.de or goip.it, not eTLD+1
- remove old `domain` optional parameter documentation
- DuckDNS: fix domain documentation on eTLD being duckdns.org
🐛 Fixes
- New file/directory permissions fixed given umask (see #813 for more details)
- remove bad calculation of "our own" umask
- remove umask "union effect"
- do not touch system umask unless `UMASK` is set
- set system umask only if `UMASK` is set
✨ Features
- Read both domain ("eTLD+1") and owner (aka *host*) from the `domain` field
- retro-compatible change, `host` or `owner` field still work if set
- documentation updated to only use the `domain` field
- Use umask from operating system to set permissions on created files
- `UMASK` option to configure a custom umask value to use
🐛 Fixes
- PublicIP HTTP echo: remove `google` provider which no longer works
- Porkbun:
- remove trailing '.' from ALIAS delete API call (#775)
- fix wildcard behavior (#773)
- noip: force `useProviderIP` to `false` when using IPv6
- Don Dominio: remove unneeded `name` field
- Upgrade github.com/qdm12/gosettings from v0.4.1 to v0.4.4-rc1
- Bump github.com/breml/rootcerts from 0.2.17 to 0.2.18 (#814)
📋 Changes
- Deprecate `provider_ip` config field completely
- change should not affect any existing configurations
- change solves issues with dual stack updates (#767)
- this option was unneeded and added unneeded complexity
📝 Documentation
- Readme: add instructions to build latest binary
- Documentation updated to only use the `domain` field for both the domain and owner/host.
- Readme: add `README.md` and `docs/` versioned links for each recent releases
- Readme: add AUR package name to features list section
- Goip: fix documentation for the `host` parameter field
- OVH: link to page to retrieve the `consumer_key` value
- Porkbun: fix discrepancy in parameter field names
- Readme: remove outdated Kanban Github board link
📦 Code health
- Rename `cmd/updater` to `cmd/ddns-updater` to have binaries named as `ddns-updater` by default
- `internal/providers`:
- split out settings validation in its own pure function
- move domain check to each provider validation function
- typo 'agend' fixed to 'agent' in user agent banned error (#790)
- `internal/models`: remove unused `DomainHost` struct
- rename `host` to `owner`
- Retro-compatible change, `host` field still works
- + 4 more
📦 Dependencies
- Drop dependency on github.com/chmike/domain
- Bump golang.org/x/net from 0.26.0 to 0.29.0 (#806)
- Bump golang.org/x/oauth2 from 0.21.0 to 0.23.0 (#802)
- Bump github.com/qdm12/gosplash from 0.1.0 to 0.2.0 (#783)
- Bump github.com/miekg/dns from 1.1.61 to 1.1.62 (#815)
- Bump golang.org/x/mod from 0.18.0 to 0.21.0 (#801)
- Bump github.com/go-chi/chi/v5 from 5.0.12 to 5.1.0 (#755)
- Upgrade temporary build image Alpine from 3.19 to 3.20
- + 2 more
🐛 Fixes
- publicip/http: remove google provider which no longer works
- Porkbun: fix wildcard behavior and alias deletion
- Noip: force useProviderIP to false for IPv6
- Don Dominio: remove unneeded `name` field
🐛 Documentation fixes
- readme: add `readme` and `docs/` versioned links
- goip: fix documentation for the `host` field
🐛 Other minor fixes
- Upgrade github.com/qdm12/gosettings from v0.4.1 to v0.4.4-rc1
- Upgrade github.com/breml/rootcerts from 0.2.17 to 0.2.18
- CI: ignore duckdns.org for links check
🐛 Fixes
- `custom`: url building fixed ⚠️
- Web UI: IPs chronological order fixed (reverse) 🖱️
- IP echo: allow custom urls for http ip providers
- `inwx`: allow wildcard hosts
- `ionos`: wildcard handling
- `name.com`
- update root domain record fixed
- detect existing root domain records
- + 4 more
✨ Features
- Web UI: improvements and fixes (#687)
- changeip.com support
- route53 simple routing support (#715)
- `CONFIG_FILEPATH` (or `--config-filepath`) option to specify a configuration file path
- healthchecks.io
- fail and exit codes support
- `HEALTH_HEALTHCHECKSIO_BASE_URL` option
- container: `/updater/data` built-in with correct ownership (#634)
- + 8 more
🐛 Fixes
- See [v2.6.1](https://github.com/qdm12/ddns-updater/releases/tag/v2.6.1) for fixes incorporated in this release compared to v2.6.0
- container: rename `/updater/app` to `/updater/ddns-updater` so it can easily be found in running processes (see #729)
- health server: only run it when running in a container
- trim spaces from each "host" value
- `cloudflare`: prevent empty "key" value if "email" is set
- `godaddy`: link to comment when status code 403 is received
- `ovh`: add no host case handling
📝 Documentation
- readme: update for standalone binaries
- update description and title to be generic and non-specific to containers
- describe availability as container image and prebuilt binaries
- split features in subsections
- readme: fix public ip echo custom url prefix `url:https://` instead of `https://`
- readme: `RESOLVER_ADDRESS` description improved
- readme: `HEALTH_HEALTHCHECKSIO_UUID` description improved
- readme: reference Qnap setup guide (issue #708)
- + 10 more
📦 Coding
- migrate to service architecture with github.com/qdm12/goservices (#743)
- `porkbun`: add context to top level errors
- linter
- remove invalid configuration file fields
- add multiple linters
- `cloudflare`: unexport `createRecord` method
- public ip: better error messages stating the provider type if unknown
- cmd/updater: split main function into smaller functions
- + 2 more
📦 Dependencies
- drop Google SDK dependency
- depend only on golang.org/x/oauth2
- reduce program size from 17MB to 11.5MB
- bump github.com/qdm12/gosettings from 0.4.0-rc9 to 0.4.1 (#683)
- bump github.com/breml/rootcerts from 0.2.14 to 0.2.17 (#612, #631, #741)
- bump github.com/miekg/dns from 1.1.57 to 1.1.61 (#600, #745)
- bump golang.org/x/mod from 0.14.0 to 0.18.0 (#637, #736)
- bump github.com/go-chi/chi/v5 from 5.0.11 to 5.0.12 (#654)
- + 2 more
📦 CI
- bump actions/checkout from 3 to 4 (#599)
- bump actions/setup-go from 2 to 5 (#630)
- bump crazy-max/ghaction-github-labeler from 4 to 5 (#596)
- bump DavidAnson/markdownlint-cli2-action from 14 to 16 (#629, #688)
- bump docker/login-action from 2 to 3 (#597)
- bump docker/setup-qemu-action from 2 to 3 (#584)
- bump golangci-lint to v1.56.2
- bump goreleaser/goreleaser-action from 5 to 6 (#737)
- + 2 more
✨ Features
- Support CLI flags 🎏 (equivalent to environment variable names)
- Automated binaries releasing on GitHub for commonly used platforms
- IPv6: add JSON parameter `ipv6_suffix` and deprecate `IPV6_PREFIX` (retro-compatible change) (#611)
- Public IP fetching 📥
- DNS fetching uses DNS over TLS (DoT) only 🔒, `google` removed due to no support for DoT
- Ipify support for HTTP "IPv4 or IPv6" fetching
- Remove NoIP HTTP option (only plaintext HTTP fetching)
- Add Spdyn for HTTP IPv4 or IPv6 fetching
- + 32 more
🐛 Fixes
- Core updating mechanism
- Get database events by IP version (#514)
- Skip update if public IP is not found
- Do not save invalid IP if the public IP is not found
- Set initial fail status if the public ip fetching failed
- Public IP fetching
- IPv6: replace bad regex with custom IPv6 extract function, affects allinkl, dnsomatic, google, he and noip
- Remove plaintext HTTP option `noip`
- + 41 more
📝 Documentation
- Readme:
- Fix documentation for `SHOUTRRR_DEFAULT_TITLE`
- Fix all dead links
- Fix shoutrrr bad link (#609)
- Remove outdated `ddnss` http public ip fetching
- Use relative links, now compatible with Docker Hub description sync job
- `docs/` directory:
- fix all dead links
- + 21 more
📦 CI
- Add markdown workflow
- Remove docker hub description workflow
- Build workflow ignores md files for spellcheck
- Add goreleaser steps to build and attach binaries on release
- Bump linter `golangci-lint` from v1.52.2 to v1.55.2
- Add mocks check step
- Force matching Shoutrrr link with go.mod shoutrrr version (#491)
- Add dependabot configuration
- + 5 more
📦 Dependencies
- Remove dependency on `golang.org/x/net`
- Bump breml/rootcerts from v0.2.11 to v0.2.14
- Bump github.com/miekg/dns from v1.1.42 to v1.1.57
- Bump golang.org/x/net from v0.1.0 to v0.17.0 (#540)
- Bump chi from v1.5.4 to v5.0.11
- Bump Go from 1.20 to 1.21
- Bump Alpine from 3.18 to 3.19
- Bump google.golang.org/grpc from 1.50.1 to 1.56.3 (#552)
- + 2 more
📦 Code
- Dockerfile:
- Remove outdated `SHOUTRRR_DEFAULT_TITLE`
- Add `HEALTH_SERVER_ADDRESS=127.0.0.1:9999`
- Migrate from `net.IP*` to `net/netip.Addr` and `net/netip.Prefix`
- Drop `github.com/qdm12/golibs` dependency
- `internal/persistence/json`: drop dependency on golibs
- `pkg/publicip/info`: remove dependency on golibs
- `cmd/updater`: replace golibs/connectivity with `internal/health`'s `CheckHTTP` function
- + 42 more
✨ Features
- 🆕 providers
- Aliyun (#252, #253)
- all-inkl.com (#309)
- dynu.com (#285)
- GCP (#337, #405)
- inwx (#379, #473)
- Porkbun (#217)
- Servercow (#224)
- + 12 more
🐛 Fixes
- Cloudflare:
- `key` -> `userServiceKey` variable name (#462)
- fix service key regex
- dd24:
- API call and fix (#236)
- Handle non-empty responses
- ddnss.de add `dual_stack` parameter (#270, thanks @quantum-byte)
- DNSOMatic:
- + 16 more
📦 Maintenance
- Fix line endings to lf (#220)
- Remove microbadger which went EOL
- Remove unneeded `/tmp/data` directory in Dockerfile
- Use `github.com/qdm12/gosplash` for program start log information
- Update `.devcontainer` definition files and readme
📦 Code health
- General refactoring involving:
- Return concrete structs instead of interfaces
- Accept locally defined interfaces as narrow as possible
- Export returned struct types from exported functions
- Do not export interfaces for other packages to use
- Define and use sentinel errors
- Wrap all sentinel errors
- Use string comparisons instead of length for string variables
- + 13 more
📦 Linting
- Upgrade golangci-lint from v1.41.1 to v1.52.2
- Enable all default linters
- Add linters and fix new errors: `ireturn`, `revive`, `asasalint`, `bidichk`, `containedctx`, `cyclop`, `decorder`, `durationcheck`, `errchkjson`, `errname`, `errorlint`, `execinquery`, `forcetypeassert`, `gomoddirectives`, `grouper`, `interfacebloat`, `maintidx`, `makezero`, `nilnil`, `nosprintfhostport`, `promlinter`, `reassign`, `tenv`, `usestdlibvars`, `goerr113`, `dupword`. `paralleltest`, `musttag`, `gocheckcompilerdirectives`
- Remove deprecated linters: `deadcode`, `structcheck` and `varcheck`
- Update exclude rules
📦 Build
- Upgrade Go from 1.16 to 1.20
- Remove unneeded alpine `tzdata` package
- Upgraded Alpine from 3.13 to 3.18
📦 CI
- Allow slash in branch name for docker image tags (#219)
- Remove go modules tidy check
- Add UID and GID build arguments
- Bump GitHub actions versions (#353)
- Restrict publish image trigger events
📦 Dependencies
- Use `github.com/qdm12/log` instead of logger from `github.com/qdm12/golibs`
- Upgrade `qdm12/goshutdown` from v0.1.0 to v0.3.0
- Upgrade `github.com/qdm12/golibs`
- Wrap errors in config package with environment variable name
- Update logger
📝 Documentation
- Fix links in `.github/CONTRIBUTING.md`
📦 Readme
- Document `RESOLVER_ADDRESS` environment variable
- Add *Build the image* section
- Clarify how to use another user ID for the container
- Rework metadata badges
- Fix lint errors
- Remove `sanitize` query parameter from svg tag
- Autoformat document
📝 `./docs`
- Cloudflare:
- host parameter should be `@`
- remove unneeded steps (#363)
- DuckDNS: fix `provider_ip` not for ipv6
- Dyn: change JSON field from `password` to `client_key` (with retro-compatibility)
- FreeDNS: add domain setup section (#238)
- Gandi: fix JSON example syntax (#362)
- Google: fix JSON example (#223)
📋 Changes
- Fix: `SHOUTRRR_ADDRESSES` case sensitivity
✨ Features
- Support for domaindiscount24.com (#207)
- Support Shoutrrr addresses
- `PUBLICIP_DNS_TIMEOUT` variable
🐛 Bug fixes
- Wildcard hosts (#214)
- Keep multi-dots wildcard host structure in display strings
- Use a function `BuildURLQueryHostname` for API calls to send the correct wildcard hostname
- Send the wildcard character in API calls
- Fix behavior for wildcard hosts for: cloudflare, ddnss.de, digitalocean, dnsomatic, dreamhost, dyn, dynv6, google, informaniak, njalla, noip, opendns, ovh, selfhost.de, spdyn, strato and variomedia
- Healthcheck query to `127.0.0.1:port` instead of for example `0.0.0.0:port`
- `DATADIR` defaults to `/updater/data`
- Server listens on all interfaces (IPv4 and IPv6) instead of just `0.0.0.0`
- + 2 more
📝 Documentation
- Add missing architectures to readme: `ppc64le`, `s390x`, `riscv64` and `armv6`
📦 Maintenance
- `internal/config` package for environment variables
- Remove `github.com/ovh/go-ovh` dependency
- Use `time/tzdata` anonymous import instead of Alpine's `tzdata`
- Use `github.com/qdm12/goshutdown` to handle program shutdown
- Simplify file paths logic
- Use `signal.NotifyContext` (introduced with Go 1.16)
- Inject more objects to `_main()` in main.go:
- `params.Env`
- + 19 more
✨ Features
- HTTP and DNS Public IP fetching options (#136, #187)
- Njalla support (#180, #181)
- SPDyn support (#182, #179)
- Variomedia support (#208, #174)
- Allow to run without settings
- `debug` log level
- `HEALTH_SERVER_ADDRESS` to change the internal health server listening address
- Request URL and body debug logs for each provider
🐛 Fixes
- Read `CONFIG` environment variable with case sensitivity (#192) - thanks @mchill
- Dreamhost: create record before removing outdated one (#206)
- `ipversion` display (#190)
- `ROOT_URL` behavior when served outside of root (bug introduced with `go-chi`)
- `IPV6_PREFIX` support to avoid unneeded updates when using IPV6
📦 Maintenance
- Use embed for static UI (#134)
- `internal/settings` shared code in sub packages
- Move settings construction from `internal/params` to `internal/settings`
- Split each provider in own subpackage in `internal/settings`
- Rename each provider file to `provider.go`
- Upgrade all Go dependencies
- Upgrade devcontainer settings
- Upgrade linting setup with Golangci-lint to `v1.40.1`
- + 5 more
✨ Features
- `pkg/publicip` package to fetch your public IP address over HTTPs and DNS (#158 and #186)
📝 Documentation
- Fix #175 (#176)
📦 Maintenance
- Remove unused `Insert` database method
- Use anonymous variable name `_` for unused matcher argument to settings constructors
✨ Features
- Only call the DNS APIs if the public IP address changes (#63)
- Only one ip method per ip version (ipv4, ipv6, ipv4/v6) (#63)
- Fetches the public ip address once every period for all records (#63)
- Support to update ipv4 and ipv6 records separately
- Support multiple comma separated hosts for each setting
- Read the JSON configuration from an environment variable `CONFIG` (#62)
- New IP fetch methods:
- Google (#69)
- + 29 more
🐛 Bug fixes
- Cloudflare named as `Dreamhost` on Web UI (#79)
- Accept Cloudflare tokens with a `-` in them (#86)
- Remove regex domain check (#92)
- Remove regex check for Clouidflare API key (#101)
- Accept email addresses with `+` in them (#112)
- Accept GoDaddy new key format (#113 and #169)
- Listening port defaults to `8000` outside Docker (#152)
- Remove GoDaddy secret regex check
📋 Changes
- DuckDNS setting changed to `host` instead of `domain`
- Replace `no_dns_lookup` option with `proxied` option only valid for Clouflare (#160)
📝 Documentation
- Add issue templates
- Readme improved (#106, #151, #165, #167)
- Readme sections moved to docs (#128) (thanks @gauravspatel)
- Timezone `TZ` variable (#90)
- DuckDNS documentation fix (#171)
- DDNSgopher logo in readme (#157)
- Architecture section in readme
📦 Maintenance
- DNS provider names sorted alphabetically
- `UNSET` status before a record gets updated the first time
- Using Alpine 3.13 and Go 1.16 for building the binary
- Update Go modules dependencies
- Refactor HTTP servers (#164)
- Rework current server
- Update call is blocking
- Run first update without blocking
- + 6 more
📦 Dev tooling
- Update dev container settings
- Go extension
- Remove rewrap extension
- SSH bind mount as read write
- Remove local vscode directory
- Upgrade golangci-lint to 1.37.0
- Add more Go linters to .golangci.yml
- Removed some Github workflows
- + 5 more
📋 Changes
- No SQLite support
- JSON file stored data
- Periodic backup
📋 Changes
- Web UI (only display)
- Support for A records update for GoDaddy, Namecheap, Cloudflare, Dreamhost, NoIP, DNSPod, Infomaniak, ddnss.de and DuckDNS
- Support for AAAA records update for Infomaniak and ddnss.de
- Using JSON file for persistence of changes, with SQLite for backward compatibility
- Compatible with Gotify
- Docker healthcheck
- Multiple IP methods available
