π Docker/Swarm Cloudflare DNS Manager (WIP)
A Node.js service that automatically manages DNS records in Cloudflare based on Docker events. It monitors both Docker containers and Swarm services for specific labels and updates corresponding DNS records.
β¨ Features
- π Automatic DNS record management based on Docker service labels
- π Real-time monitoring of both Docker containers and Swarm services events
- π·οΈ Support for multiple DNS record types (A, AAAA, CNAME, MX, TXT)
- π Public IP caching and validation
- πͺ Fault-tolerant design with retry mechanisms
- π Automatic DNS creation from Traefik labels (optional)
π Prerequisites
- π¦ Node.js 20 or higher
- π³ Docker (works in both Swarm and standalone mode)
- βοΈ Cloudflare account and API token with DNS edit permissions
- π Access to Docker socket (read-only is sufficient)
π Installation
Quick Start with Docker Swarm
# Create a config file for environment variables
cat << EOF > cloudflare-dns.env
CLOUDFLARE_TOKEN=your_cloudflare_api_token
LOG_LEVEL=info
EOF
# Deploy the service to your swarm
docker service create \
--name cloudflare-dns \
--env-file cloudflare-dns.env \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock,ro \
--constraint 'node.role == manager' \
marlburrow/cloudflare-dns-swarm
Or using a stack file (for Portainer or docker stack deploy):
version: "3.8"
services:
dns-manager:
image: marlburrow/cloudflare-dns-swarm:latest
environment:
- CLOUDFLARE_TOKEN=your_cloudflare_api_token
- LOG_LEVEL=info
- RETRY_ATTEMPTS=3
- RETRY_DELAY=300000
- IP_CHECK_INTERVAL=3600000
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
resources:
limits:
memory: 256M
reservations:
memory: 128M
restart_policy:
condition: any
delay: 5s
max_attempts: 3
window: 120s
Save this as cloudflare-dns-stack.yml and deploy:
docker stack deploy -c cloudflare-dns-stack.yml cloudflare-dns
π Usage
Works with both Docker containers and Swarm services. Here are examples for both:
π·οΈ Available Labels
dns.cloudflare.hostname: DNS record name (required)dns.cloudflare.type: Record type (A, AAAA, CNAME, TXT, MX)dns.cloudflare.content: Record content (required for CNAME, optional for A/AAAA)dns.cloudflare.ttl: Time to live in seconds (optional, default: 1)dns.cloudflare.proxied: Enable/disable Cloudflare proxy (optional, default: true)
π― Default Behaviors
The service includes smart defaults to minimize configuration:
- π€ Record Type: If not specified, defaults to
Arecord - π Record Content:
- For
Arecords: Uses public IP from ipify.org if not specified - For
AAAArecords: Uses public IPv6 if available, else skips record creation - For other types (
CNAME,TXT,MX): Content is required
- For
- β‘ Proxy Status: Defaults to
true(traffic proxied through Cloudflare) - β±οΈ TTL: Defaults to 1 (automatic)
Example with minimal configuration:
# Only hostname specified - creates an A record with public IP
docker service create \
--name my-service \
--label dns.cloudflare.hostname=api.domain.com \
your-image
π¦ Docker Container
# Create a container with DNS records
docker run -d \
--name my-container \
--label dns.cloudflare.hostname=api.domain.com \
--label dns.cloudflare.type=A \
--label dns.cloudflare.proxied=false \
your-image
π³ Docker Swarm Service
πΉ Basic A Record
# Create a service with an A record and custom TTL
docker service create \
--name my-service \
--label dns.cloudflare.hostname=subdomain.domain.com \
--label dns.cloudflare.type=A \
--label dns.cloudflare.ttl=3600 \
your-image
π CNAME Record
# Create a service with a CNAME record
docker service create \
--name my-service \
--label dns.cloudflare.hostname=alias.domain.com \
--label dns.cloudflare.type=CNAME \
--label dns.cloudflare.content=target.domain.com \
--label dns.cloudflare.proxied=true \
your-image
π IPv4 and IPv6 Records
# Create a service with both A and AAAA records
docker service create \
--name my-service \
--label dns.cloudflare.hostname=api.domain.com \
--label dns.cloudflare.type=A \
--label dns.cloudflare.proxied=true \
--label dns.cloudflare.hostname.v6=api.domain.com \
--label dns.cloudflare.type.v6=AAAA \
--label dns.cloudflare.content.v6=2001:db8::1 \
--label dns.cloudflare.proxied.v6=false \
your-image
π·οΈ Multiple Subdomains
# Create a service with multiple subdomains
docker service create \
--name my-service \
--label dns.cloudflare.hostname=api.domain.com \
--label dns.cloudflare.type=A \
--label dns.cloudflare.hostname.admin=admin.domain.com \
--label dns.cloudflare.type.admin=A \
--label dns.cloudflare.hostname.web=www.domain.com \
--label dns.cloudflare.type.web=CNAME \
--label dns.cloudflare.content.web=domain.com \
your-image
π Multiple Records
# Create a service with mixed record types
docker service create \
--name my-service \
--label dns.cloudflare.hostname=domain.com \
--label dns.cloudflare.type=A \
--label dns.cloudflare.hostname.mx=domain.com \
--label dns.cloudflare.type.mx=MX \
--label dns.cloudflare.content.mx="10 mail.domain.com" \
--label dns.cloudflare.hostname.txt=domain.com \
--label dns.cloudflare.type.txt=TXT \
--label dns.cloudflare.content.txt="v=spf1 include:_spf.domain.com ~all" \
your-image
βοΈ Configuration
The following environment variables can be used to configure the application:
Core Settings
| Variable | Description | Default | Required |
|---|---|---|---|
CLOUDFLARE_TOKEN |
Cloudflare API token | - | Yes |
DOCKER_SOCKET |
Docker socket path | /var/run/docker.sock |
No |
LOG_LEVEL |
Logging level (debug, info, warn, error) | info |
No |
RETRY_ATTEMPTS |
Number of retry attempts | 3 |
No |
RETRY_DELAY |
Delay between retries (ms) | 300000 |
No |
IP_CHECK_INTERVAL |
IP check interval (ms) | 3600000 |
No |
DNS Settings
| Variable | Description | Default | Required |
|---|---|---|---|
USE_TRAEFIK_LABELS |
Enable Traefik label support | false |
No |
DNS_DEFAULT_RECORD_TYPE |
Default DNS record type | A |
No |
DNS_DEFAULT_CONTENT |
Default record content | - | No |
DNS_DEFAULT_PROXIED |
Default Cloudflare proxy status | true |
No |
DNS_DEFAULT_TTL |
Default TTL | 1 |
No |
Examples
Basic Configuration
CLOUDFLARE_TOKEN=your_token_here
USE_TRAEFIK_LABELS=true
DNS_DEFAULT_RECORD_TYPE=A
DNS_DEFAULT_PROXIED=true
CNAME Configuration
DNS_DEFAULT_RECORD_TYPE=CNAME
DNS_DEFAULT_CONTENT=origin.domain.com
DNS_DEFAULT_PROXIED=false
DNS_DEFAULT_TTL=3600
π Traefik Integration
The service can automatically create DNS records from your Traefik Host rules.
Important Notes
- Traefik integration must be explicitly enabled with
USE_TRAEFIK_LABELS=true - DNS records are only created for services with
traefik.enable=true - DNS settings can be overridden using explicit dns.cloudflare.* labels
- Multiple hosts in a single rule are supported and will create separate DNS records
- CNAME records require explicit content to be specified
Configuration
Enable Traefik integration and configure default behavior:
# Enable Traefik integration
USE_TRAEFIK_LABELS=true
# Configure default DNS settings (optional)
DNS_DEFAULT_RECORD_TYPE=A
DNS_DEFAULT_CONTENT=
DNS_DEFAULT_PROXIED=true
DNS_DEFAULT_TTL=1
Examples
Basic Usage
services:
webapp:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`app.domain.com`)"
# This will create:
# - An A record for app.domain.com
# - Using your public IP as content
# - With Cloudflare proxy enabled
Custom DNS Settings
services:
webapp:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`app.domain.com`)"
# Override default DNS settings
- "dns.cloudflare.type=CNAME"
- "dns.cloudflare.content=origin.domain.com"
- "dns.cloudflare.proxied=false"
Multiple Hosts
services:
webapp:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`app.domain.com`) || Host(`api.domain.com`)"
# This will create DNS records for both domains
# using the default settings
Development Setup
If you want to contribute or modify the code:
- Clone the repository:
git clone https://github.com/yourusername/docker-swarm-dns-manager.git
cd docker-swarm-dns-manager
- Copy
.env.exampleto.envand fill in your Cloudflare credentials:
cp .env.example .env
- Install dependencies and start in development mode:
yarn install
yarn dev
# Or using docker-compose for development:
docker compose up -d
The development setup includes:
- Hot reloading for code changes
- Debug level logging
- Source maps for debugging
π§ͺ Testing
The project uses Jest for testing. The test suite includes:
- Unit tests for all services and utilities
- Integration tests for Docker events and DNS updates
- Validation tests for labels and configurations
- Mock implementations for external services (Docker, Cloudflare, IP services)
To run the tests:
# Run tests
yarn test
# Run tests in watch mode
yarn test:watch
# Run tests with coverage report
yarn test:coverage
π Error Handling & Reliability
Based on the test suite, the service includes:
- Automatic retries for failed DNS operations
- IP address validation and double-checking
- Graceful handling of Docker event failures
- Caching of IP addresses with periodic refresh
- Validation of all DNS record configurations
- Fault tolerance for network issues
The project maintains a high test coverage to ensure reliability. All new contributions should include appropriate tests.
π€ Contributing
Contributions are welcome! Feel free to open an issue or pull request.
πΊοΈ Roadmap
π― Upcoming Features
π Multi-Provider Support
The next major version will introduce a plugin system for multiple DNS providers:
-
π Plugin Architecture
- Abstract provider interface
- Easy integration of new providers
- Hot-swappable providers
-
π’ Planned Providers
- β Cloudflare (current)
- π AWS Route53
- π Google Cloud DNS
- π OVH DNS
- π Digital Ocean DNS
Want to contribute to these features? Check our Contributing section!
π License
MIT