Declaratively manage DNS zones, Pull Zones, and Edge Rules on bunny.net
Project description
Bunny.net DNS & Pull Zone Auto-Setup
A Python CLI tool for declaratively managing DNS zones, Pull Zones, and Edge Rules on bunny.net from a JSON configuration file.
Features
- Declarative configuration - Define your desired state in JSON, the tool syncs it to bunny.net
- DNS management - Create zones, manage A, AAAA, CNAME, TXT, MX, SRV, and other record types
- Pull Zone management - Create CDN pull zones with custom origins and regional pricing
- Hostname & SSL - Automatically add custom hostnames and provision free SSL certificates
- Edge Rules - Configure CDN edge rules with friendly action/trigger names
- Pull from bunny.net - Export existing infrastructure as a config JSON (
--sot bunny) - Dry-run mode - Preview changes before applying
- Domain isolation - Sync specific domains without affecting others
Installation
uv pip install bunny-dns-sync
Configuration
- Copy the example files:
cp .env.example .env
cp config.example.json config.json
- Add your bunny.net API key to
.env:
BUNNY_API_KEY=your-api-key-here
- Edit
config.jsonwith your domains and records.
Configuration Format
{
"domains": {
"example.com": {
"dns_records": [
{"type": "A", "name": "@", "value": "1.2.3.4", "ttl": 3600},
{"type": "AAAA", "name": "@", "value": "2606:50c0:8000::153", "ttl": 3600},
{"type": "CNAME", "name": "www", "value": "example.com", "ttl": 3600},
{"type": "MX", "name": "@", "value": "mail.example.com", "priority": 10, "ttl": 3600},
{"type": "TXT", "name": "@", "value": "v=spf1 include:_spf.google.com ~all", "ttl": 3600},
{"type": "SRV", "name": "_sip._tcp", "value": "sip.example.com", "priority": 10, "weight": 60, "port": 5060, "ttl": 3600}
],
"pull_zones": {
"my-cdn": {
"origin_url": "https://origin.example.com",
"origin_host_header": "origin.example.com",
"type": "standard",
"enabled_regions": ["EU", "US"],
"hostnames": ["cdn.example.com"],
"edge_rules": []
}
}
}
}
}
Supported DNS Record Types
| Type | Description |
|---|---|
| A | IPv4 address |
| AAAA | IPv6 address |
| CNAME | Canonical name |
| TXT | Text record |
| MX | Mail exchange (requires priority) |
| SRV | Service record (requires priority, weight, port) |
| CAA | Certificate Authority Authorization |
| NS | Name server |
| PTR | Pointer record |
Pull Zone Options
| Option | Description |
|---|---|
origin_url |
Origin server URL |
origin_host_header |
Host header sent to origin |
type |
standard or volume |
enabled_regions |
Array of: EU, US, ASIA, SA, AF |
hostnames |
Custom hostnames (SSL auto-provisioned) |
edge_rules |
Array of edge rule configurations |
Edge Rules
{
"edge_rules": [
{
"description": "CORS Headers",
"enabled": true,
"trigger_match": "any",
"triggers": [
{"type": "url", "match": "any", "patterns": ["*"]}
],
"actions": [
{"type": "set_response_header", "header": "Access-Control-Allow-Origin", "value": "*"}
]
}
]
}
Trigger types: url, url_extension, url_query_string, request_header, response_header, country_code, remote_ip, status_code, request_method, random_chance
Action types: set_response_header, set_request_header, redirect, block, force_ssl, override_cache_time, origin_url, force_download, disable_optimizer, set_status_code
Usage
Push (local → bunny.net)
# Sync a specific domain (recommended)
uv run bunny-dns -c config.json --domain example.com
# Dry run - preview changes without applying
uv run bunny-dns -c config.json --domain example.com --dry-run
# DNS only
uv run bunny-dns -c config.json --domain example.com --dns-only
# Pull zones only
uv run bunny-dns -c config.json --domain example.com --pullzones-only
# Additive mode - don't delete records not in config
uv run bunny-dns -c config.json --domain example.com --no-delete
# Sync all domains in config
uv run bunny-dns -c config.json
Pull (bunny.net → local)
Export your current bunny.net configuration as JSON — useful for bootstrapping a config from existing infrastructure or verifying drift.
# Pull a specific domain
uv run bunny-dns --sot bunny --domain example.com
# Pull all DNS zones on the account
uv run bunny-dns --sot bunny --all
# Write to file instead of stdout
uv run bunny-dns --sot bunny --domain example.com -o config.json
# Pull DNS only or pull zones only
uv run bunny-dns --sot bunny --domain example.com --dns-only
uv run bunny-dns --sot bunny --domain example.com --pullzones-only
CLI Options
| Option | Description |
|---|---|
-c, --config |
Path to JSON configuration file (required for push) |
-d, --domain |
Only sync/pull this specific domain |
-n, --dry-run |
Preview changes without applying (push only) |
--dns-only |
Only sync/pull DNS zones |
--pullzones-only |
Only sync/pull Pull Zones |
--no-delete |
Don't delete records not in config (push only) |
--sot |
Source of truth: local (default, push) or bunny (pull) |
--all |
Pull all DNS zones on the account (with --sot bunny) |
-o, --output |
Write pull output to file instead of stdout |
--api-key |
API key (defaults to BUNNY_API_KEY env var) |
Check Propagation Status
After updating nameservers, check if DNS has propagated:
# Basic check
uv run bunny-dns-check example.com
# Include pull zone hostname/SSL checks
uv run bunny-dns-check example.com -c config.json
Output:
NAMESERVERS: ✓ OK
Expected: coco.bunny.net, kiki.bunny.net
Current: kiki.bunny.net, coco.bunny.net
→ DNS is pointing to bunny.net
DNS RECORDS:
✓ A @ → 185.199.110.153, 185.199.111.153
✓ AAAA @ → 2606:50c0:8000::153, 2606:50c0:8003::153
✓ MX @ → 0 mail.example.com
PULL ZONE HOSTNAMES:
✓ cdn.example.com
CNAME: my-cdn.b-cdn.net
SSL: ok (200)
Workflow: Migrating to Bunny DNS
If you already have DNS elsewhere and want to move to bunny.net, you can bootstrap your config from your existing setup using pull, then push it:
# Export your current bunny.net state (if zone already exists)
uv run bunny-dns --sot bunny --domain example.com -o config.json
# Or create config.json manually, then:
uv run bunny-dns -c config.json --domain example.com --dry-run # preview
uv run bunny-dns -c config.json --domain example.com # apply
Then update nameservers at your registrar to kiki.bunny.net and coco.bunny.net, verify with uv run bunny-dns-check, and re-run sync to load SSL certificates. See the Fathom Analytics section below for a full step-by-step example.
Safety Features
- Domain isolation - Only touches domains explicitly in your config
- Dry-run mode - Preview all changes before applying
- No-delete mode - Additive only, never removes records
- Rate limit handling - Auto-retry with exponential backoff
- Explicit domain flag -
--domainensures you only sync what you intend
Development
git clone https://github.com/mrpesho/bunny-dns-sync.git
cd bunny-dns
uv pip install -e ".[dev]"
Testing
The project includes a comprehensive test suite with 99% code coverage.
# Run all tests
uv run pytest tests/ -v
# Run with coverage report
uv run pytest tests/ --cov=bunny_dns --cov-report=term-missing
# Run specific test file
uv run pytest tests/test_dns_manager.py -v
Test Structure
| File | Tests | Coverage |
|---|---|---|
test_bunny_client.py |
HTTP client, retries, exceptions | 100% |
test_dns_manager.py |
DNS records, normalization, sync | 100% |
test_pullzone_manager.py |
Pull zones, hostnames, regions | 99% |
test_edge_rules_manager.py |
Edge rules, action/trigger parsing | 100% |
test_sync.py |
Orchestrator, config loading | 99% |
Use Case: Fathom Analytics Proxy
Ad blockers block requests to known analytics domains like cdn.usefathom.com. By proxying Fathom Analytics through your own subdomain via Bunny CDN, requests come from ana.yourdomain.com instead — which blockers don't recognize.
1. Create your config
Create a config.json that sets up both the DNS CNAME record and a Bunny Pull Zone for your domain. Replace example.com with your actual domain and ana-example with a unique pull zone name:
{
"domains": {
"example.com": {
"dns_records": [
{"type": "CNAME", "name": "ana", "value": "ana-example.b-cdn.net", "ttl": 3600}
],
"pull_zones": {
"ana-example": {
"origin_url": "https://cdn.usefathom.com",
"origin_host_header": "cdn.usefathom.com",
"type": "standard",
"enabled_regions": ["EU", "US"],
"hostnames": ["ana.example.com"],
"edge_rules": []
}
}
}
}
}
This configures:
- A CNAME record pointing
ana.example.comto your Bunny pull zone - A Pull Zone that proxies requests to Fathom's CDN
- A custom hostname with automatic free SSL
2. Preview changes
uv run bunny-dns -c config.json --domain example.com --dry-run
3. Apply
uv run bunny-dns -c config.json --domain example.com
Note: If your domain's nameservers are not yet pointing to bunny.net, SSL certificate provisioning will fail on the first run. That's expected — it will succeed after you update nameservers.
4. Update nameservers (if not already on bunny.net)
At your domain registrar, set the nameservers to:
kiki.bunny.netcoco.bunny.net
5. Verify propagation
uv run bunny-dns-check example.com -c config.json
6. Re-run to load SSL certificates
uv run bunny-dns -c config.json --domain example.com
The tool detects hostnames missing certificates and loads them automatically.
7. Configure Fathom
In your Fathom dashboard, set ana.example.com as your custom domain. Update your tracking script to use it:
<script src="https://ana.example.com/script.js" data-site="YOUR_SITE_ID" defer></script>
License
MIT
Support
If you're new to bunny.net and find this tool useful, consider signing up through my affiliate link. It helps support continued development at no extra cost to you.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bunny_dns_sync-0.1.0.tar.gz.
File metadata
- Download URL: bunny_dns_sync-0.1.0.tar.gz
- Upload date:
- Size: 40.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54022891923115378e06227e8e0aa94420e99e31a928ae88102897191d582391
|
|
| MD5 |
1e520e7ba01c12e730dd0752d8d5e19b
|
|
| BLAKE2b-256 |
a6086ed34307eec88224848b328da9799599937000a15e916afd87c3f5837409
|
File details
Details for the file bunny_dns_sync-0.1.0-py3-none-any.whl.
File metadata
- Download URL: bunny_dns_sync-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
412daded1f8e2957cabcbba02888874aa9876c073c0b5bea9631bfe957a169a9
|
|
| MD5 |
1ccf0fe8510c1a70dbad57c73dddcc9b
|
|
| BLAKE2b-256 |
bb96733d332b52eaee6b0a2a11252642ebe8768440d4567c4d485e243ecf208b
|