12 KiB
Porkbun Dynamic DNS Update Script
A bash script for automatically updating DNS A records on Porkbun with your current public IP address. Supports multi-machine failover configurations where multiple servers can independently update their own A records for the same domain.
Features
- ✅ Automatic IP Detection - Uses Porkbun's API and multiple fallback methods
- ✅ Multi-Machine Failover - Multiple machines can maintain A records for the same domain
- ✅ Machine Identification - Uses notes field to track which machine owns each record
- ✅ Secure - API credentials stored in
.envfile (excluded from git) - ✅ Error Handling - Comprehensive validation and error messages
- ✅ Wildcard Support - Updates both root and wildcard DNS records
Requirements
bashshellcurl- for API callsjq- for JSON parsing- Porkbun API access enabled for your domain
Installation
-
Clone or download this script to your machine
-
Make the script executable:
chmod +x updateDNS.sh -
Copy the
.envfile and configure it (see Configuration below)
Configuration
Edit the .env file with your settings:
# Required: Your Porkbun API credentials
API_KEY=pk1_your_api_key_here
SECRET_KEY=sk1_your_secret_key_here
# Required: The domain to update
DOMAIN=example.com
# Required: Name of the machine for tracking
MACHINE_ID="exmample_machine"
Getting Porkbun API Credentials
- Log in to your Porkbun account
- Navigate to Account > API Access
- Generate API keys for your domain
- Important: Enable API access for your specific domain in the domain settings
Usage
Single Machine (Simple Dynamic DNS)
Basic configuration on one machine:
# .env
API_KEY=pk1_xxxxx
SECRET_KEY=sk1_xxxxx
DOMAIN=example.com
MACHINE_ID=home-server
Run the script:
./updateDNS.sh
This will update:
example.com→ Your current public IP*.example.com→ Your current public IP
Multi-Machine Failover Setup
Configure multiple machines with the same domain but different MACHINE_IDs:
Server 1:
# .env
API_KEY=pk1_xxxxx
SECRET_KEY=sk1_xxxxx
DOMAIN=example.com
MACHINE_ID=server1
Server 2:
# .env
API_KEY=pk1_xxxxx
SECRET_KEY=sk1_xxxxx
DOMAIN=example.com
MACHINE_ID=server2
Server 3:
# .env
API_KEY=pk1_xxxxx
SECRET_KEY=sk1_xxxxx
DOMAIN=example.com
MACHINE_ID=backup-server
Each machine runs the same script independently. The result will be:
example.com. 300 IN A 203.0.113.10 (server1's IP)
example.com. 300 IN A 198.51.100.20 (server2's IP)
example.com. 300 IN A 192.0.2.30 (backup-server's IP)
DNS clients will receive all three IPs, providing:
- Automatic failover - if one server is down, clients use others
- Load distribution - traffic is distributed across servers
- Independent updates - each machine manages only its own record
Managed records and notes tagging
The script only manages records that it created itself, identified via the DNS notes field.
-
Internally it uses a managed-notes prefix:
NOTES_PREFIX="ddns:" -
For each machine, the script sets the notes value to:
ddns:<MACHINE_ID> -
When cleaning up or checking for duplicates, it only looks at records where:
namematches the FQDN being updated (e.g.example.comor*.example.com), andtypeisA,ALIAS, orCNAME, andnotes == "ddns:<MACHINE_ID>"
This means:
- Manually managed records without this prefix are left untouched.
- Multiple machines (different
MACHINE_IDs) can safely coexist on the same domain. - If you ever want to manually take control of a record that was created by this script,
you can remove or change the
ddns:<MACHINE_ID>note and the script will stop managing it.
Dry-run mode
You can run the script in dry-run mode to see what it would do without actually creating or deleting any DNS records.
Set the DRY_RUN environment variable to 1 when invoking the script:
DRY_RUN=1 ./updateDNS.sh
In dry-run mode:
- The script still loads configuration and detects your public IP.
- It still retrieves and displays existing DNS records.
- It logs which records would be deleted and what would be created.
- It does not call Porkbun's
dns/deleteordns/createendpoints.
This is useful when first configuring the script or making changes to your DNS setup, so you can verify behavior before applying it.
Systemd integration helpers
This repository also includes helper scripts to install the updater as a systemd service, uninstall it cleanly, and view logs more easily.
install.sh – install systemd timer + service
install.sh sets up a porkbun-ddns systemd service and timer that will run
updateDNS.sh for you on a schedule (default: every 5 minutes).
What it does:
- Verifies required files exist in the current directory:
updateDNS.sh.envporkbun-ddns.serviceporkbun-ddns.timer
- Ensures
updateDNS.shis executable. - Checks for required dependencies:
curl,jq,systemctl. - Validates that
.envcontainsAPI_KEY,SECRET_KEY,DOMAIN, andMACHINE_ID. - Runs
updateDNS.shonce as a test (with your current config). - Installs and enables:
/etc/systemd/system/porkbun-ddns.service/etc/systemd/system/porkbun-ddns.timer
- Starts the timer and shows its status.
Usage:
cd /path/to/porkbun
./install.sh
Notes:
- Do not run
install.shdirectly as root; it will usesudowhen needed. - The service runs as the current user (it rewrites
User=andGroup=in the service file to match who raninstall.sh).
After installation, some useful commands:
systemctl status porkbun-ddns.timer
systemctl status porkbun-ddns.service
journalctl -u porkbun-ddns.service -n 50
uninstall.sh – remove systemd timer + service
uninstall.sh cleanly removes the porkbun-ddns systemd units while leaving your
scripts and configuration files intact.
What it does:
- Stops
porkbun-ddns.timerandporkbun-ddns.serviceif they are running. - Disables the timer so it no longer starts at boot.
- Removes
/etc/systemd/system/porkbun-ddns.serviceand/etc/systemd/system/porkbun-ddns.timer. - Reloads systemd and resets any failed state for these units.
Usage:
cd /path/to/porkbun
./uninstall.sh
Notes:
- Do not run
uninstall.shas root; it will usesudofor systemd operations. - Your local files remain:
updateDNS.sh.envporkbun-ddns.serviceporkbun-ddns.timerso you can reinstall later with./install.sh.
logs.sh – convenient log viewer
logs.sh is a small wrapper around journalctl and systemctl to make it easier
to inspect the porkbun-ddns.service logs and status.
Basic usage (show last 50 lines):
cd /path/to/porkbun
./logs.sh
Common options:
-
Follow logs in real-time (like
tail -f):./logs.sh -f -
Show last N lines:
./logs.sh -n 100 -
Show logs from today only:
./logs.sh -t -
Show logs from the last hour:
./logs.sh -h -
Show only error messages:
./logs.sh -e -
Show service and timer status plus recent runs:
./logs.sh -s
Run ./logs.sh --help for the full list of options.
How It Works
- Load Configuration - Reads API credentials and settings from
.env - Detect Public IP - Uses Porkbun's
/pingendpoint or fallback services (api.ipify.org, icanhazip.com) - Retrieve DNS Records - Gets all existing DNS records for the domain and displays them
- Check for Duplicates - Detects if multiple records exist for this machine ID
- Skip if Unchanged - If IP matches and exactly one record exists, skips update
- Delete Own Records - Removes this machine's previous A records (by matching notes field)
- Verify Cleanup - Ensures all old records were deleted before creating new ones
- Create New Record - Adds fresh A record with current IP and machine ID in notes
- Verify Creation - Confirms exactly one record exists for this machine
Note: The script only deletes and verifies records that it manages (those whose notes are exactly
ddns:<MACHINE_ID>). Other records in the same zone are left untouched.
Security Best Practices
Protect Your API Keys
-
Never commit
.envto version control:echo "*.env" >> .gitignore echo ".env" >> .gitignore -
Rotate your API keys regularly in the Porkbun dashboard
-
Use restrictive file permissions:
chmod 600 .env -
Use different API keys for development/production if possible
If You Accidentally Exposed Keys
- Immediately revoke the API keys in your Porkbun account
- Generate new keys and update your
.envfile
Troubleshooting
Error: "API access not enabled"
- Log in to Porkbun
- Go to your domain settings
- Enable API access for the specific domain
- Wait a few minutes for changes to propagate
Error: "Failed to create DNS record"
- Check that API credentials are correct
- Verify API access is enabled for your domain
- Ensure MACHINE_ID doesn't contain special characters
- Check that you're not exceeding Porkbun's rate limits
Multiple machines overwriting each other
- Make sure each machine has a unique MACHINE_ID
- Verify each machine is using the latest version of the script
- Check DNS records in Porkbun dashboard to see notes field (look for
ddns:<MACHINE_ID>)
IP not updating
- Check script is running
- Verify your public IP actually changed
- Check Porkbun API status
- Review logs for error messages
API Rate Limits
Porkbun's API has rate limits. For dynamic DNS:
- Recommended: Run every 5-15 minutes
- Don't: Run more than once per minute
- The script only makes API calls when it runs, not continuously
Example Output
Example output with the new structured logging format:
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Using environment file: /home/user/porkbun/.env
IP source: Porkbun API (/ping)
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Current Public IP: 45.73.134.145
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Machine ID: EXAMPLE_MACHINE
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Domain: example.com
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Processing DNS record for: example.com (IP: 45.73.134.145)
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Machine ID: EXAMPLE_MACHINE
Mon Nov 24 12:04:39 PM EST 2025 [INFO] Existing records for FQDN 'example.com':
Type: A, Name: example.com, Content: 45.73.134.145, Notes: ddns:EXAMPLE_MACHINE, Managed: true, ID: 508029248
Type: NS, Name: example.com, Content: curitiba.porkbun.com, Notes: none, Managed: false, ID: 506591857
Type: NS, Name: example.com, Content: fortaleza.porkbun.com, Notes: none, Managed: false, ID: 506591856
Type: NS, Name: example.com, Content: maceio.porkbun.com, Notes: none, Managed: false, ID: 506591854
Type: NS, Name: example.com, Content: salvador.porkbun.com, Notes: none, Managed: false, ID: 506591855
Mon Nov 24 12:04:39 PM EST 2025 [INFO] Record already exists with correct IP (45.73.134.145). No update needed.
Mon Nov 24 12:04:39 PM EST 2025 [INFO] Processing DNS record for: *.example.com (IP: 45.73.134.145)
Mon Nov 24 12:04:39 PM EST 2025 [INFO] Machine ID: EXAMPLE_MACHINE
Mon Nov 24 12:04:40 PM EST 2025 [INFO] Existing records for FQDN '*.example.com':
Type: A, Name: *.example.com, Content: 45.73.134.145, Notes: ddns:EXAMPLE_MACHINE, Managed: true, ID: 508029259
Mon Nov 24 12:04:40 PM EST 2025 [INFO] Record already exists with correct IP (45.73.134.145). No update needed.
Mon Nov 24 12:04:40 PM EST 2025 [INFO] DNS update completed successfully
Files
updateDNS.sh- Main script.env- Configuration file (contains secrets, DO NOT COMMIT)README.md- This file
License
This script is provided as-is for use with Porkbun DNS services.
Contributing
Improvements welcome! Consider adding:
- IPv6 support (AAAA records)
- Email notifications on IP change
- Health check endpoint