This package is not related to or developed by Porkbun. No relationship between the developer of this package and Porkbun exists.
All trademarks, logos and brand names are the property of their respective owners. All company, product and service names used in this package are for identification purposes only. Use of these names,trademarks and brands does not imply endorsement.
porkbun-ddns is a unofficial DDNS-Client for Porkbun Domains.
This library will only update the records if the IP(s) have changed or the dns entry didn't exist before, it will also set/update A (IPv4) and AAAA (IPv6) records.
Version 2.0.0 introduces breaking changes to the public
PorkbunDDNSAPI and the CLI:
- Constructor:
PorkbunDDNS(config, domain, ...)->PorkbunDDNS(credentials, retry, domain, ...).Configis nowAppConfig(credentials, retry, webhook)— build it withCredentials,RetryPolicyandWebhookConfig, or load it viaextract_config.- CLI: the
--fritzbox/-fflag is removed. Pipefritzbox-ipsinto--public-ipsinstead:porkbun-ddns domain.com --public-ips "$(fritzbox-ips <fritzbox-ip>)".- Endpoint:
Credentials.endpointnow defaults to the real Porkbun endpoint (previously it raised when unset).See the full breakdown in PR #163.
Since porkbun-dynamic-dns-python is deprecated I took it into my own hands to code a decent DDNS Client for Porkbun. Inspired by con-f-use pull request, I built a pip Package and a docker container.
As alternative to cert-bun use my lego-certbot image.
Make sure that any domain you use this client with has API access enabled. See the below picture for reference.
If this is not enabled, you'll see an error about your API keys being invalid, despite them being correct.
Minimum required python version: 3.10
pip install porkbun-ddnsusage: porkbun-ddns [-h] [-c CONFIG] [-e ENDPOINT] [-pk APIKEY] [-sk SECRETAPIKEY]
[--retry-count RETRY_COUNT] [--retry-delay RETRY_DELAY]
[--webhook-url WEBHOOK_URL]
[--webhook-template WEBHOOK_TEMPLATE]
[--webhook-template-file WEBHOOK_TEMPLATE_FILE]
[--log-level LOG_LEVEL] [-i [PUBLIC_IPS ...]] [-4 | -6] [-v]
[--env_only]
domain [subdomains ...]
positional arguments:
domain Domain to be updated
subdomains Subdomain(s)
options:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Path to config file (default:
~/.config/porkbun-ddns-config.json)
-e ENDPOINT, --endpoint ENDPOINT
The endpoint
-pk APIKEY, --apikey APIKEY
The Porkbun-API-key
-sk SECRETAPIKEY, --secretapikey SECRETAPIKEY
The secret API-key
--retry-count RETRY_COUNT
Number of attempts for transient API failures
--retry-delay RETRY_DELAY
Seconds to wait between retry attempts
--webhook-url WEBHOOK_URL
Webhook URL to notify when IPs change
--webhook-template WEBHOOK_TEMPLATE
Jinja2 template for the webhook payload
--webhook-template-file WEBHOOK_TEMPLATE_FILE
Path to a file containing the Jinja2 webhook template
(takes precedence over --webhook-template)
--log-level LOG_LEVEL
Set log verbosity (DEBUG, INFO, WARNING, ERROR,
CRITICAL)
-i [PUBLIC_IPS ...], --public-ips [PUBLIC_IPS ...]
Public IPs (v4 and or v6)
-4, --ipv4-only Only set/update IPv4 A Records
-6, --ipv6-only Only set/update IPv6 AAAA Records
-v, --verbose Show Debug Output
--env_only Don't use any config, get all variables from the
environmentThese parameter are required for each run of the program. The program will take the values for these (in this order) from:
- The command-line-arguments (
-pk pk1_xxx) - The environment-variables (
export PORKBUN_APIKEY='pk1_xxx') - The config-file (
apikey="pk_xxx")
In Docker use the API_ENDPOINT environment-variable instead.
So if a value is set through the CLI and in the file, the CLI-value will be used. This allows for a default-configuration in the config-file, whose settings can be selectively overridden through enviromnment-variables or CLI-arguments.
Transient API failures (unreachable endpoint, timeouts, HTTP 5xx) are retried automatically, HTTP 4xx errors (e.g. invalid API keys) fail immediately. Default is 3 attempts with a 5 seconds delay between them.
The program will take the values for these (in this order) from:
- The command-line-arguments (
--retry-count 3) - The environment-variables (
export PORKBUN_RETRY_COUNT='3') - The config-file (
retry_count="3")
When the IP(s) of your records change, an aggregated webhook-notification can be POSTed to a URL of your choice. This works out of the box with Slack, MS Teams, Mattermost and Google Chat.
The program will take the values for these (in this order) from:
- The command-line-arguments (
--webhook-url 'https://...') - The environment-variables (
export PORKBUN_WEBHOOK_URL='https://...') - The config-file (
webhook_url="https://...")
In Docker use the WEBHOOK_URL, WEBHOOK_TEMPLATE and WEBHOOK_TEMPLATE_FILE environment-variables instead.
The payload can be customized with an inline Jinja2-template (--webhook-template) or a template-file (--webhook-template-file), where the file takes precedence over the inline one. If neither is set, the following Slack-compatible default is used:
{"text": "IP changed: {{ old_ips | join(', ') }} -> {{ new_ips | join(', ') }} ({{ domain }})"}The following context-variables are available in templates: changes (list of changes, each {record_type, fqdn, old_ip|None, new_ip}), old_ips (previous IPs), new_ips (new IPs), domain (the updated domain) and timestamp (ISO-8601 UTC timestamp of the notification).
One notification is sent per run, after all records have been updated. Notifications are fire-and-forget: a failure to deliver never crashes the update-loop.
Controls the verbosity of the logs. Accepts standard logging level names, case-insensitively: DEBUG, INFO, WARNING, ERROR, CRITICAL (default INFO). Set it via --log-level WARNING on the CLI or LOG_LEVEL=WARNING in Docker.
When both the legacy --verbose/DEBUG and LOG_LEVEL are set, LOG_LEVEL wins. An invalid value logs a warning and falls back to INFO, it never crashes.
# using the default config-file in ~/.config/porkbun-ddns-config.json:
$ porkbun-ddns domain.com my_subdomain
# Using only environment variables:
# PORKBUN_APIKEY
# PORKBUN_SECRETAPIKEY
# PORKBUN_ENDPOINT (Optional)
$ porkbun-ddns domain.com my_subdomain --env_only
# Specific config-file:
$ porkbun-ddns domain.com my_subdomain -c "./config.json"
# Multiple subdomains:
$ porkbun-ddns domain.com my_subdomain_1 my_subdomain_2 my_subdomain_3
# Set root and subdomains:
$ porkbun-ddns domain.com @ my_subdomain_1 my_subdomain_2 my_subdomain_3
# Set wildcard domain:
$ porkbun-ddns domain.com '*'
# Set IP's explicit
$ porkbun-ddns domain.com my_subdomain -i '1.2.3.4' '1234:abcd:0:4567::8900'
# Use Fritz!Box to obtain IP's (via fritzbox-ips sidecar) and set IPv4 A Record only
$ porkbun-ddns domain.com my_subdomain --public-ips "$(fritzbox-ips fritz.box)" -4You can set up a cron job get the full path to porkbun-ddns with which porkbun-ddns, then execute crontab -e and add the following line:
*/30 * * * * <PORKBUN-DDNS-PATH>/porkbun-ddns "<YOUR-PATH>/config.json" domain.com my.subdomain >/dev/null 2>&1
config.json example:
{
"endpoint":"https://api.porkbun.com/api/json/v3",
"apikey": "pk1_xxx",
"secretapikey": "sk1_xxx",
"retry_count": "3",
"retry_delay": "5",
"webhook_url": "https://hooks.slack.com/services/..."
}
services:
porkbun-ddns:
image: "mietzen/porkbun-ddns:latest"
container_name: porkbun-ddns
environment:
DOMAIN: "domain.com" # Your Porkbun domain
SUBDOMAINS: "my_subdomain,my_other_subdomain,my_subsubdomain.my_subdomain" # Subdomains comma spreaded
SECRETAPIKEY: "<YOUR-SECRETAPIKEY>" # Your Porkbun Secret-API-Key
APIKEY: "<YOUR-APIKEY>" # Your Porkbun API-Key
# API_ENDPOINT: "https://api.porkbun.com/api/json/v3" # Override the Porkbun API endpoint (e.g. a mirror/proxy)
# PUBLIC_IPS: "1.2.3.4,2001:043e::1" # Set if you got static IP's (wins over FRITZBOX)
# FRITZBOX: "192.168.178.1" # Use Fritz!BOX to obtain Public IP's (queried directly by the entrypoint)
# SLEEP: "300" # Seconds to sleep between DynDNS runs
# IPV4: "TRUE" # Set IPv4 address
# IPV6: "TRUE" # Set IPv6 address
# DEBUG: "FALSE" # DEBUG LOGGING
# LOG_LEVEL: "WARNING" # Set log verbosity (DEBUG, INFO, WARNING, ERROR, CRITICAL)
# RETRY_COUNT: "3" # Number of attempts for transient API failures
# RETRY_DELAY: "5" # Seconds to wait between retry attempts
# WEBHOOK_URL: "https://hooks.slack.com/services/..." # POST an IP-change notification to this URL (Slack, MS Teams, Mattermost, Google Chat compatible by default)
# WEBHOOK_TEMPLATE: '{"text": "IP changed: {{ old_ips | join(", ") }} -> {{ new_ips | join(", ") }} ({{ domain }})"}' # Optional custom Jinja2 template
# WEBHOOK_TEMPLATE_FILE: "/path/to/template.j2" # Optional Jinja2 template file (takes precedence over WEBHOOK_TEMPLATE)
restart: unless-stopped
# # Uncomment below to let it detect ipv6 address:
# networks:
# - ipv6_enabled
# networks:
# ipv6_enabled:
# enable_ipv6: true
docker run -d \
-e DOMAIN="domain.com" \
-e SUBDOMAINS="my_subdomain,my_other_subdomain,my_subsubdomain.my_subdomain" \
-e SECRETAPIKEY="<YOUR-SECRETAPIKEY>" \
-e APIKEY="<YOUR-APIKEY>" \
-e LOG_LEVEL="WARNING" \
-e WEBHOOK_URL="https://hooks.slack.com/services/..." \
--name porkbun-ddns \
--restart unless-stopped \
mietzen/porkbun-ddns:latestMinimum required python version: 3.10
from pathlib import Path
from porkbun_ddns import PorkbunDDNS
from porkbun_ddns.config import AppConfig, Credentials, RetryPolicy, WebhookConfig, DEFAULT_ENDPOINT, extract_config
# Build config directly:
app = AppConfig(
credentials=Credentials(apikey="YOUR-APIKEY", secretapikey="YOUR-SECRETAPIKEY", endpoint=DEFAULT_ENDPOINT),
retry=RetryPolicy(), # defaults: retry_count=3, retry_delay=5
webhook=WebhookConfig(),
)
porkbun_ddns = PorkbunDDNS(app.credentials, app.retry, 'domain.com')
# Or load from config file:
# app = extract_config(Path("./config.json"))
# porkbun_ddns = PorkbunDDNS(app.credentials, app.retry, 'domain.com')
# With static IPs:
# porkbun_ddns = PorkbunDDNS(app.credentials, app.retry, 'domain.com', public_ips=['1.2.3.4', '1234:abcd:0:4567::8900'])
# With Fritz!Box (via fritzbox-ips sidecar CLI):
# porkbun-ddns domain.com --public-ips "$(fritzbox-ips fritz.box)"
porkbun_ddns.set_subdomain('my_subdomain')
porkbun_ddns.update_records()