mibe /
php-ddns-client
A simple client for updating hosts at Dynamic DNS service providers
Loading repository data…
stecklars / repository
A simple dynamic DNS client written in PHP for use with the netcup DNS API.
This project is not affiliated with the company netcup GmbH. Although it is developed by an employee, it is not an official client by netcup GmbH and was developed in my free time. netcup is a registered trademark of netcup GmbH, Karlsruhe, Germany.
A simple dynamic DNS client written in PHP for use with the netcup DNS API.
--force to bypass the cache.JITTER_MAX in config.Download the latest version from the releases or clone the repository:
$ git clone https://github.com/stecklars/dynamic-dns-netcup-api.git
I'm always trying to keep the master branch stable.
Then, allow update.php to be executed by your user:
chmod u+x update.php
config.dist.php to config.php
cp config.dist.php config.phpconfig.php with the required values. The options are explained in there../update.php
You should probably run this script every few minutes, so that your IP is updated as quickly as possible. Add it to your cronjobs and run it regularly, for example every five minutes.
A Docker image is available for systems without PHP, such as NAS devices. The image is built for linux/amd64, linux/arm64, and linux/arm/v7 (e.g. Raspberry Pi, NAS devices). It includes PHP, cURL, and a built-in scheduler — no additional setup required.
You can configure the container either by mounting a config.php (steps below) or by passing environment variables (details). Pick whichever fits your workflow — the file mount takes precedence if both are present.
Create your config.php first — use config.dist.php as a template. Before starting the container in cron mode, verify your config works:
docker run --rm -v ./config.php:/app/config.php:ro stecklars/dynamic-dns-netcup-api --run-once
If this runs successfully, proceed with the instructions for your platform below. If it fails, the error message will tell you what to fix. If the container exits immediately after starting in cron mode, check docker logs dyndns for the error.
| Variable | Default | Description |
|---|---|---|
| CRON_SCHEDULE | */5 * * * * | How often to check for IP changes |
| TZ | UTC | Timezone for the schedule and log output |
| HEALTHCHECK_GRACE_SECONDS | JITTER_MAX + 120 | Extra grace after the next scheduled run before the container becomes unhealthy |
(See Configuration via environment variables for the env vars that replace config.php.)
| Container path | Description |
|---|---|
/app/config.php | Your configuration file (read-only). Optional when configuring via environment variables. |
/app/data | Persistent IP cache (empty dir, created automatically) |
stecklars/dynamic-dns-netcup-api in your NAS Docker GUI and download the imageThe image is pulled from Docker Hub automatically — no need to clone the repository:
docker run -d --name dyndns \
-v ./config.php:/app/config.php:ro \
-v dyndns-data:/app/data \
-e CRON_SCHEDULE="*/5 * * * *" \
-e TZ=Europe/Berlin \
--restart unless-stopped \
stecklars/dynamic-dns-netcup-api
If you have cloned the repository, you can use docker compose instead:
docker compose up -d
To run the script once instead of starting the scheduler (e.g., to test your config):
docker run --rm -v ./config.php:/app/config.php:ro stecklars/dynamic-dns-netcup-api --run-once
One-shot mode is meant for testing and manual runs — for regular use, use the cron mode (the default) which handles scheduling, caching, and jitter automatically.
Script flags like --force and --quiet can be used in both modes, e.g.:
docker run --rm -v ./config.php:/app/config.php:ro stecklars/dynamic-dns-netcup-api --run-once --force
If you'd rather not mount a config.php, you can pass every setting as an environment variable instead — convenient when secrets come from your orchestrator (docker run --env-file, Docker secrets, Kubernetes ConfigMaps, etc.). On startup the entrypoint generates config.php from the env vars.
docker run -d --name dyndns \
-e CUSTOMERNR=12345 \
-e APIKEY=your-api-key \
-e APIPASSWORD=your-api-password \
-e DOMAINLIST="example.com: @, www; second.tld: mail" \
-e USE_IPV4=true \
-e USE_IPV6=false \
-e CHANGE_TTL=true \
-v dyndns-data:/app/data \
--restart unless-stopped \
stecklars/dynamic-dns-netcup-api
| Variable | Required | Default | Description |
|---|---|---|---|
| CUSTOMERNR | yes | — | netcup customer number |
| APIKEY | yes | — | netcup API key |
| APIPASSWORD | yes | — | netcup API password |
| DOMAINLIST | yes | — | Domain configuration (see config.dist.php for the format) |
| USE_IPV4 | no | true | Update A records |
| USE_IPV6 | no | false | Update AAAA records |
| CHANGE_TTL | no | true | Lower TTL to 300 seconds on each run |
| APIURL | no | https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON | Override the netcup API endpoint |
| IPV4_ADDRESS_URL | no | https://get-ipv4.steck.cc | Primary IPv4 lookup URL |
| IPV4_ADDRESS_URL_FALLBACK | no | https://ipv4.seeip.org | Fallback IPv4 lookup URL |
| IPV6_ADDRESS_URL | no | https://get-ipv6.steck.cc | Primary IPv6 lookup URL |
| IPV6_ADDRESS_URL_FALLBACK | no | https://v6.ident.me | Fallback IPv6 lookup URL |
| RETRY_SLEEP | no | 30 | Seconds to wait between retries |
| JITTER_MAX | no | 30 | Max random delay before API calls (0 to disable) |
Booleans accept true / false / 1 / 0 / yes / no / on / off (case-insensitive). Verify your env-var configuration in one-shot mode before going into cron mode:
docker run --rm \
-e CUSTOMERNR=12345 -e APIKEY=... -e APIPASSWORD=... \
-e DOMAINLIST="example.com: @" \
stecklars/dynamic-dns-netcup-api --run-once
--run-once exercises the env-var → config.php conversion and a single update; it does not start crond or validate CRON_SCHEDULE. If --run-once is happy, cron mode usually is too — but the easiest way to confirm is to start the container in cron mode and watch docker logs.
If a config.php is mounted at /app/config.php, it always takes precedence and these env vars are ignored.
docker logs dyndns # show all logs
docker logs -f dyndns # follow logs in real-time
docker logs --tail 20 dyndns # show last 20 lines
In docker compose, use docker compose logs -f.
The image defines a Docker HEALTHCHECK. It does not run update.php again; instead it checks whether the last successful run is still fresh relative to CRON_SCHEDULE.
This means irregular schedules are handled correctly. For example, a schedule like 0 3 * * 1-5 stays healthy over the weekend and only turns unhealthy after a weekday run is overdue. The default grace period is JITTER_MAX + 120 seconds and can be overridden with HEALTHCHECK_GRACE_SECONDS.
:z flag to all volume mounts, e.g., -v ./config.php:/app/config.php:ro,z -v dyndns-data:/app/data:z. This does not affect NAS systems.USE_IPV6=true, run the container with --network host or configure Docker's IPv6 support.Just add these Options after the command like ./update.php --quiet
| short option | long option | function |
|---|---|---|
| -q | --quiet | The script won't output notices or warnings, only errors |
| -c | --config | Manually provide a path to the config file |
| -4 | --ipv4 | Manually provide the IPv4 address |
Selected from shared topics, language and repository description—not editorial ratings.
mibe /
A simple client for updating hosts at Dynamic DNS service providers