Nebula-sync - Pi-hole Configuration Synchronization
When running multiple Pi-hole instances in a homelab environment, keeping configurations synchronized becomes crucial for maintaining consistent DNS behavior across your network. Nebula-sync provides an elegant solution for automatically synchronizing Pi-hole configurations between primary and replica instances.
Nebula-sync is a lightweight tool that uses Pi-hole’s API to replicate settings, blocklists, and custom DNS records between multiple Pi-hole deployments. This ensures that changes made to your primary Pi-hole instance are automatically propagated to your backup instances.
Overview
The synchronization setup works as follows:
Primary Pi-hole (192.168.7.250)
↓
Nebula-sync Container
↓
Replica Pi-hole (192.168.7.251)
Nebula-sync runs on a scheduled basis and synchronizes specific configuration elements from the primary to replica instances, ensuring consistency without affecting read-only configurations that are managed by Docker environment variables.
Why Nebula-sync?
High Availability: Ensures backup Pi-hole instances have identical configurations, making failover seamless.
Automation: Eliminates manual configuration replication across multiple instances.
Selective Sync: Allows fine-grained control over which settings are synchronized.
API-Based: Uses Pi-hole’s built-in API for reliable configuration management.
Lightweight: Minimal resource usage with configurable scheduling.
Configuration Strategy
Since some Pi-hole settings are managed through Docker environment variables (which are read-only via API), nebula-sync focuses on synchronizing the dynamic configurations:
- Custom DNS host records
- CNAME records
- Blocklists and allowlists
- Domain lists and group assignments
- Ad list configurations
Static configurations like upstream DNS servers, interface bindings, and network settings remain managed through individual container configurations.
Docker Compose Setup
docker-compose.yml
---
services:
nebula-sync:
image: ghcr.io/lovelaze/nebula-sync:latest
container_name: nebula-sync
restart: unless-stopped
env_file: .env
Environment Configuration
Create .env file with synchronization settings:
# Primary Pi-hole instance (source)
PRIMARY="http://192.168.7.250|"
# Replica Pi-hole instances (destinations)
REPLICAS="http://192.168.7.251|"
# Synchronization behavior
FULL_SYNC=false
RUN_GRAVITY=false
CRON=*/15 * * * *
CLIENT_SKIP_TLS_VERIFICATION=true
TZ=Europe/Berlin
# DNS-specific synchronization
SYNC_CONFIG_DNS=true
# Specify exactly what to sync to avoid read-only key conflicts
SYNC_CONFIG_DNS_INCLUDE=hosts,cnameRecords
# Disable other config sync to avoid conflicts
SYNC_CONFIG_DHCP=false
SYNC_CONFIG_NTP=false
SYNC_CONFIG_RESOLVER=false
SYNC_CONFIG_DATABASE=false
SYNC_CONFIG_MISC=false
SYNC_CONFIG_DEBUG=false
# Gravity database synchronization
SYNC_GRAVITY_DHCP_LEASES=false
SYNC_GRAVITY_GROUP=false
SYNC_GRAVITY_AD_LIST=true
SYNC_GRAVITY_AD_LIST_BY_GROUP=true
SYNC_GRAVITY_DOMAIN_LIST=true
SYNC_GRAVITY_DOMAIN_LIST_BY_GROUP=true
SYNC_GRAVITY_CLIENT=false
SYNC_GRAVITY_CLIENT_BY_GROUP=false
Configuration Parameters
Connection Settings
PRIMARY: The source Pi-hole instance URL and optional API token.
- Format:
"http://ip-address|api-token" - Example:
"http://192.168.7.250|abc123"
REPLICAS: Comma-separated list of destination Pi-hole instances.
- Format:
"http://ip1|token1,http://ip2|token2" - Multiple replicas:
"http://192.168.7.251|,http://192.168.7.252|"
Scheduling
CRON: Standard cron expression for sync frequency.
- Every 15 minutes:
*/15 * * * * - Hourly:
0 * * * * - Daily at 2 AM:
0 2 * * *
Sync Scope Control
SYNC_CONFIG_DNS_INCLUDE: Specific DNS settings to synchronize.
hosts- Custom DNS A recordscnameRecords- CNAME alias records- Avoids read-only settings like
dns.upstreamordns.interface
SYNC_GRAVITY_AD_LIST: Synchronizes blocklist configurations. SYNC_GRAVITY_DOMAIN_LIST: Synchronizes allowlist configurations.
Deployment
Start the nebula-sync service:
# Deploy the container
docker compose up -d
# Verify it's running
docker compose ps
# Check synchronization logs
docker compose logs -f nebula-sync
Authentication Setup
For production environments, configure API tokens:
- Generate API tokens on each Pi-hole instance:
# Access Pi-hole container docker exec -it pihole /bin/bash # Generate API token pihole -a -p - Update environment configuration:
PRIMARY="http://192.168.7.250|your-primary-token" REPLICAS="http://192.168.7.251|your-replica-token" - Restart the service:
docker compose down && docker compose up -d
Monitoring and Troubleshooting
Log Analysis
Monitor synchronization status:
# View recent sync operations
docker compose logs --tail 50 nebula-sync
# Follow live sync activity
docker compose logs -f nebula-sync
# Check for API errors
docker compose logs nebula-sync | grep -i error
Common Issues
API Authentication Failures:
- Verify API tokens are correct
- Ensure Pi-hole instances are accessible
- Check network connectivity between containers
Read-Only Configuration Errors:
- These occur when trying to sync Docker environment variables
- Solution: Use
SYNC_CONFIG_DNS_INCLUDEto specify only writable settings
Sync Conflicts:
- Nebula-sync overwrites replica configurations with primary settings
- Ensure primary instance contains the desired configuration state
Manual Sync Trigger
Force immediate synchronization:
# Execute one-time sync
docker exec nebula-sync /usr/local/bin/nebula-sync
Integration with Ansible
For automated DNS record management, I use Ansible playbooks to update the primary Pi-hole instance, which then propagates changes via nebula-sync:
# Update primary Pi-hole with Ansible
ansible-playbook -i inventories/hosts playbooks/pihole_local_dns/add-pihole-dns.yml
# Changes automatically sync to replicas within 15 minutes
This creates a workflow where Ansible manages the primary instance, and nebula-sync ensures replicas stay synchronized.
Best Practices
Backup Before Sync: Always backup Pi-hole databases before implementing synchronization.
Test Configuration: Validate sync settings with non-production instances first.
Monitor Logs: Regular log review helps identify sync issues early.
Selective Sync: Only synchronize necessary settings to avoid conflicts.
Network Isolation: Run nebula-sync in the same network context as Pi-hole for optimal connectivity.
Limitations
One-Way Sync: Changes must be made on the primary instance; replica modifications will be overwritten.
Read-Only Settings: Docker environment variables cannot be synchronized via API.
Network Dependencies: Requires reliable network connectivity between instances.
API Rate Limits: High-frequency sync may impact Pi-hole performance.
Learnings
API Limitations: Pi-hole’s API has read-only restrictions on certain configuration keys. Understanding which settings can be modified via API is crucial for avoiding errors.
Sync Frequency: A 15-minute sync interval provides a good balance between responsiveness and system load. More frequent syncing may not be necessary for most homelab environments.
Container Networking: Running nebula-sync in the same Docker network as Pi-hole instances simplifies connectivity and reduces potential network issues.
Configuration Management: Treating the primary Pi-hole as the single source of truth simplifies configuration management and reduces conflicts.
Nebula-sync provides a robust solution for maintaining consistent Pi-hole configurations across multiple instances. With proper configuration and monitoring, it ensures that your DNS infrastructure remains synchronized and highly available without manual intervention.