In diesem Tutorial gehe ich auf zwei unterschiedliche Installationen von Docker ein und fasse die Vor- und Nachteile zusammen. Des Weiteren werde ich zu jedem Befehl eine Erklärung oder ein Kommentar schreiben.
Variante 1: Standardinstallation (Rootful)#
Damit eine saubere Installation stattfindet, sollten alte sowie Pakete die einen konflikt verursachen können, deinstalliert werden.
Debian 12
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do
sudo apt-get remove -y "$pkg"
done
Ubuntu 24.04
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove -y "$pkg"
done
for pkg in ...; do … done
iteriert über die aufgezählten Paketnamen.
In jedem Durchlauf ersetzt die Shell $pkg
mit einem Namen (z. B. docker.io) und führt sudo apt-get remove -y "$pkg"
aus.
Vorteil: Ein einziger Block entfernt mehrere Pakete robust in einer konsistenten Reihenfolge. Sollten Pakete nicht vorhanden sein ist dies kein problem, sie werden i.d.R. übersprungen.Offizielles Docker-Repository einrichten#
Wir installieren Docker aus dem offiziellen Docker-APT-Repository (aktuellere Versionen).
# Basis-Werkzeuge + Keyring-Verzeichnis
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
# Verteilung erkennen und passenden GPG-Key + Repo eintragen
if grep -qi debian /etc/os-release; then
# Debian: GPG-Key holen und als .asc ablegen
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo tee /etc/apt/keyrings/docker.asc > /dev/null
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Repo-Zeile: 'signed-by' bindet den Key nur für dieses Repo ein
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
else
# Ubuntu: analog
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo tee /etc/apt/keyrings/docker.asc > /dev/null
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
|sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
fi
# Paketindizes aktualisieren
sudo apt-get update
Warum diese Schritte?#
/etc/apt/keyrings
: Moderner, sicherer Ort für GPG-Keys.signed-by=…
: Bindet den Schlüssel nur an dieses Repo.$(dpkg --print-architecture)
: Nimmt automatisch amd64, arm64, usw.$VERSION_CODENAME
: Wählt z. B. bookworm oder noble — so muss nichts von Hand angepasst werden.
Docker & Compose installieren#
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Funktionstest#
sudo docker run --rm hello-world
--rm
sorgt dafür, dass der Container im Anschluss wieder gelöscht wird.(Optional) Docker als normaler Benutzer nutzen#
Wenn docker ohne sudo
ausgeführt werden soll, kann man die Gruppe docker
nutzen.
# Gruppe anlegen, wenn sie nicht existiert
sudo groupadd docker 2>/dev/null || true
# Aktuellen Benutzer der Gruppe hinzufügen
sudo usermod -aG docker "$USER"
# Neue Gruppenzugehörigkeit in der aktuellen Shell aktivieren:
newgrp docker
# Test ohne sudo:
docker run --rm hello-world
Wichtig#
Die Mitgliedschaft in der Gruppe docker
gewährt faktisch root-ähnliche Rechte, weil Docker beliebige Mounts/Devices/Netzwerke orchestriert. Für gemeinsam genutzte Server ist Rootless oft die bessere Wahl (siehe Tabelle unten).
Variante 2: Rootless (Docker ohne Root-Rechte)#
Der Docker-Daemon und die Container laufen im User-Namespace des aktuellen Benutzers. Ideal auf Shared Hosts oder bei strikten Compliance-Vorgaben.
Voraussetzungen installieren#
sudo apt-get update
sudo apt-get install -y uidmap dbus-user-session docker-ce-rootless-extras
- uidmap: liefert newuidmap/newgidmap (User Namespaces)
- dbus-user-session: für systemd –user Dienste hilfreich
- docker-ce-rootless-extras: Tools wie dockerd-rootless-setuptool.sh
/etc/subuid
& /etc/subgid
sollten für deinen Benutzer Bereiche wie username:100000:65536
enthalten (werden bei vielen Systemen automatisch gepflegt). Das ermöglicht die Abbildung vieler „Container-UIDs“ auf deinen Benutzer.
(Falls aktiv) Rootful-Daemon stoppen#
sudo systemctl disable --now docker.service docker.socket
# alter Socket ggf. entfernen, damit es keinen Konflikt gibt
sudo rm -f /var/run/docker.sock
Rootless einrichten & starten#
# OHNE sudo als normaler Benutzer:
dockerd-rootless-setuptool.sh install
# Wichtige Variablen in die Shell einhängen:
echo 'export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock' >> ~/.bashrc
. ~/.bashrc
# Docker als systemd --user Dienst starten und beim Login automatisch starten
systemctl --user start docker
systemctl --user enable docker
# Linger erlaubt user-dienste ohne aktive Login-Session am System zu halten
sudo loginctl enable-linger "$(whoami)"
# Test:
docker run --rm hello-world
Einschränkungen & Hinweise:#
- Privilegierte Ports (<1024): Ohne zusätzlichen Helfer (rootlesskit + Port-Helpers) nicht direkt bindbar.
- Netzwerkmodi: host und einige Overlay-Szenarien sind limitiert.
- Leichte Overheads durch User-Namespace/Slirp4netns.
- Gerätezugriff (GPU, spezielle USB/Block-Devices) ist eingeschränkt/aufwändiger.
Rootful vs. Rootless — Gegenüberstellung#
Fähigkeit / Merkmal | Rootful (Standard) | Rootless |
---|---|---|
Docker-Daemon als Systemdienst (systemd) | ✅ | ⚠️ (als systemd --user ) |
Ohne sudo nutzbar | ⚠️ (Gruppe docker – riskant) | ✅ |
Sicherheitsoberfläche (Angriffsfläche) | ⚠️ (Daemon als root) | ✅ (kein Root zur Laufzeit) |
Host-Netzwerkmodus (--network=host ) | ✅ | ❌ |
Privilegierte Ports <1024 binden | ✅ | ⚠️ (Port-Helper nötig) |
Overlay-/Mehrhost-Netze | ✅ | ⚠️/❌ (je nach Setup) |
Swarm/klassisches Compose-Netzwerk | ✅ | ⚠️ (teilweise limitiert) |
GPU-Passthrough (NVIDIA/AMD) | ✅ | ⚠️ (zus. Konfig nötig, teils nicht möglich) |
Zugriff auf Host-Devices/Volumes | ✅ | ⚠️ (restriktiver) |
Performance allgemein | ✅ | ⚠️ (leichter Overhead möglich) |
Einfaches Debugging/Standardpfade | ✅ | ⚠️ (User-Pfade variieren) |
Geeignet für Shared-Hosts / Multi-User | ⚠️ | ✅ |
Risiko durch Gruppe docker | ⚠️ (hoch) | ✅ (entfällt) |
Docker Compose: Installation prüfen#
Nach der oben gezeigten Installation ist Compose v2 bereits als Plugin dabei:
docker compose version
Feinschliff#
Docker-Dienst beim Booten starten (Rootful)
sudo systemctl enable docker.service containerd.service # aktivieren
# oder:
sudo systemctl disable docker.service containerd.service # deaktivieren
Firewall-Hinweis#
Docker erstellt NAT-Regeln selbst. Wenn du UFW/Firewalld nutzt, beachte, dass exponierte Ports Docker-seitig schon geöffnet werden. Lege (falls benötigt) Regeln in iptables an, um eingehenden Traffic global zu filtern.
Logging#
Logging-Treiber local
rotiert automatisch; bei json-file
setze explizit Optionen, z. B.:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Deinstallation / Aufräumen#
# Container/Images/Volumes auflisten & ggf. sichern!
docker ps -a
docker volume ls
# Rootful deinstallieren:
sudo apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo rm -rf /var/lib/docker /var/lib/containerd
# Rootless stoppen (als Benutzer):
systemctl --user disable --now docker
Docker Compose - Beispiele#
Adguard Home mit Unbound#
Aufbau
- adguard (DNS-Filter & Web-UI)
- unbound (rekursiver DNS-Resolver, nutzt Root-Zonen/Root-Hints)
- Gemeinsames Bridge-Netzwerk dnsnet → Service-Discovery per Namen (unbound).
dns-stack/
├─ compose.yaml
├─ adguard/
│ ├─ conf/
│ │ └─ AdGuardHome.yaml # (optional: vorkonfiguriert, siehe unten)
│ └─ work/ # von AdGuard genutzt
└─ unbound/
Verzeichnise anlegen
mkdir -p dns-stack/{adguard/{conf,work},unbound}
Docker Stack#
systemd-resolved
mit einem Stub-Resolver an 127.0.0.53
. Wenn du 53:53
auf dem Host binden willst, deaktiviere den Stub-Listener (siehe „Port-Kollisionen“ weiter unten) – oder benutze testweise andere Host-Ports.name: dns-stack
networks:
dnsnet:
name: dnsnet
driver: bridge
services:
unbound:
image: crazymax/unbound:latest
container_name: unbound
restart: unless-stopped
networks: [dnsnet]
# Optional: Drop-in-Konfigs hierhin legen
volumes:
- ./unbound/config:/config:ro
adguard:
image: adguard/adguardhome:latest
container_name: adguard
restart: unless-stopped
networks: [dnsnet]
depends_on:
- unbound # wartet nur, bis unbound gestartet ist
ports:
- "53:53/tcp"
- "53:53/udp"
- "3000:3000/tcp" # nur für die Ersteinrichtung notwendig
- "80:80/tcp"
- "443:443/tcp"
volumes:
- ./adguard/work:/opt/adguardhome/work
- ./adguard/conf:/opt/adguardhome/conf
AdGuard Home → Unbound als Upstream#
Web-UI - Upstream
docker compose up -d
starten.
Öffne http://<host>:3000
→ Setup-Assistenten folgen und einrichten.
Bei Upstream-DNS trägst du udp://unbound:5053
ein (nur im Docker-Netz relevant; AdGuard löst unbound über Docker-DNS auf).
Speichern → fertig.
Im Anschluss ist in ./dns-stack/adguard/conf/
die AdGuardHome.yaml
angelegt worden. Hier sind folgende Einträge zu ergänzen oder zu ändern. Mit diesen Einstellungen ist zum einen der Upstream-DNS bereits auf Unbound eingestellt. Damit AdGuard im Docker-Netzwerk nun auch Unbound mit dem Hostnamen aufgelöst werden kann, ist die zweite Änderung notwendig!
upstream_dns:
- udp://unbound:5053
bootstrap_dns:
- 127.0.0.11
Stack starten und Funktion überprüfen#
# im Ordner dns-stack/
docker compose up -d
docker compose ps
# Teste eine Abfrage gegen AdGuard (Host-Seite):
dig @127.0.0.1 -p 53 example.com A +short
# oder über das LAN von einem anderen Client:
# dig @<DEIN_HOST_IP> example.com A +short
# Logs ansehen:
docker compose logs -f unbound
docker compose logs -f adguard