A minimalistic DNS management utility for backing up and migrating DNS records across multiple providers. The idea is to get YAML from all providers, and then use it as ground truth with Terraform.
A fast, minimal DNS utility that backs up your DNS records from multiple providers to YAML. Because losing DNS records is no fun. π±
- π Multi-provider support:
- βοΈ Cloudflare
- 𦩠Gandi
- π’ GoDaddy
- π· Porkbun
- π€ PRs welcome for more!
- π¦ Export all providers with one command
- π₯ Import domains to Cloudflare (migrate from other providers)
- π Move domains between providers with one command
- π List domains in any provider
- π YAML output - easy to read, diff, and version control
- π Environment variable config - no config files to leak
- π Handles pagination for large accounts
- π― Machine-parseable output for scripting
go install github.com/wkoszek/dnsx/cmd/dnsx@latest
cd dnsx
# Build the binary
make build
# Install to ~/bin
make install
# Or install to a custom location
cp dnsx /usr/local/bin/# Export from a specific provider
dnsx export cloudflare
dnsx export gandi
dnsx export godaddy
dnsx export porkbun
# Export from all configured providers
dnsx export all
# Custom output directory
dnsx export cloudflare --outdir /tmp/dns-backup
dnsx export all --outdir ./backups
# Import domains to Cloudflare (creates zones)
dnsx import example.com
dnsx import domain1.com domain2.com domain3.com
# Generate Terraform from YAML
dnsx gen terraform terraform/
# Show help
dnsx --helpList all domains in a provider:
# List domains in Porkbun
dnsx ls porkbun
# List domains in Cloudflare
dnsx ls cloudflare
# List domains in Gandi
dnsx ls gandi
# With 1Password
op run --env-file=.env.1password -- dnsx ls porkbunThe mv command migrates a domain from one provider to another. It creates the zone in the destination provider and updates nameservers at the source registrar:
# Move a domain from Porkbun to Cloudflare
dnsx mv porkbun/example.com cloudflare/
# Move a domain from Gandi to Cloudflare
dnsx mv gandi/example.com cloudflare/
# With 1Password
op run --env-file=.env.1password -- dnsx mv porkbun/example.com cloudflare/This will:
- Create a new zone in Cloudflare for the domain
- Get the assigned Cloudflare nameservers
- Update nameservers at the source registrar (Porkbun or Gandi)
- Print YAML for adding to your configuration
The set ns command updates nameservers at your domain registrar (supports Gandi and Porkbun):
# Set nameservers for a domain at Gandi
dnsx set ns freebsd.io adam.ns.cloudflare.com bella.ns.cloudflare.com
# Set nameservers at Porkbun
dnsx set ns --registrar porkbun freebsd.io adam.ns.cloudflare.com bella.ns.cloudflare.com
# With 1Password
op run --env-file=.env.1password -- dnsx set ns freebsd.io adam.ns.cloudflare.com bella.ns.cloudflare.comThis is useful when migrating to Cloudflare - first add the domain in Cloudflare's dashboard, get the assigned nameservers, then use this command to point your domain to them.
The import command creates new zones in Cloudflare for domains currently hosted elsewhere:
# Import a single domain
dnsx import freebsd.io
# Import multiple domains at once
dnsx import freebsd.io lnkr.xyz example.com
# With 1Password
op run --env-file=.env.1password -- dnsx import freebsd.io lnkr.xyzThis will:
- Create a new zone in Cloudflare for each domain
- Enable
jump_startto auto-scan existing DNS records - Return the Cloudflare nameservers you need to set at your registrar
Example output:
β freebsd.io created
Zone ID: abc123def456
Nameservers:
- adam.ns.cloudflare.com
- bella.ns.cloudflare.com
Next steps:
1. Update nameservers at your registrar to the ones shown above
2. Wait for DNS propagation (up to 24-48 hours)
3. Run 'dnsx export cloudflare' to sync YAML files
All configuration is done via environment variables. Set the appropriate variables for the providers you want to use:
export CLOUDFLARE_API_TOKEN="your-api-token"
export CLOUDFLARE_ACCOUNT_ID="your-account-id" # optionalYou can also use Terraform variable names:
export TF_VAR_cloudflare_api_token="your-api-token"
export TF_VAR_cloudflare_account_id="your-account-id"Get your API token at: https://dash.cloudflare.com/profile/api-tokens
export GANDI_API_KEY="your-api-key"Get your API key at: https://account.gandi.net/en/users/USER/security
export GODADDY_API_KEY="your-api-key"
export GODADDY_API_SECRET="your-api-secret"Get your API credentials at: https://developer.godaddy.com/keys
export PORKBUN_API_KEY="your-api-key"
export PORKBUN_SECRET_KEY="your-secret-key"Get your API credentials at: https://porkbun.com/account/api
brew install --cask 1password-cli- Open 1Password
- Create items for each provider (e.g., "Cloudflare API", "Gandi API")
- Add your API keys/tokens as fields
- Save
- Right-click on any field in 1Password
- Select "Copy Secret Reference"
- It will look like:
op://Private/Cloudflare API/api_token
Edit the example file:
vim .env.1passwordRun dnsx with 1Password:
# The op CLI injects secrets as environment variables
op run --env-file=.env.1password -- dnsx export all
# Or for a specific provider
op run --env-file=.env.1password -- dnsx export cloudflareRecords are exported to YAML files, one file per domain:
domain: example.com
zone_id: abc123 # Cloudflare only
records:
- name: "@"
type: A
value: 192.0.2.1
ttl: 3600
- name: www
type: CNAME
value: example.com
ttl: 3600
- name: "@"
type: MX
value: mail.example.com
ttl: 3600
priority: 10
metadata:
provider: cloudflare
exported_at: 2025-11-27T12:00:00Z
account_id: your-account-id # if available# Build for current platform
make build
# Build for all platforms
make release
# Format code
make fmt
# Run vet
make vet
# Clean build artifacts
make cleandnsx/
βββ cmd/dnsx/ # CLI entry point
β βββ main.go
βββ internal/
β βββ exporter/ # Export from providers
β β βββ exporter.go
β β βββ cloudflare.go
β β βββ gandi.go
β β βββ godaddy.go
β β βββ porkbun.go
β βββ importer/ # Import to providers
β β βββ cloudflare.go
β βββ registrar/ # Registrar operations (set NS)
β β βββ registrar.go
β β βββ gandi.go
β β βββ porkbun.go
β βββ generator/ # Terraform generation
β β βββ terraform.go
β βββ output/ # YAML output handling
β βββ yaml.go
βββ go.mod
βββ Makefile
βββ README.md
To add a new DNS provider:
- Create a new file in
internal/exporter/(e.g.,route53.go) - Implement the
Exporterinterface:type Exporter interface { Name() string IsConfigured() bool Export(ctx context.Context) ([]DomainData, error) }
- Add the exporter to the map in
cmd/dnsx/main.go - Update the help text and README
#!/bin/bash
# backup-dns.sh
BACKUP_DIR="/backups/dns/$(date +%Y-%m-%d)"
dnsx export all --outdir "$BACKUP_DIR"
# Commit to git
cd "$BACKUP_DIR"
git add .
git commit -m "DNS backup $(date +%Y-%m-%d)"
git push# Export current state
dnsx export cloudflare --outdir current
# Compare with previous backup
diff -ur previous/ current/# Option 1: One-command migration (recommended)
# Moves domain from Porkbun to Cloudflare, updates NS automatically
dnsx mv porkbun/freebsd.io cloudflare/
# Option 2: Manual step-by-step migration
# 1. Import domain to Cloudflare (creates zone, shows assigned nameservers)
dnsx import freebsd.io
# 2. Update nameservers at your registrar via API
dnsx set ns --registrar porkbun freebsd.io adam.ns.cloudflare.com bella.ns.cloudflare.com
# 3. Wait for propagation, then export from Cloudflare
dnsx export cloudflare --outdir yaml/
# 4. Generate Terraform and apply
dnsx gen terraform terraform/- Never commit API keys to version control
- Use environment variables or secret management tools
- The tool doesn't store or cache credentials
- API keys are only used for the duration of the export
- Output directory permissions are set to
0750(owner read/write/execute only)
MIT License - See LICENSE file for details
Contributions welcome! Please feel free to submit a Pull Request.
Created by Adam Koszek [email protected]