Sherpa-DNS is a python application designed to create and manage DNS records for services defined in docker compose stacks or stand-alone docker containers via labels. It draws inspiration from the Kubernetes External-DNS project but is specifically tailored for docker environments.
Sherpa-DNS runs as a Docker container. It needs read access to the Docker API to monitor container events. Because the Sherpa-DNS image is based on Chainguard and runs as a non-root user (nonroot
UID 65532) for enhanced security, it cannot directly access the host's Docker socket (/var/run/docker.sock
) due to permissions.
To solve this securely, we rely on a dedicated docker-socket-proxy container. This proxy container runs with the necessary privileges to access the host's Docker socket. It then exposes a limited, read-only version of the Docker API which the non-root Sherpa-DNS container can safely connect to without needing direct access to the host socket itself.
The recommended installation method using Docker Compose handles setting up this proxy automatically.
This method uses the pre-built Sherpa-DNS image from GitHub Container Registry and includes the docker-socket-proxy
for secure, non-root access to the Docker API. This is the easiest and most secure way to run Sherpa-DNS.
- Download the compose file: Download
docker-compose.yml
from the docker/directory of the Sherpa-DNS repository. - Create
sherpa-dns.yaml
: In the same directory where you saveddocker-compose.yml
, create yoursherpa-dns.yaml
configuration file. You can copyexample_sherpa-dns.yaml
as a starting point and modify it. - Create
.env
file: In the same directory, create an.env
file with your Cloudflare API token and, optionally, your encryption key (ifregistry.encrypt_txt
istrue
in your config):# .env file contents CLOUDFLARE_API_TOKEN=your_api_token_here ENCRYPTION_KEY=your_secret_passphrase_here # Only needed if registry.encrypt=true
- Run Docker Compose: From the directory containing your
docker-compose.yml
,sherpa-dns.yaml
, and.env
file, run:This will start both thedocker compose up -d
docker-socket-proxy
and thesherpa-dns
service.
Sherpa-DNS uses a YAML file (default: sherpa-dns.yaml
passed as an argument, or looked for at /config/sherpa-dns.yaml
inside the container) for configuration. Environment variables like ${VAR_NAME}
can be used and will be substituted from the container's environment (e.g., passed via .env
or -e
).
You can use example_sherpa-dns.yaml as a starting point to create your own config.
Configures how Sherpa-DNS discovers target endpoints.
label_prefix
(string): The prefix for Docker labels used by Sherpa-DNS.- Default:
"sherpa.dns"
- Default:
label_filter
(string, optional): If set, Sherpa-DNS will only process containers that have a specific label matching this filter.- Format: Can be just a key (e.g.,
"enable-sherpa"
) to check for the label's existence, or key=value (e.g.,"enable-sherpa=true"
) to check for a specific key and value. - Default:
""
(empty string, meaning no filtering - process all containers).
- Format: Can be just a key (e.g.,
Configures the DNS provider where records will be managed.
name
(string): The name of the DNS provider.- Default:
"cloudflare"
(Currently the only supported provider).
- Default:
cloudflare
: A nested section containing Cloudflare-specific settings.api_token
(string): Your Cloudflare API token. Required. Use environment variable substitution (e.g.,"${CLOUDFLARE_API_TOKEN}"
).proxied_by_default
(boolean): Sets the default "Proxied" status for created A/CNAME records if not specified by a label.- Default:
false
- Default:
Configures how Sherpa-DNS tracks the DNS records it manages.
type
(string): The type of registry to use.- Default:
"txt"
(Currently the only supported type, uses TXT records for tracking).
- Default:
txt_prefix
(string): A prefix added to the hostname when creating the corresponding TXT registry record. This helps identify Sherpa-managed TXT records and avoids conflicts (e.g., a TXT and CNAME cannot have the same name).- Default:
"sherpa-dns-"
- Default:
txt_owner_id
(string): An identifier written into the TXT record content (owner=...
) to distinguish records managed by different Sherpa-DNS instances (e.g., staging vs. production).- Default:
"default"
- Default:
txt_wildcard_replacement
(string): A string used to replace the literal*
character in a hostname when generating the TXT record's name. This ensures the TXT record name itself is valid DNS syntax (e.g.,*.example.com
becomessherpa-dns-star.example.com
if the replacement isstar
).- Default:
"star"
- Default:
encrypt_txt
(boolean): Whether to encrypt the content of the TXT registry records.- Default:
false
- Default:
encryption_key
(string, optional): A secret passphrase used to derive the encryption key ifencrypt_txt
istrue
. Do not use the raw encryption key here. Use environment variable substitution (e.g.,"${ENCRYPTION_KEY}"
). Required ifencrypt_txt
istrue
.
Configures the main reconciliation logic.
interval
(string): How often the controller reconciles the desired state (from Docker labels) with the actual state (from DNS provider). Uses duration format (e.g.,60s
,1m
,5m
).- Default:
"1m"
- Default:
once
(boolean): Iftrue
, run the reconciliation loop only once and then exit. Useful for testing or specific scripting scenarios.- Default:
false
- Default:
dry_run
(boolean): Iftrue
, calculate changes but do not actually make any calls to the DNS provider API. Logs planned changes instead.- Default:
false
- Default:
cleanup_on_stop
(boolean): Iftrue
, DNS records for containers that stop/disappear will be queued for deletion after a delay. Iffalse
, records are left behind.- Default:
true
- Default:
cleanup_delay
(string): How long to wait after a container stops before deleting its DNS records. Uses duration format (e.g.,30s
,15m
,1h
). Only relevant ifcleanup_on_stop
istrue
.- Default:
"15m"
- Default:
Filters which DNS zones the provider should manage.
include
(list of strings, optional): Only manage zones matching these domain names/patterns. Patterns can include wildcards (*
). If empty or omitted, all zones accessible by the API token are potentially managed (subject toexclude
).exclude
(list of strings, optional): Explicitly exclude zones matching these domain names/patterns. Exclusions take precedence over inclusions.
Configures application logging.
level
(string): The minimum log level to output. Standard Python levels (e.g.,"debug"
,"info"
,"warning"
,"error"
).- Default:
"info"
- Default:
You control which DNS records Sherpa-DNS creates by adding labels to your Docker containers (either directly in docker run
or within the labels:
section of a docker-compose.yml
service).
Use the prefix defined in source.label_prefix
(default sherpa.dns
).
sherpa.dns/hostname
(string): Required. The fully qualified domain name (FQDN) for the DNS record (e.g.,myapp.example.com
,*.internal.example.com
).sherpa.dns/type
(string): Optional. The type of DNS record to create.- Values:
"A"
,"CNAME"
- Default:
"A"
- Values:
sherpa.dns/target
(string): Optional. The target/value of the DNS record.- Default for
A
records: The IP address of the container within the default Docker bridge network (or a specific network if networking is configured differently - check source code for exact logic). - Default for
CNAME
records: The container's name. - You can override this to point an
A
record to a specific IP or aCNAME
to a specific target hostname.
- Default for
sherpa.dns/ttl
(string): Optional. The Time-To-Live for the DNS record in seconds.- Value: A number representing seconds (e.g.,
"300"
for 5 minutes) OR the special value"1"
which maps to Cloudflare's "Auto" TTL. - Default: Cloudflare's default TTL (usually "Auto" / 1).
- Value: A number representing seconds (e.g.,
sherpa.dns/proxied
(string): Optional. Whether the DNS record should be proxied through Cloudflare (orange cloud).- Values:
"true"
,"false"
- Default: The value of
provider.cloudflare.proxied_by_default
insherpa-dns.yaml
(which defaults tofalse
).
- Values:
A Record for a Web App (Auto IP, Auto TTL, Not Proxied):
# docker-compose.yml
services:
my-web-app:
image: nginx:latest
labels:
- "sherpa.dns/hostname=app.example.com"
A Record with Specific IP and TTL (Proxied):
# docker-compose.yml
services:
backend-service:
image: nginx:latest
labels:
- "sherpa.dns/hostname=api.example.com"
- "sherpa.dns/target=123.123.1.20"
- "sherpa.dns/ttl=600"
- "sherpa.dns/proxied=true"
CNAME Record:
# docker-compose.yml
services:
redirector:
image: nginx:latest
labels:
- "sherpa.dns/hostname=old-app.example.com"
- "sherpa.dns/type=CNAME"
- "sherpa.dns/target=new-app.example.com" # Point to another DNS name
- "sherpa.dns/ttl=1" # Auto TTL
Wildcard Record:
# docker-compose.yml
services:
wildcard-handler:
image: nginx:latest
labels:
- "sherpa.dns/hostname=*.internal.example.com"
- "sherpa.dns/target=192.168.1.100" # Target IP for the wildcard A record
- "sherpa.dns/type=A"
Sherpa-DNS operates with a few key components:
- Source (
DockerContainerSource
): Watches the Docker daemon for container events (start, stop, die) and periodically lists running containers. It extracts DNS configuration from container labels that match the configured prefix and filter. - Registry (
TXTRegistry
): Queries the DNS Provider (Cloudflare) for special TXT records that act as a database. It uses these TXT records (identified bytxt_prefix
andtxt_owner_id
) to determine which A/CNAME records it currently manages. - Provider (
CloudflareProvider
): Interacts with the Cloudflare API to list zones, list existing DNS records, create new records, update records, and delete records. - Controller (
Controller
): The central coordinator. It periodically:- Gets the desired state (list of
Endpoint
objects) from the Source. - Gets the current state (list of managed
Endpoint
objects) from the Registry. - Calculates the changes needed (create, update, delete) using the
Plan
. - Tells the Registry to
sync
the changes, which involves calls to the Provider API to modify A/CNAME records and the corresponding TXT registry records. - Manages a delayed cleanup mechanism for records associated with stopped containers.
- Gets the desired state (list of
- Health Server (
HealthCheckServer
): Provides basic/health
and/metrics
endpoints for monitoring.
If you want to contribute or run the code locally for development:
- Clone the repository.
- Create your
.env
andsherpa-dns.yaml
files. - Use the provided
Makefile
:make format
: Format code usingisort
,ruff
,black
.make lint
: Check code style usingruff
andblack
.make run-dev
: Build the image locally and run the container with logs attached, usingdocker/docker-compose.dev.yml
. This forces a rebuild on each run.make stop
: Stop any runningmake run-dev
ormake run
containers.make help
: Display available commands.
This project is licensed under the MIT License. See the LICENSE
file for details.