This daemon allows you to assign domain names (with wildcard support) to your Docker containers that are resolved locally and within the containers themselves.
It uses:
- dnsmasq
- your systemd-resolved configuration
- Docker Domain resolution
- The principle
- Installation
- Configuration
- Examples of use
- What happens when you shut down the service?
- What about Traefik
The daemon will simply start a dnsmasq service listening on your docker0
interface. It will then tell systemd-resolved that this interface should be used to resolve domain names (while continuing to resolve external domain names).
Whenever a container starts or is stopped:
- the daemon reads the list of Docker containers
- it creates a domain name
container_name.docker
(you can change this in/etc/docker/docker-domains.conf
) - it creates a domain name
container_name.network_name.docker
if the container is started inside a Docker network - if the container has a hostname, it will also be added
- if container has got a domain, it will append the domain to hostname
- if you specified
DOCKER_STATIC_NAMES
in confguration and the container name corresponds, so the corresponding domain name is also added (interessing with Kind withingress-nginx
withcontroller.hostPort
to "true")
Keep in mind that .docker
can be changed in /etc/docker/docker-domains.conf
.
Subdomains are also resolved. So if your container is started with a hostname at "foo.docker", the addresses *.foo.docker
will point to container.
All domains are also resolved inside containers! That's the reason this project exists.
You theorically not need to change firewall configuration. If you have any problem and/or solution, please fill an issue and/or provide a pull-request.
Clone this repository, then in a terminal, go to the directory and type:
make build
sudo make install
To activate the service:
sudo systemctl start docker-domains
You can also start it with your system:
# --now indicates to start the service at the same time
sudo systemctl enable --now docker-domains
You can uninstall with the command:
sudo make uninstall
The compilation is done with a Docker container, so you don't need to install the go
compiler on your computer.
The configuration is placed in /etc/docker/docker-domains.conf
- you must restart the service after changes.
This is what you can set:
DOCKER_DOMAIN
this is the "domain" to add to container name. E.g. a container named "foo" will respond to "foo.docker" ifDOCKER_DOMAIN
is set to.docker
DOCKER_INTERFACE
is the docker net interface. It's commonlydocker0
, don't change it unless you know what you doDOCKER_DEFAULT_NETWORK
is the default network for you containers unless you specify one. This is commonlybridge
, anddocker-domains
will not append the network name in domain name for this networkDOCKER_STATIC_NAMES
is a coma spearated list ofname:domain
to force. It's pretty interessing when you use a software which starts contiainers without letting you the choice of container name. E.g. for Kind names the control planekind_control_plane
. If you use a Ingress Controller that bind ports on this container, so you can propose a domamin name to access your web applications. Don't forget to add a dot to the domain name to allow wildcard
After having installed and started the docker-domains
service:
# no need to bind ports :)
docker run --rm --name website --hostname foo.com -d nginx
# Try to get pages:
curl foo.com
curl website.docker
# stop
docker stop website
# then resolution should fail
curl website.docker
Kind is a local Kubernetes instance that will start in Docker. The default master is named kind_control_plane
. You can start a Ingress Controller inside the Kubernetes instance to hit your web applications with a local domain.
First, change /etc/docker/docker-domains.conf
and set DOCKER_STATIC_NAMES
to force kind_control_plane
to be resolved as .kind
for example:
DOCKER_STATIC_NAMES=kind_control_plane:.kind
And restart docker-domains: systemctl restart docker-domains
Then:
# create a cluster
kind create cluster
# wait for started, type "kubectl get nodes" - node should be "Ready" after a few seconds
# add an ingress controller listening on 8à/443 ports on control plane
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install -n ingress ingress-controller ingress-nginx/ingress-nginx \
--create-namespace \
--set controller.hostPort=true
# check "kubectl -n ingress get pods", all pods should be "Running" after a few seconds
# Now create a "ghost" website for example, set the "hostname" to "something.kind"
# e.g. ghost.kind
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install -n ghost ghost bitnami/ghost --create-namespace \
--set ingress.enabled=true \
--set ingress.ingressClassName=nginx \
--set ingress.hostname=ghost.kind
After a few seconds, you can go to http://ghost.kind
It works because:
kind_control_plane
IP address is resolved for all.kind
subdomains (applied in configuration)- the ingress controller is configured to listen on ports 80 and 443 on "all nodes", so on
kind_control_plane
also - the ingress controller routes the subdomains to the corresponding "Pods"
It's a bit easier than using nip.io
with the risk of a IP change across reboot.
You need to add your own reverse proxy if you're not using a service that can handle certificates. But, in short, it's easy to do with mkcert
.
Install certutil and mkcert:
sudo apt install libnss3-tools
-or-
sudo dnf install nss-tools
-or-
sudo pacman -S nss
-or-
sudo zypper install mozilla-nss-tools
curl -L https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64 -o ~/.local/bin/mkcert
chmod +x ~/.local/bin/mkcert
E.g. with Nginx.
First, create a certificate and key inside a local directory of your project:
mkdir -p nginx/{conf.d,certs}
# Do it only once, install CA root to your trust store (chrome/chromium/brave, firefox...)
# You'll need to restart your web browser !
mkcert -install
# create foo.com certificate and key
mkcert --install --cert-file nginx/certs/foo.com.crt --key-file nginx/certs/foo.com.key foo.com
Then, create the nginx/conf.d/default.conf
changing YOUR_CONTAINER
to a service/container name (avoid domain):
server {
listen :443;
ssl_certificate /etc/nginx/certs/foo.com.crt;
ssl_certificate_key /etc/nginx/certs/foo.com.key;
location / {
proxy_pass http://YOUR_CONTAINER
}
}
In youd docker-compose file, add this:
services:
#...
reverseproxy:
image: nginx:alpine
volumes:
- nginx/conf.d:/etc/nginx/conf.d:z
- nginx/certs:/etc/nginx/certs:z
hostname: foo.com
depends_on:
- YOUR_CONTAINER
And that's all. foo.com
can now be resolved and you can connect https://foo.com
We can use the example above to imaginate a project that has got several web applications.
Set the certificat to wildcard:
mkcert -cert-file nginx/certs/foo.com.crt -key-file nginx/certs/foo.com.key "*.foo.com"
Change the nginx configuration to server 2 subdomains:
server {
listen :443;
server_name site1.foo.com; # first subdomain
ssl_certificate /etc/nginx/certs/foo.com.crt;
ssl_certificate_key /etc/nginx/certs/foo.com.key;
location / {
# point on a first container
proxy_pass http://YOUR_CONTAINER_1
}
}
server {
listen :443;
server_name site2.foo.com; # second subdomain
ssl_certificate /etc/nginx/certs/foo.com.crt;
ssl_certificate_key /etc/nginx/certs/foo.com.key;
location / {
# point on another container
proxy_pass http://YOUR_CONTAINER_2
}
}
Because docker-domains configure dnsmasq to resolve all subdomains of "foo.com", so both site1.foo.com and site2.foo.com will be resolved to the Nginx container. Nginx will detect the requested subdomain to route clients to the right container.
When docker-domain
is shut down, the daemon-specific configuration in systemd-resolved
is removed and dnsmasq
is stopped. Thus, you are back to the original situation.
Traefik is nice. Really, I like it.
But there are drawbacks:
- it's a reverse proxy, you will need to use
.localhost
domains or touch yourself/etc/hosts
to resolve others domain names. So, to reproduce a customer settings in production, it's a bit complexe - with standard and respectful distrubution (so, not Ubuntu...), you cannot bind ports 80 and 443 with a container started as standar user. That means that Traefik should be launched as root
- with standard and... ok...
/etc/hosts
cannot be used to respond to "wildcard" domains. That means that you will need to add all your subdomains in/etc/hosts
- you need to remember to clean up
/etc/hosts
file... - Traefik can be complicated with dynamic Varnish activation - some users reports me this, I don't have details
- As it's a reverse proxy, it may break or add HTTP Headers
Using Domain resolution is lightweight and doesn't add a layer. Also, you can use whatever the protocol you want, it's not only for HTTP.