Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The architectures supported by this image are:

- Go to the [duckdns website](https://duckdns.org/), register your subdomain(s) and retrieve your token.
- Create a container with your subdomain(s) and token. If you own `user.duckdns.org`, you set `SUBDOMAINS=user`. You would NOT set a sub subdomain like `overseerr` from `overseerr.user.ducksdns.org`.
- It will update your IP with the DuckDNS service every 5 minutes (with a random jitter).
- It will update your IP with the DuckDNS service at a configurable interval (default: every 5 minutes, with a random jitter). Use the `UPDATE_INTERVAL` environment variable to customize the update frequency.

## Notice regarding automatic detection

Expand Down Expand Up @@ -94,6 +94,7 @@ services:
- SUBDOMAINS=subdomain1,subdomain2
- TOKEN=token
- UPDATE_IP=ipv4 #optional
- UPDATE_INTERVAL=5m #optional
- LOG_FILE=false #optional
volumes:
- /path/to/duckdns/config:/config #optional
Expand All @@ -112,6 +113,7 @@ docker run -d \
-e SUBDOMAINS=subdomain1,subdomain2 \
-e TOKEN=token \
-e UPDATE_IP=ipv4 `#optional` \
-e UPDATE_INTERVAL=5m `#optional` \
-e LOG_FILE=false `#optional` \
-v /path/to/duckdns/config:/config `#optional` \
--restart unless-stopped \
Expand All @@ -131,6 +133,7 @@ Containers are configured using parameters passed at runtime (such as those abov
| `-e SUBDOMAINS=subdomain1,subdomain2` | multiple subdomains allowed, comma separated, no spaces, if your domain is user.duckdns.org you put user, not a sub-subdomain |
| `-e TOKEN=token` | DuckDNS token |
| `-e UPDATE_IP=ipv4` | Set to `ipv6` or `ipv4` to update **only** your public IPv4/6 address. Set to `both` to update IPv6 and IPv4 address. This variable makes use of a [third-party service](#notice-regarding-automatic-detection). Omitting this variable uses DuckDNS for detection and only supports IPv4. `both` and `ipv6` modes needs [host networking](#networking-net). |
| `-e UPDATE_INTERVAL=5m` | Set the update interval. Format: `[number][m|h]` where `m` is minutes (5-60) or `h` is hours (1-24). Examples: `5m` (every 5 minutes), `30m` (every 30 minutes), `2h` (every 2 hours). Default is `5m`. |
| `-e LOG_FILE=false` | Set to `true` to log to file (also need to map /config). |
| `-v /config` | Persistent config files. Also set `LOG_FILE=true` to keep address history. |
| `--read-only=true` | Run container with a read-only filesystem. Please [read the docs](https://docs.linuxserver.io/misc/read-only/). |
Expand Down Expand Up @@ -299,6 +302,7 @@ Once registered you can define the dockerfile to use with `-f Dockerfile.aarch64

## Versions

* **18.02.26:** - Add configurable update interval via UPDATE_INTERVAL environment variable.
* **27.07.25:** - Rebase to Alpine 3.22.
* **27.01.25:** - Rebase to Alpine 3.21.
* **24.06.24:** - Rebase to Alpine 3.20.
Expand Down
4 changes: 3 additions & 1 deletion readme-vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ param_env_vars:
opt_param_usage_include_env: true
opt_param_env_vars:
- {env_var: "UPDATE_IP", env_value: "ipv4", desc: "Set to `ipv6` or `ipv4` to update **only** your public IPv4/6 address. Set to `both` to update IPv6 and IPv4 address. This variable makes use of a [third-party service](#notice-regarding-automatic-detection). Omitting this variable uses DuckDNS for detection and only supports IPv4. `both` and `ipv6` modes needs [host networking](#networking-net).", env_options: ["", "ipv4", "ipv6", "both"]}
- {env_var: "UPDATE_INTERVAL", env_value: "5m", desc: "Set the update interval. Format: `[number][m|h]` where `m` is minutes (5-60) or `h` is hours (1-24). Examples: `5m` (every 5 minutes), `30m` (every 30 minutes), `2h` (every 2 hours). Default is `5m`.", env_options: ["5m", "30m", "1h", "2h"]}
- {env_var: "LOG_FILE", env_value: "false", desc: "Set to `true` to log to file (also need to map /config).", env_options: ["false", "true"]}
opt_param_usage_include_vols: true
opt_param_volumes:
Expand All @@ -36,7 +37,7 @@ app_setup_block_enabled: true
app_setup_block: |
- Go to the [duckdns website]({{project_url}}), register your subdomain(s) and retrieve your token.
- Create a container with your subdomain(s) and token. If you own `user.duckdns.org`, you set `SUBDOMAINS=user`. You would NOT set a sub subdomain like `overseerr` from `overseerr.user.ducksdns.org`.
- It will update your IP with the DuckDNS service every 5 minutes (with a random jitter).
- It will update your IP with the DuckDNS service at a configurable interval (default: every 5 minutes, with a random jitter). Use the `UPDATE_INTERVAL` environment variable to customize the update frequency.

## Notice regarding automatic detection

Expand Down Expand Up @@ -85,6 +86,7 @@ init_diagram: |
"duckdns:latest" <- Base Images
# changelog
changelogs:
- {date: "18.02.26:", desc: "Add configurable update interval via UPDATE_INTERVAL environment variable."}
- {date: "27.07.25:", desc: "Rebase to Alpine 3.22."}
- {date: "27.01.25:", desc: "Rebase to Alpine 3.21."}
- {date: "24.06.24:", desc: "Rebase to Alpine 3.20."}
Expand Down
1 change: 1 addition & 0 deletions root/app/duck.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ if [[ "${LOG_FILE,,}" = "true" ]]; then
touch /config/logrotate.status
chmod 640 /config/logrotate.status
/usr/sbin/logrotate -s /config/logrotate.status /config/logrotate.conf
echo "duck.sh invoked at $(date)" >> "${DUCK_LOG}"
else
DUCK_LOG="/dev/null"
fi
Expand Down
2 changes: 2 additions & 0 deletions root/defaults/abc.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# min . hour day month weekday command
${MINUTES} ${HOURS} * * * sleep $((60 + $RANDOM % 230)); /app/duck.sh 2>&1
2 changes: 0 additions & 2 deletions root/etc/crontabs/abc

This file was deleted.

43 changes: 42 additions & 1 deletion root/etc/s6-overlay/s6-rc.d/init-duckdns/run
Original file line number Diff line number Diff line change
@@ -1,12 +1,53 @@
#!/usr/bin/with-contenv bash
# shellcheck shell=bash

#Check to make sure the subdomain and token are set
# Check to make sure the subdomain and token are set
if [ -z "${SUBDOMAINS}" ] || [ -z "${TOKEN}" ]; then
echo "Please pass both your subdomain(s) and token as environment variables in your docker run command. See the readme for more details."
sleep infinity
fi

# Render template into real crontab with default interval if not provided
UPDATE_INTERVAL=${UPDATE_INTERVAL:-5m}
if [[ ! "$UPDATE_INTERVAL" =~ ^[0-9]+[mh]?$ ]]; then
echo "Invalid interval format: \"$UPDATE_INTERVAL\", expected format \"[0-9]+[mh]?\""
exit 1
fi

case "$UPDATE_INTERVAL" in
*h)
HOURS=${UPDATE_INTERVAL%h}
if (( HOURS < 1 || HOURS > 24 )); then
echo "Update interval must be between 1-24 hours"
exit 1
fi
# Cron hour field is 0-23, so 24h must be normalized to daily at midnight.
if (( HOURS == 24 )); then
HOURS="0"
else
HOURS="*/$HOURS"
fi
MINUTES="0"
;;
*)
MINUTES=${UPDATE_INTERVAL%m}
if (( MINUTES < 5 || MINUTES > 60 )); then
echo "Update interval must be between 5-60 minutes"
exit 1
fi
HOURS="*"
# Cron minute field is 0-59, so 60m must be normalized to top of each hour.
if (( MINUTES == 60 )); then
MINUTES="0"
else
MINUTES="*/$MINUTES"
fi
;;
esac

sed "s|\${MINUTES} \${HOURS}|$MINUTES $HOURS|g" < /defaults/abc.tmpl > /etc/crontabs/abc
chmod 644 /etc/crontabs/abc

if [[ ! -f /config/logrotate.conf ]]; then
cp /defaults/logrotate.conf /config/logrotate.conf
chmod 640 /config/logrotate.conf
Expand Down