feat: Add Docker-based dynamic domain discovery#276
Closed
fullpipe wants to merge 7 commits into
Closed
Conversation
Author
|
P.S.: Have some issues with code formatting. A lot of fixes with simple $ cargo -V
cargo 1.95.0 (f2d3ce0bd 2026-03-21)
$ cargo fmt --version
rustfmt 1.9.0 |
There was a problem hiding this comment.
Pull request overview
This RFC introduces Docker-based dynamic domain discovery so the updater can automatically pick up domains from running containers (via a ddns.domain label) instead of requiring manual DOMAINS env var updates.
Changes:
- Adds a Docker scanner (using
bollard) that periodically reads container labels and publishes an updated domain set through atokio::sync::watchchannel. - Threads the dynamic domain set into the update loop by changing
updater::update_onceto accept a domains map per cycle. - Extends env config with
DOCKER_HOSTto enable Docker-based discovery and updates tests/struct initialization accordingly.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/updater.rs | Accepts per-cycle domains input and iterates over it for record updates. |
| src/main.rs | Sets up a watch channel for domains and optionally spawns the Docker domain scanner. |
| src/docker.rs | New module: scans Docker containers for ddns.domain labels and updates the watch channel. |
| src/config.rs | Adds docker_host to AppConfig and loads DOCKER_HOST from env; updates validation/tests. |
| src/pp.rs | Derives Clone for PP to allow passing it into spawned tasks. |
| Cargo.toml | Adds the bollard dependency. |
| Cargo.lock | Locks new transitive dependencies from adding bollard. |
Comments suppressed due to low confidence (2)
src/updater.rs:121
- If
domainscontains an IP type that has no configured provider (e.g.,IP6_PROVIDER=nonebut domains include IPv6),detected_ipswill always be empty and the code may proceed to delete records whenDELETE_ON_FAILURE=true. This becomes more likely with Docker-discovered domains and can lead to unintended deletions.
for (ip_type, domains) in domains.iter() {
let ips = detected_ips.get(ip_type).cloned().unwrap_or_default();
if ips.is_empty() && !config.delete_on_failure {
ppfmt.warningf(
src/config.rs:545
- The validation error message for having no update targets doesn’t mention
DOCKER_HOST, even though it is now a valid way to configure DNS update targets. This makes misconfiguration harder to diagnose.
if domains.is_empty() && docker_host.is_none() && waf_lists.is_empty() {
return Err(
"No update targets configured. Set DOMAINS, IP4_DOMAINS, IP6_DOMAINS, or WAF_LISTS."
.to_string(),
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+66
to
+111
| async fn scan_and_publish_once( | ||
| docker: &Docker, | ||
| static_domains: &HashMap<IpType, Vec<String>>, | ||
| docker_domains: HashSet<String>, | ||
| domains_tx: &mut Sender<HashMap<IpType, Vec<String>>>, | ||
| ) -> Result<HashSet<String>, Error> { | ||
| let list_options = ListContainersOptionsBuilder::default() | ||
| .all(true) | ||
| .filters(&HashMap::from([( | ||
| "status", | ||
| vec!["created", "restarting", "running"], | ||
| )])) | ||
| .build(); | ||
|
|
||
| let list = docker.list_containers(Some(list_options)).await?; | ||
|
|
||
| let new_docker_domains: HashSet<String> = list | ||
| .iter() | ||
| .filter_map(|c| { | ||
| c.labels | ||
| .as_ref() | ||
| .and_then(|labels| labels.get("ddns.domain")) | ||
| }) | ||
| .cloned() | ||
| .collect(); | ||
|
|
||
| if docker_domains == new_docker_domains { | ||
| return Ok(docker_domains); | ||
| } | ||
|
|
||
| let mut new_domains = static_domains.clone(); | ||
|
|
||
| new_domains | ||
| .entry(IpType::V4) | ||
| .or_default() | ||
| .extend(new_docker_domains.clone()); | ||
|
|
||
| new_domains | ||
| .entry(IpType::V6) | ||
| .or_default() | ||
| .extend(new_docker_domains.clone()); | ||
|
|
||
| domains_tx.send_replace(new_domains); | ||
|
|
||
| return Ok(new_docker_domains); | ||
| } |
Introduce a background domain cleanup task and make notifiers clonable. - Add spawn_domain_cleanup in docker.rs: spawns a task that watches domain changes and deletes Cloudflare records for domains removed from the running set when delete_on_stop is enabled (skips in legacy mode). It uses the CloudflareHandle to perform final_delete and sends messages via CompositeNotifier. - Make CompositeNotifier cloneable by adding a CloneBox helper and changing NotifierDyn to require CloneBox; implement Box<dyn NotifierDyn> cloning. Update Shoutrrr notifier types to derive Clone. - Minor CloudflareHandle tweaks: add Debug/Clone derive, small API call formatting cleanup, and various signature/format adjustments. - Wire up spawn_domain_cleanup from main and adjust several call sites to borrow domains_rx instead of cloning unnecessarily. - Lots of formatting and test adjustments (wrapping long lines, reformatting mocks) to accommodate the new code and maintain style. These changes enable safe concurrent cleanup of DNS/WAF items when containers stop and allow notifier components to be duplicated for use inside spawned tasks.
Author
|
Merged it to https://github.com/fullpipe/cloudflare-ddns if some one intrested |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hello,
Using ddns with docker most of the time. And it is annoying to update env vars when adding new local domains. So I made use of
/var/run/docker.sock. And now it is possible to use ddns like this.