Skip to Content
Home LabDocker

Docker

GitHub: https://github.com/ricmatsui/home/tree/master/roles/docker

Docker runs on each node in the cluster to provide container support. The nodes are joined into a swarm for easier management of containers, networks, secrets, and other resources. The swarm also enables service communication across the entire cluster.

pi@pi:~ $ sudo docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION gilz81ihgoq5th9qv67o8oub0 cannoli Ready Active Reachable 27.4.1 rpo4cymiikuh5cbc8qzvte4ym * pi Ready Active Leader 28.5.2 5q8pi3s1j7aahpky5gaxo4mbl tart Ready Active Reachable 28.5.2

Nodes are labelled so that services can be placed to meet the specific constraints required. For example, the ingress node is labelled with "home.network": "ingress". Communication between nodes travels over the Wireguard overlay network 192.168.3.0/24.

pi@pi:~ $ sudo docker node inspect pi [ { ... "Spec": { "Labels": { "home.camera": "connected", "home.instance_size": "small", "home.instance_type": "pi", "home.network": "ingress", "home.storage": "external" }, "Role": "manager", "Availability": "active" }, ... "Status": { "State": "ready", "Addr": "192.168.3.33" }, ... } ]

Networking

Swarm

Docker Swarm provides overlay networks that span the entire cluster.

pi@pi:~ $ sudo docker network ls NETWORK ID NAME DRIVER SCOPE 7fa0320b799d bridge bridge local ed93bc768117 docker_gwbridge bridge local ... lvjd5edvznbk ingress overlay swarm mvlrjbe7qx4f temporal_temporal overlay swarm 0uer3zrkkl3f traefik_traefik overlay swarm

Overlay networks in the swarm are given subnets and containers are assigned addresses from the subnets. Here the swarm is configured to use the 10.10.0.0/16 address space and networks are given /24 subnets.

pi@pi:~ $ sudo docker inspect traefik_traefik [ { "Name": "traefik_traefik", "Id": "0uer3zrkkl3f4sou7gehdsq5i", "Created": "2025-11-05T20:02:53.340102187Z", "Scope": "swarm", "Driver": "overlay", "EnableIPv4": true, "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.10.1.0/24", "Gateway": "10.10.1.1" } ] }, ... "Containers": { "247836f56c003fa9e5d292b6c003672210fad484fe6e4f28d6cf97a19e29eab4": { "Name": "traefik_traefik-forward-auth.1.vviin5jl8mqf47fkt5zkybnqk", "EndpointID": "a5efed0731a0d0b381d074e4027e4c4407d62c508f8da020310c5a877b99260c", "MacAddress": "02:42:0a:0a:01:19", "IPv4Address": "10.10.1.25/24", "IPv6Address": "" }, "8938090adbe9cb346004dbc4ecfe2ededac16a52c6e771c834f858375f165a03": { "Name": "traefik_traefik.1.rlpz6v41b5za9dlilue3lolle", "EndpointID": "2023751ffc88cb847e3f17ee34db2897989a3bfa4c6bccded5d6c9bb8a775019", "MacAddress": "02:42:0a:0a:01:1d", "IPv4Address": "10.10.1.29/24", "IPv6Address": "" }, ... }, ... } ]

Containers can be assigned to multiple networks and are given virtual interfaces for each network.

0fdd4ee6d081:/home/ui-server# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 459: eth1@if460: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP link/ether 02:42:0a:0a:01:2c brd ff:ff:ff:ff:ff:ff inet 10.10.1.44/24 brd 10.10.1.255 scope global eth1 valid_lft forever preferred_lft forever 465: eth2@if466: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:12:00:06 brd ff:ff:ff:ff:ff:ff inet 172.18.0.6/16 brd 172.18.255.255 scope global eth2 valid_lft forever preferred_lft forever 473: eth0@if474: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP link/ether 02:42:0a:0a:03:25 brd ff:ff:ff:ff:ff:ff inet 10.10.3.37/24 brd 10.10.3.255 scope global eth0 valid_lft forever preferred_lft forever

Docker also configures a DNS server for containers so they can resolve service names in the cluster.

0fdd4ee6d081:/home/ui-server# cat /etc/resolv.conf # Generated by Docker Engine. # This file can be edited; Docker Engine will not make further changes once it # has been modified. nameserver 127.0.0.11 options ndots:0 # Based on host file: '/etc/resolv.conf' (internal resolver) # ExtServers: [75.75.75.75 75.75.76.76] # Overrides: [] # Option ndots from: internal 0fdd4ee6d081:/home/ui-server# nslookup traefik_traefik Server: 127.0.0.11 Address: 127.0.0.11:53 Non-authoritative answer: Non-authoritative answer: Name: traefik_traefik Address: 10.10.1.5

Local

Ports can be published on the host so that requests to the host are forwarded to the container.

pi@pi:~ $ sudo docker ps CONTAINER ID IMAGE ... PORTS NAMES 8938090adbe9 traefik ... 0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp, 0.0.0.0:1883->1883/tcp, [::]:1883->1883/tcp, 0.0.0.0:7233->7233/tcp, [::]:7233->7233/tcp traefik_traefik.1.rlpz6v41b5za9dlilue3lolle ...

By using macvlan, containers can also be bridged directly to a host’s network interface.

pi@pi:~ $ sudo docker network ls NETWORK ID NAME DRIVER SCOPE ... rhic9yfokyea macvlan macvlan swarm 41b667c736df macvlan-config null local ...

A pool of addresses can be set to be used for macvlan, along with the parent interface that will be bridged.

pi@pi:~ $ sudo docker inspect macvlan-config [ { "Name": "macvlan-config", "Id": "41b667c736df090b7d7a01c6e4eb2bcd590885fe3897505a6615037667671cc6", "Created": "2025-10-03T22:37:14.516512194Z", "Scope": "local", "Driver": "null", "EnableIPv4": true, "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "192.168.1.0/24", "IPRange": "192.168.1.16/28", "Gateway": "192.168.1.1" } ] }, ... "Options": { "parent": "eth0" }, "Labels": {} } ]

By providing a bridged network interface, the container is attached to the host’s network, allowing for more network access such as sending Wake-on-LAN packets, mDNS, etc.

ricardo@cannoli:~$ sudo docker inspect 3fc691c2eb76 [ { "Name": "/home-assistant_home-assistant.1.j2rdwaecyb363om5n7r6xjs1r", ... "NetworkSettings": { ... "Networks": { "macvlan": { "IPAMConfig": {}, "Links": null, "Aliases": null, "MacAddress": "02:42:c0:a8:01:10", "DriverOpts": null, "NetworkID": "rhic9yfokyeat8tufwndxult1", "EndpointID": "954747f30ed1c94a3c541c89ecca1ca113cfd5787f1a7f91d274ef9638d502a7", "Gateway": "192.168.1.1", "IPAddress": "192.168.1.16", "IPPrefixLen": 24, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "DNSNames": [ "home-assistant_home-assistant.1.j2rdwaecyb363om5n7r6xjs1r", "3fc691c2eb76" ] }, ... } } } ]

Stacks

Stacks are used to deploy workloads to the cluster in an organized way and can contain services, networks, volumes, and secrets.

pi@pi:~ $ sudo docker stack ls NAME SERVICES home-assistant 1 registry 1 traefik 2 ...

The nodes will continously monitor the services, restarting and updating them to match the latest stack configuration that was deployed.

pi@pi:~ $ sudo docker service ls ID NAME MODE REPLICAS IMAGE PORTS z9jz8v4rtanb home-assistant_home-assistant replicated 1/1 ghcr.io/home-assistant/home-assistant:2025.9.4 nggnsv5ryhsf registry_registry replicated 1/1 registry@sha256:169211e20e2f2d5d115674681eb79d21a217b296b43374b8e39f97fcf866b375 v8rwsrodm5p2 temporal_temporal-server replicated 1/1 temporalio/auto-setup:1.27.2 ivnnk4xycats traefik_traefik replicated 1/1 traefik@sha256:2f603f8d3abe1dd3a4eb28960c55506be48293b41ea2c6ed4a4297c851a57a05 ...

Stability

For stability, Docker is run in its own systemd slice, separate from the system slice.

pi@pi:~ $ sudo systemctl status pi State: running Jobs: 0 queued Failed: 0 units Since: Thu 1970-01-01 00:00:02 UTC; 55 years 10 months ago CGroup: / ├─user.slice └─user-1000.slice ├─session-3113.scope ├─3516207 sshd: pi [priv] └─3516215 sshd: pi ... ├─docker.slice ├─docker-db298c412b1c85bd85e7b38a91b6dbf35b52011782a5874d9fbf5f359b8f17bf.scope ├─3662458 nginx: master process nginx-debug -g daemon off; └─3663158 nginx: worker process ├─containerd.service ├─3659355 /usr/bin/containerd ... ├─docker.service ├─3660126 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ├─3662911 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.6 -container-port 80 -use-listen-fd ... ├─init.scope └─1 /sbin/init └─system.slice ├─cron.service └─391 /usr/sbin/cron -f ├─bluetooth.service └─586 /usr/libexec/bluetooth/bluetoothd ...

The slice is configured to have a maximum for memory usage and to prioritize the underlying system first. cgroup features are enabled to set these limits. By tuning these limits based on system resources, containers cannot cause resource exhaustion on the host.

pi@pi:~ $ sudo systemctl status docker.slice docker.slice - Docker Slice Loaded: loaded (/etc/systemd/system/docker.slice; static) Active: active since Thu 2025-06-26 00:44:54 UTC; 4 months 20 days ago IO: 46.0G read, 77.3M written Tasks: 313 Memory: 398.9M (max: 1.9G) CPU: 1d 19h 16min 46.980s CGroup: /docker.slice ├─containerd.service ├─3659355 /usr/bin/containerd ... ├─docker-4b9723255f48bd1814dc5187c417f2b0819b03dfaf56969416eaaf5389497016.scope ├─3662671 nginx: master process nginx -g daemon off; └─3663155 nginx: worker process ... └─docker.service ├─3660126 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ├─3662911 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.6 -container-port 80 -use-listen-fd ...

Subscribe for more

Get notified about new pages.
Unsubscribe anytime.