No-Fixed-Address
Docker app from Richardson Resources' Repository
Overview
Readme
View on GitHub
No Fixed Address
Self-hosted dynamic DNS for Cloudflare DNS.
A small container that keeps your A and AAAA records pointed at your current public IP address.
Why
Home internet connections rarely have a fixed IP address, so a name like
home.example.com stops working the next time your ISP hands you a new one.
Free dynamic DNS services solve that until they don't: hostnames lapse unless
you log in to confirm them every month, free tiers shrink, and the service in
the middle is one more account that has to stay alive.
If your domain's DNS is already on Cloudflare, you don't need that service. Cloudflare's API can update the record directly, and an API token doesn't expire unless you tell it to. No Fixed Address does exactly that, and nothing else:
- Set and forget. Checks when it starts and then every five minutes (configurable), and only writes when the address has actually changed.
- IPv4 and IPv6. Any number of names, in one zone or several, including zone apexes and wildcards.
- Leaves your settings alone. Proxy status and TTL are preserved unless you ask it to manage them.
- Least privilege. One API token limited to the zones you use, a non-root container, and the token is never logged or put on a command line.
- Rides out outages. Network and API failures are retried with backoff, from one minute up to an hour. It never crash-loops.
- Tells you when it's stuck. A Docker health check turns unhealthy when updates stop succeeding, and an optional notification to Discord, Slack, ntfy, Gotify, Apprise or any webhook says when an address changes or something needs attention.
- Small. A POSIX shell script with curl and jq on Alpine, for amd64, arm64 and 32-bit ARM (v7 and v6), so it runs on any Raspberry Pi.
How it works
- At startup it checks every setting, verifies the API token with Cloudflare and finds the zone each record is in. Bad settings stop the container with a message saying what to fix.
- It finds the public address by asking
https://1.1.1.1/cdn-cgi/trace, falling back toapi.ipify.organdifconfig.me. IPv6 uses[2606:4700:4700::1111]/cdn-cgi/traceandapi6.ipify.org. Answers are strictly validated, and private or reserved addresses are rejected, so a failed or garbled lookup can't put a nonsense address in your DNS. Validation can't prove an address is really yours, which is why every default service uses HTTPS. - It compares the address with each record and updates only the ones that differ.
- It sleeps until the next check. If something failed along the way, it retries after 60 seconds, then 2 minutes, then 4, up to an hour, until a check succeeds.
Quick start
Try it without changing anything:
docker run --rm \ -e CF_API_TOKEN=your-token \ -e RECORDS=home.example.com \ -e INTERVAL=300 \ -e DRY_RUN=true \ ghcr.io/mdr6273/no-fixed-address:latestIt logs what it would change, then waits for the next check. Press Ctrl+C to stop it.
Run it for real with
DRY_RUNremoved, using docker run, Docker Compose or the Unraid template.
Creating the Cloudflare API token
The container needs an API token that can read the zones your records are in and edit their DNS records. Limit it to those zones so that, if it ever leaked, nothing else in your Cloudflare account would be exposed.
- Log in to the Cloudflare dashboard, open My Profile from the account menu, and choose API Tokens.
- Select Create Token, then Create Custom Token (Get started).
- Give it a name you'll recognise later, such as
No Fixed Address (home server). - Under Permissions, add two rows:
- Zone / DNS / Edit
- Zone / Zone / Read
- Under Zone Resources, choose Include / Specific zone / your domain. If your records are in more than one domain, add a row for each.
- Leave Client IP Address Filtering empty. Your IP address is the thing that changes, so a filter would eventually lock the container out.
- Leave the TTL (token expiry) empty, or set an end date and remember to replace the token before then.
- Select Continue to summary, then Create Token, and copy the token. Cloudflare shows it only once.
Check that the token works before using it:
curl -s -H "Authorization: Bearer your-token" \
https://api.cloudflare.com/client/v4/user/tokens/verify
The response should include "status":"active".
Use an API token, not your Global API Key. An account-owned token, created
with the same permissions on the account's own API Tokens page, works as
well. Cloudflare verifies those at a different endpoint, so the curl check
above reports them as invalid, but the container recognises and verifies them
either way.
Unraid
The Unraid template is maintained in Richardson Resources' Community
Applications repository, unraid-apps,
as templates/nofixedaddress.xml. To install it:
Open a terminal on the server (the
>_icon in the web UI) and download the template:wget -O /boot/config/plugins/dockerMan/templates-user/my-No-Fixed-Address.xml \ https://raw.githubusercontent.com/mdr6273/unraid-apps/main/templates/nofixedaddress.xmlOn the Docker tab, select Add Container, and choose No-Fixed-Address from the Template list (under user templates).
Fill in Cloudflare API token and Records. Records are full names, such as
home.example.com, not justhome, and can be in different domains. For the first start, set Dry run totrue.Select Apply, then open the container's log from its icon on the Docker tab. It should show the token being verified and what it would change.
When the log looks right, edit the container, set Dry run back to
false, and apply.
Notes for Unraid:
- Keep the token out of screenshots: the window Unraid shows after Apply prints the whole
docker runcommand, token included. Blank it out before sharing that window, for example when asking for help. - Updates: when a new version is published, the Docker tab shows an update for the container. Apply it from there, after reading the changelog. See Updating.
- Time zone: Unraid passes your server's time zone to every container, so log timestamps already match it. The template has no
TZfield on purpose, because a blank one would override that. - IPv6: the default bridge network has no IPv6. To update AAAA records, set Network Type to Host, or use a custom network with IPv6 enabled.
- Advanced settings: notifications, TTL, log level, a token file and the lookup URLs are under Show more settings.
- Containers from before 1.1.0 have a Zone field. It still works, and you can also clear it: the container then finds the zone of each record itself.
- Locked down: the template's Extra Parameters (shown with Advanced View) drop all Linux capabilities, block privilege escalation, and make the container's filesystem read-only apart from a scratch area at
/tmp. The container needs nothing more, so leave them in place. - Older links: before unraid-apps existed, the template was published from this repository. The unraid/ folder keeps a copy of it and of the icon so that those links, and containers installed from them, keep working. It is the same template; see unraid/README.md.
docker run
docker run -d \
--name nofixedaddress \
--restart unless-stopped \
--read-only --tmpfs /tmp \
--cap-drop ALL --security-opt no-new-privileges \
-e CF_API_TOKEN=your-token \
-e RECORDS=home.example.com,vpn.example.com \
-e INTERVAL=300 \
-e TZ=America/Chicago \
ghcr.io/mdr6273/no-fixed-address:latest
The --read-only, --tmpfs, --cap-drop and --security-opt lines are
optional but recommended: the container needs no privileges and writes only to
/tmp.
latest follows the newest release. To get fixes and new features but never a
breaking change, use 1 instead; Versions lists all the tags.
Docker Compose
See docker-compose.example.yml for a complete file, including a read-only, unprivileged container. The short version:
services:
nofixedaddress:
image: ghcr.io/mdr6273/no-fixed-address:latest
restart: unless-stopped
environment:
CF_API_TOKEN: ${CF_API_TOKEN} # from a .env file next to the compose file
RECORDS: home.example.com,vpn.example.com
INTERVAL: "300"
TZ: America/Chicago
Keeping the token out of docker inspect
Anything passed with -e or environment: shows up in docker inspect. To
avoid that, put the token in a file and point CF_API_TOKEN_FILE at it, for
example with a Compose secret:
services:
nofixedaddress:
image: ghcr.io/mdr6273/no-fixed-address:latest
restart: unless-stopped
environment:
CF_API_TOKEN_FILE: /run/secrets/cf_api_token
RECORDS: home.example.com
INTERVAL: "300"
secrets:
- cf_api_token
secrets:
cf_api_token:
file: ./cf_api_token.txt
The container runs as uid 10001, so the file must be readable by that user. Make it readable by no one else, since anyone who can read it can change every record in your zones:
sudo chown 10001:10001 cf_api_token.txt
sudo chmod 0400 cf_api_token.txt
Updating
Your settings carry over, because they live in the container's configuration,
not inside the image. latest also brings new major versions, which can
rename settings or change defaults, so glance at the changelog
when an update shows up: if the version's first number has gone up, read what
changed before applying it.
- Unraid: when a new version is published, the Docker tab shows update ready next to the container (Check for Updates looks right away). Select it and apply the update.
- Docker Compose: in the folder with your compose file, run
docker compose pull, thendocker compose up -d. - docker run: run
docker pull ghcr.io/mdr6273/no-fixed-address:latest, remove the container withdocker rm -f nofixedaddress, and start it again with the samedocker runcommand. A running container keeps using the image it started with.
These steps assume the latest tag, and work the same for 1 and 1.1. If you
pinned an exact version such as 1.1.0, change the tag to the new version
instead. A pinned tag never changes, so Unraid never shows an update for it.
Versions
Version numbers have three parts, and the part that goes up tells you what kind of release it is (semantic versioning):
| Release | What changed | Before updating |
|---|---|---|
| 1.1.1 | Fixes, including rebuilds for security updates in the base image | Nothing to do |
| 1.2.0 | New features, such as a new optional setting; existing setups work as before | Nothing to do |
| 2.0.0 | Breaking changes, such as a renamed setting or a different default | Read the changelog |
Breaking means a change to the names, defaults or meaning of the settings in
Configuration, the image name, or the user ID the container
runs as (10001). Log messages can change in any release, and so can
CF_API_URL, which exists only for testing.
Each release is published under several tags:
| Tag | Moves to | Use it to |
|---|---|---|
latest |
every release | always run the newest version (the Unraid template uses this) |
1 |
1.x.y releases | get fixes and features, but never a breaking change |
1.1 |
1.1.x releases | get fixes only |
1.1.0 |
nothing; it never changes | update only when you choose |
Configuration
| Variable | Default | Description |
|---|---|---|
CF_API_TOKEN |
required | The scoped API token. |
CF_API_TOKEN_FILE |
Path to a file containing the token, such as a Docker secret. Use instead of CF_API_TOKEN, not as well. |
|
RECORDS |
required | Comma-separated full names to keep updated, such as home.example.com,vpn.example.com (not just home). They can be in different zones: the container finds each name's zone among the zones the token can see. Wildcards such as *.example.com work too. Often one name is enough: see Pointing more names at it. |
INTERVAL |
86400 |
Seconds between checks. A check always runs at startup. The Unraid template and the examples here set 300 (every five minutes), like other dynamic DNS updaters; the image's own default stays at a day so that setups which never set it don't change. |
IPV4 |
true |
Keep A records pointed at the public IPv4 address. |
IPV6 |
false |
Keep AAAA records pointed at the public IPv6 address. Needs IPv6 inside the container. |
PROXIED |
unset | true or false sets Cloudflare's proxy on every record. Unset leaves each record as it is. |
TTL |
unset | 1 for automatic, or 60 to 86400 seconds. Unset leaves each record as it is. Ignored for proxied records, which always use automatic TTL. |
CREATE_IF_MISSING |
false |
Create records that don't exist yet, using PROXIED and TTL (not proxied, automatic TTL if unset). When false, a missing record is logged as an error and skipped. |
DRY_RUN |
false |
Log what would change without changing anything. |
LOG_LEVEL |
info |
debug, info, warn or error. |
TZ |
UTC |
Time zone for log timestamps, such as Europe/London. |
NOTIFY_URL |
unset | A webhook to notify when records change or something goes wrong. Unset, nothing is sent. |
NOTIFY_URL_FILE |
Path to a file containing the webhook URL. Use instead of NOTIFY_URL, not as well. |
|
NOTIFY_FORMAT |
auto |
discord, slack, ntfy, gotify, apprise, json or text. auto recognises Discord and Slack webhook URLs; for anything else, say which. |
Booleans accept true/false, yes/no, on/off and 1/0. A
variable set to an empty value counts as unset, which is how Unraid passes
blank template fields.
These rarely need changing:
| Variable | Default | Description |
|---|---|---|
ZONE |
unset | Restricts RECORDS to one zone, such as example.com, and lets @ in RECORDS stand for it. Unset, each record's zone is found among the zones the token can see, and when two match, as with a subdomain zone such as sub.example.com, the longer one wins. Required before 1.1.0, so older setups have it, and they keep working as they are. |
IPV4_SOURCES |
https://1.1.1.1/cdn-cgi/trace,https://api.ipify.org,https://ifconfig.me/ip |
Comma-separated lookup URLs, tried in order. Each must answer with the bare address or with an ip= line. Use https:// URLs: plain http:// works but is warned about at startup, because anyone on the network path could answer with an address of their choosing. |
IPV6_SOURCES |
https://[2606:4700:4700::1111]/cdn-cgi/trace,https://api6.ipify.org |
The same, for IPv6. |
STATE_DIR |
/tmp/nofixedaddress |
Where the health check's timestamp is kept. |
CF_API_URL |
https://api.cloudflare.com/client/v4 |
Only for testing against a stand-in for the API. The token is sent to this URL, and the container warns at startup when it isn't the default. |
Pointing more names at it
The container only needs to manage one record. Point every other name at it with a CNAME, and they all follow it when your address changes:
| Name | Type | Content |
|---|---|---|
home.example.com |
A | Your public IP, kept current by No Fixed Address |
nas.example.com |
CNAME | home.example.com |
photos.example.com |
CNAME | home.example.com |
RECORDS=home.example.com then covers all of them, and adding a service
never means changing the container's settings.
Each name has its own proxy setting in Cloudflare (the orange or grey cloud), and a few rules decide which to use:
- A CNAME can be proxied even when its target isn't, but not the other way round. A DNS-only CNAME pointing at a proxied record still resolves to Cloudflare's addresses. If any name needs to be DNS-only, keep the managed record DNS-only (
PROXIED=false) and choose per CNAME. - Keep anything that isn't a website DNS-only, such as a VPN, game server or SSH. Cloudflare's proxy only carries HTTP and HTTPS.
- Large uploads and video usually need DNS-only. On the Free and Pro plans the proxy rejects requests over 100 MB, and Cloudflare's terms restrict serving video through it, which matters for photo and media servers such as Immich, Jellyfin or Plex.
- Keep proxied names one level deep. Cloudflare's free certificate covers
example.comand names likenas.example.com, but notnas.home.example.com. DNS-only names can be any depth, because your own certificate (for example from Let's Encrypt on a reverse proxy) is the one browsers see. - A DNS-only name shows your home IP to anyone who looks it up. Proxied names don't, as long as nothing else gives the address away.
Testing with DRY_RUN
DRY_RUN=true runs everything as normal (verifying the token, finding the
zones and records, detecting the address) but never writes to Cloudflare.
Changes it would have made are logged with a [dry-run] prefix:
2026-10-29T23:23:52-0500 info No Fixed Address 1.1.0 starting
2026-10-29T23:23:53-0500 info checking every 300s; proxied: as is, ttl: as is, create missing records: true
2026-10-29T23:23:53-0500 info dry run: changes are logged but not applied
2026-10-29T23:23:54-0500 info API token verified
2026-10-29T23:23:54-0500 info zone example.com: keeping A records for home.example.com, vpn.example.com pointed at this host
2026-10-29T23:23:55-0500 info [dry-run] home.example.com A 1.2.3.4 -> 5.6.7.8
2026-10-29T23:23:56-0500 info [dry-run] vpn.example.com A created with 5.6.7.8
2026-10-29T23:23:56-0500 info check complete (IPv4 5.6.7.8): 2 updated, 0 unchanged
Add LOG_LEVEL=debug to also see each API call and its HTTP status, the
address each lookup returned, and the records that were already correct. When
the output looks right, remove DRY_RUN and restart the container. Real
updates are logged the same way, without the prefix:
2026-10-30T07:02:11-0500 info home.example.com A 1.2.3.4 -> 5.6.7.8
Health check
The image has a Docker HEALTHCHECK that runs every five minutes. The
container is unhealthy when no check has succeeded since it started, or
when the last successful check is older than INTERVAL plus two hours. A
check counts as successful when every record was confirmed or updated, so
problems that need your attention, such as a missing record or missing
permissions, show up as unhealthy as well as in the log.
docker inspect --format '{{json .State.Health}}' nofixedaddress
The check writes a small timestamp file under /tmp. If you run the container
with a read-only root filesystem, mount a tmpfs there (--tmpfs /tmp), as the
Compose example does.
Notifications
Set NOTIFY_URL to a webhook and the container sends a short message when:
- records change: one message per check, listing the same lines as the log, such as
home.example.com A 1.2.3.4 -> 5.6.7.8. WithDRY_RUN=truethe lines start with[dry-run], so a dry run is also a test of your notifications. - something needs attention: a problem that retrying can't fix, such as a missing record or permission, is reported at once. Trouble that may pass, such as a network outage, is reported only once it has lasted two hours, which is when the health check turns unhealthy, so a brief outage never wakes you up. The container also sends a message before it stops, for example after Cloudflare rejects the token.
- it's working again: one message after a problem was reported, such as
working again (IPv4 5.6.7.8).
Nothing is sent on a normal start or when nothing has changed. Each message is sent once, with a 10-second limit, and a webhook that fails only produces a warning in the log: it never holds up an update.
| Service | NOTIFY_FORMAT |
Where the URL comes from |
|---|---|---|
| Discord | discord (or auto) |
Channel settings → Integrations → Webhooks → New Webhook → Copy Webhook URL |
| Slack | slack (or auto) |
An app with Incoming Webhooks turned on → Add New Webhook to Workspace |
| ntfy | ntfy |
Your topic, such as https://ntfy.sh/your-secret-topic or a server of your own. For a protected topic, add the login as https://user:password@ntfy.example.com/topic, or an access token as ?auth=... |
| Gotify | gotify |
https://gotify.example.com/message?token=..., with the token of an application created under Apps |
| Apprise API | apprise |
https://apprise.example.com/notify/KEY for a saved configuration. Apprise forwards to over 100 services, including email, Telegram, Pushover and Matrix |
| Anything else | json |
Receives {"title", "message", "event", "changes"}, where event is updated, failing or recovered and changes lists the changed records. Works with Home Assistant and n8n webhooks |
| Anything else | text |
Receives the message as plain text |
Webhook URLs contain a secret, so the URL is treated like the API token: it is
never logged (only its host is, such as notifications: discord via discord.com), and it reaches curl on standard input rather than its command
line. Like the token, it does show in docker inspect; use NOTIFY_URL_FILE
to avoid that. Use an https:// URL: plain http:// works but is warned
about, because anyone on the network path could read the messages and the
secret.
Troubleshooting
Start with the log (docker logs nofixedaddress, or the log icon on Unraid).
For more detail, restart with LOG_LEVEL=debug. The token and the
notification URL are never logged at any level.
Cloudflare rejected the API token
The token is wrong, incomplete, revoked or expired. Copy it again, check it
with the curl command in Creating the Cloudflare API token,
and make sure it's an API token rather than the Global API Key. The container
exits with status 1, since retrying can't fix this.
CF_ZONE is now called ZONE
Version 0.0.3 renamed CF_ZONE to ZONE and CF_RECORDS to RECORDS.
Rename both settings and start the container again; their values stay the
same. On Unraid, edit the container, select Edit next to Zone, change
Key to ZONE, do the same for Records with RECORDS, and apply.
Since 1.1.0 ZONE is optional, so you can also remove CF_ZONE instead of
renaming it.
RECORDS entry 'home' is not a full name like home.example.com
Records are full names. Use home.example.com, not home. With ZONE set,
the same mistake reads RECORDS entry 'home' is not in the zone example.com.
The container exits with status 2 until the setting is fixed.
RECORDS entry '@' needs ZONE
@ stands for the zone, so it only works with ZONE set. Without ZONE,
write the zone name in full, such as RECORDS=example.com.
no zone was found for home.example.com
None of the zones the token can see contains this name. Check the spelling, and that the token's Zone Resources include the zone it's in. The message ends with the zones the token can see. The container exits with status 1, since retrying can't fix this.
zone example.com was not found
With ZONE set, it must be the zone (example.com), not a record name
(home.example.com). The token also needs Zone / Zone / Read, and its
Zone Resources must include this zone.
... failed: HTTP 403 ...; the token needs Zone:DNS:Edit and Zone:Zone:Read
The token can see the zone but not change its records. Edit the token and add Zone / DNS / Edit.
home.example.com has no A record
The record doesn't exist yet. Create it in the Cloudflare dashboard with any
address (the next check corrects it), or set CREATE_IF_MISSING=true.
home.example.com has 2 A records; leaving them alone
There are several records with the same name and type, and there's no way to know which one to update. Delete the extras in the dashboard.
could not determine the public IPv6 address
The container has no IPv6 connectivity. Docker's default bridge network is
IPv4-only: use host networking (--network host, or Network Type: Host on
Unraid) or a network with IPv6 enabled. If you don't need AAAA records, set
IPV6=false; while either family fails, the check is retried with backoff and
the container reports unhealthy.
could not determine the public IPv4 address
None of the lookup services answered. Check that the container can make
outbound HTTPS requests (firewall rules, DNS, a proxy). Behind a proxy, set
the usual HTTPS_PROXY variable; curl picks it up. You can also point
IPV4_SOURCES at services you prefer, as long as they use https://.
TTL=300 has no effect
Cloudflare always uses automatic TTL for proxied records, so TTL only
applies to records that aren't proxied.
NOTIFY_FORMAT=auto only recognises Discord and Slack webhooks
auto can only tell Discord and Slack apart by their addresses. For any
other service, set NOTIFY_FORMAT to say which, as in the table under
Notifications.
notification to ntfy.example.com failed: HTTP 404
The webhook answered with an error. 404 usually means the URL is incomplete
or the webhook was deleted, and 401 or 403 that its token or password is
wrong. Notifications never affect updates, so the container carries on either
way. Check the URL, then test it with DRY_RUN=true, which sends a message on
the first check.
HTTP 429
Cloudflare is rate limiting the token. The container backs off and retries on
its own. If you see this often, increase INTERVAL or check whether other
tools share the same token.
The container keeps restarting
Exit status 2 means a setting is invalid, and 1 means Cloudflare rejected the token or a record is in no zone the token can see. The last lines of the log say which, and the restart policy keeps restarting it until the setting is fixed.
Security
- The token only needs DNS edit and zone read rights on the zones your records are in.
- The container runs as an unprivileged user (uid 10001), needs no capabilities, and works with a read-only root filesystem plus a tmpfs at
/tmp. - The token is never logged, and reaches curl on standard input rather than its command line. The script removes it from its own environment before it starts curl or jq. A token passed as
CF_API_TOKENstill stays in the container's configuration, wheredocker inspect,docker execsessions and the health check can see it. UseCF_API_TOKEN_FILEto avoid that. - Addresses from the lookup services are validated before use, and private, loopback and reserved ranges are refused.
- The notification URL gets the same care as the token: it's never logged, and reaches curl on standard input. Messages contain record names, addresses and error messages, never the token.
To report a vulnerability, see SECURITY.md.
Development
shellcheck src/nofixedaddress.sh tests/*.sh # lint
sh tests/run.sh # offline unit tests
sh tests/e2e.sh # end to end, against a local mock of the API
docker build -t nofixedaddress:dev .
NFA_IMAGE=nofixedaddress:dev sh tests/e2e.sh # the same tests against the image
The unit tests need jq, and the end-to-end tests curl, jq and python3.
They use the mock in tests/mock_cf_api.py instead of
the real API and real webhooks. See
CONTRIBUTING.md for more.
License
MIT, © 2026 Mike Richardson.
Cloudflare is a trademark of Cloudflare, Inc. This project is not affiliated with or endorsed by Cloudflare.
Requirements
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/mdr6273/no-fixed-address:latestRuntime arguments
- Network
bridge- Shell
sh- Privileged
- false
- Extra Params
--cap-drop=ALL --security-opt=no-new-privileges --read-only --tmpfs /tmp
Template configuration
A Cloudflare API token with Zone / Zone / Read and Zone / DNS / Edit permissions, limited to the zones your records are in. Create it under My Profile, API Tokens in the Cloudflare dashboard. The README has step-by-step instructions.
- Target
- CF_API_TOKEN
Comma-separated full names to keep updated, for example home.example.com,vpn.example.com (not just home). They can be in different domains: each name's zone is found automatically. One name is often enough: point your other names at it with CNAME records.
- Target
- RECORDS
How often to check whether the public IP has changed. 300 is every 5 minutes, 3600 is hourly, 86400 is once a day. A check always runs when the container starts.
- Target
- INTERVAL
- Default
- 300
- Value
- 300
Keep A records pointed at the public IPv4 address.
- Target
- IPV4
- Default
- true|false
- Value
- true
Keep AAAA records pointed at the public IPv6 address. The container needs IPv6 connectivity for this: use Host networking or a custom network with IPv6 enabled.
- Target
- IPV6
- Default
- false|true
- Value
- false
true or false to set Cloudflare's proxy on every record, or blank to leave each record as it is. Records used for VPNs, game servers and similar must not be proxied.
- Target
- PROXIED
Create records that do not exist yet. When false, a missing record is logged as an error and skipped.
- Target
- CREATE_IF_MISSING
- Default
- false|true
- Value
- false
Log what would change without changing anything. Useful for the first start.
- Target
- DRY_RUN
- Default
- false|true
- Value
- false
Optional webhook to tell you when records change, when something needs attention, and when it works again: a Discord or Slack webhook, an ntfy topic, a Gotify or Apprise API URL, or anything that accepts JSON or text. Blank turns notifications off. The README explains where each URL comes from.
- Target
- NOTIFY_URL
auto recognises Discord and Slack webhook URLs. For any other service, choose it here.
- Target
- NOTIFY_FORMAT
- Default
- auto|discord|slack|ntfy|gotify|apprise|json|text
- Value
- auto
1 for automatic, or 60 to 86400. Blank leaves each record's TTL as it is. Ignored for proxied records, which always use automatic TTL.
- Target
- TTL
debug also logs every check and API call, which helps when reporting a problem. The token and the notification URL are never logged.
- Target
- LOG_LEVEL
- Default
- info|debug|warn|error
- Value
- info
Alternative to the API token field: the path inside the container of a file holding the token, mapped in with a Path. Set one or the other, not both.
- Target
- CF_API_TOKEN_FILE
Comma-separated URLs used to find the public IPv4 address, tried in order. Use https:// URLs. Blank uses https://1.1.1.1/cdn-cgi/trace, https://api.ipify.org and https://ifconfig.me/ip.
- Target
- IPV4_SOURCES
Comma-separated URLs used to find the public IPv6 address, tried in order. Use https:// URLs. Blank uses https://[2606:4700:4700::1111]/cdn-cgi/trace and https://api6.ipify.org.
- Target
- IPV6_SOURCES
Leave blank. Only for testing against a stand-in for the Cloudflare API: the API token is sent to this URL.
- Target
- CF_API_URL
Leave blank. Where the health check's timestamp is kept; defaults to /tmp/nofixedaddress.
- Target
- STATE_DIR