A Node.js service that automatically manages DNS records in Cloudflare based on Docker events. It monitors Docker containers for specific labels and updates corresponding DNS records automatically.
- π Automatic DNS record management based on Docker container labels
- π Real-time monitoring of Docker container 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)
- π³ Works with regular Docker (no Swarm required)
- π¦ Node.js 20 or higher
- π³ Docker (standalone mode)
- βοΈ Cloudflare account and API token with DNS edit permissions
- π Access to Docker socket (read-only is sufficient)
- π IPv6 enabled in Docker daemon (required only for AAAA records)
docker run -d \
--name dnsfik \
--restart unless-stopped \
-e CLOUDFLARE_TOKEN=your_cloudflare_api_token \
-e LOG_LEVEL=info \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
dnsfikNote: The container runs as root to access the Docker socket (similar to Traefik, Portainer, etc.).
Create a docker-compose.yml file:
version: "3.8"
services:
dns-manager:
image: dnsfik:latest
container_name: dnsfik
restart: unless-stopped
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:roThen start the service:
docker compose up -dAdd DNS labels to your Docker containers to automatically manage DNS records:
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)
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 run -d \
--name my-container \
--label dns.cloudflare.hostname=api.domain.com \
your-image# Create a container with an A record and custom TTL
docker run -d \
--name my-app \
--label dns.cloudflare.hostname=subdomain.domain.com \
--label dns.cloudflare.type=A \
--label dns.cloudflare.ttl=3600 \
your-image# Create a container with a CNAME record
docker run -d \
--name my-app \
--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# Create a container with both A and AAAA records
docker run -d \
--name my-app \
--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# Create a container with multiple subdomains
docker run -d \
--name my-app \
--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# Create a container with mixed record types
docker run -d \
--name my-app \
--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-imageThe following environment variables can be used to configure the application:
| Variable | Description | Default | Required |
|---|---|---|---|
CLOUDFLARE_TOKEN |
Cloudflare API token | - | Yes |
CLOUDFLARE_ZONE_ID |
Cloudflare Zone ID (optional) | - | No |
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 |
| 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 |
Basic Configuration
CLOUDFLARE_TOKEN=your_token_here
CLOUDFLARE_ZONE_ID=your_zone_id_here
USE_TRAEFIK_LABELS=true
DNS_DEFAULT_RECORD_TYPE=A
DNS_DEFAULT_PROXIED=trueCNAME Configuration
DNS_DEFAULT_RECORD_TYPE=CNAME
DNS_DEFAULT_CONTENT=origin.domain.com
DNS_DEFAULT_PROXIED=false
DNS_DEFAULT_TTL=3600Important: To create AAAA (IPv6) records with automatic IP detection (dns.cloudflare.content=public_ip or without content), IPv6 must be enabled in the Docker daemon configuration.
- β A records (IPv4) work normally
β οΈ AAAA records (IPv6) will be skipped with a warning- βΉοΈ Other DNS records for the same container continue to process normally
You can specify the IPv6 address explicitly without enabling IPv6 in Docker:
labels:
- "dns.cloudflare.hostname=api.domain.com"
- "dns.cloudflare.type=AAAA"
- "dns.cloudflare.content=2001:db8::1"The service can automatically create DNS records from your Traefik Host rules for your Docker containers.
- Traefik integration must be explicitly enabled with
USE_TRAEFIK_LABELS=true - DNS records are only created for containers 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
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=1Basic 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 enabledCustom 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 settingsIf you want to contribute or modify the code:
- Clone the repository:
git clone https://github.com/SIGTERM-015/dnsfik.git
cd dnsfik- Copy
.env.exampleto.envand fill in your Cloudflare credentials:
cp .env.example .env- Install dependencies and start in development mode:
Option A: Local development
yarn install
yarn devOption B: Using docker-compose for development
docker compose up -dThe development setup includes:
- Hot reloading for code changes
- Debug level logging
- Source maps for debugging
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:coverageBased 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.
To check test coverage locally:
yarn test:coverage-textContributions are welcome! Please read our CONTRIBUTING.md for details on:
- π¦ Release process with semantic versioning
- π·οΈ How to use labels to create releases
- π Development workflow
- β Testing requirements
Quick summary:
- Create a PR to
main - Add a label:
release:major,release:minor, orrelease:patch - Merge the PR
- π Automatic release and Docker image publication!
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!
MIT