Home ITHome ServerDocker auf Ubuntu installieren – und die Firewall-Falle vermeiden

Docker auf Ubuntu installieren – und die Firewall-Falle vermeiden

by dr
0 Kommentare

Docker unter Ubuntu zu installieren ist unkompliziert – und genau deshalb übersehen die meisten Anleitungen die eine Sache, die auf Ubuntu wirklich zählt: Ubuntu bringt die Firewall ufw mit, und Docker setzt sie stillschweigend außer Kraft. Wer das nicht weiß, betreibt Dienste offen im Netz und hält sie für geschützt.

Diese Anleitung nimmt den offiziellen Installationsweg und weist das Firewall-Problem nachprüfbar nach. Getestet auf Ubuntu 26.04.1 LTS „Resolute Raccoon“, Kernel 7.0, installiert wurden Docker 29.8.0 und Compose v5.5.1 (Stand September 2026). Alle Ausgaben stammen aus diesem Durchlauf.

Das Wichtigste in Kürze

  • Nimm Dockers eigenes Repository – nicht das Snap-Paket und nicht docker.io.
  • Ubuntu nutzt in der Paketquelle UBUNTU_CODENAME, nicht VERSION_CODENAME. Ein kleiner, aber entscheidender Unterschied zu Debian.
  • Der Dienst startet nach der Installation von allein.
  • Docker umgeht ufw. Nachgewiesen in diesem Artikel – mit zwei geprüften Gegenmitteln.

Voraussetzungen

Ein 64-Bit-Ubuntu und ein Benutzer mit sudo. Offiziell unterstützt Docker derzeit:

Version Codename Hinweis
Ubuntu 26.04 LTS Resolute aktuelle LTS – hier getestet
Ubuntu 24.04 LTS Noble weit verbreitet
Ubuntu 22.04 LTS Jammy noch unterstützt

Architekturen: x86_64, arm64, armhf, s390x, ppc64le. Nicht-LTS-Zwischenversionen werden nicht dauerhaft gepflegt – auf einem Server ist LTS die richtige Wahl.

Erst prüfen: liegt schon ein Docker-Snap herum?

Ubuntu bietet Docker auch als Snap an, und der Server-Installer schlägt es unter „Featured Server Snaps“ zur Mitinstallation vor. Das Snap läuft in einer eigenen Sandbox mit abweichenden Pfaden und kollidiert mit der offiziellen Engine. Prüfe deshalb zuerst:

snap list | grep -i docker

Kommt eine Zeile zurück, entferne es vor der Installation:

sudo snap remove --purge docker

Achtung: --purge löscht auch die Daten des Snaps, also dort liegende Container und Volumes.

Schritt 1: Konkurrierende Pakete entfernen

sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

Auf einem frischen System meldet dpkg für jeden Namen „Kein Paket gefunden“. Das ist keine Fehlermeldung, sondern die Auskunft, dass nichts zu entfernen war. Beachte gegenüber Debian den zusätzlichen Eintrag docker-compose-v2 – den gibt es nur in den Ubuntu-Quellen.

Schritt 2: Paketquelle einrichten

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

Beachte den Pfad: /linux/ubuntu/, nicht /linux/debian/. Wer hier die Debian-Anleitung kopiert, bekommt Pakete für die falsche Distribution.

Den Fingerabdruck des Schlüssels solltest du prüfen, bevor dein System ihm vertraut:

gpg --show-keys --with-fingerprint /etc/apt/keyrings/docker.asc

Erwartet wird – identisch für alle Distributionen:

9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88

Nun die Paketquelle im deb822-Format:

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

Hier liegt der feine Unterschied zu Debian: Ubuntu setzt UBUNTU_CODENAME ein und fällt nur ersatzweise auf VERSION_CODENAME zurück. Wichtig wird das bei Abkömmlingen wie Linux Mint oder Pop!_OS – dort steht in VERSION_CODENAME der eigene Name der Distribution, für den Docker keine Pakete anbietet, während UBUNTU_CODENAME die zugrunde liegende Ubuntu-Version nennt.

Kontrolliere das Ergebnis:

Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: resolute
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc
sudo apt update

Dockers Server muss in der Ausgabe auftauchen:

Holen:5 https://download.docker.com/linux/ubuntu resolute InRelease [32,5 kB]
Holen:6 https://download.docker.com/linux/ubuntu resolute/stable amd64 Packages [25,5 kB]

Schritt 3: Installieren

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Am Ende zeigt Ubuntu seine typischen Meldungen von needrestart:

No containers need to be restarted.
No user sessions are running outdated binaries.

Versionen prüfen:

$ docker --version
Docker version 29.8.0, build 88096ef
$ docker compose version
Docker Compose version v5.5.1

Und der Dienst läuft bereits:

$ systemctl is-enabled docker
enabled
$ systemctl is-active docker
active

Schritt 4: Funktionstest und Rechte

sudo docker run hello-world

Erscheint „Hello from Docker!“, ist die Engine einsatzbereit. Für Docker ohne sudo:

sudo usermod -aG docker $USER

Danach abmelden und neu anmelden. Gruppenmitgliedschaften werden bei der Anmeldung gesetzt; in der laufenden Sitzung zeigt id -nG die Gruppe docker noch nicht, und docker ps antwortet weiter mit permission denied while trying to connect to the docker API at unix:///var/run/docker.sock. Für ein einzelnes Terminal hilft newgrp docker.

Und ein Satz zur Einordnung: Wer in der Gruppe docker ist, hat faktisch Root-Rechte auf dem ganzen System – ein Container kann das Dateisystem des Wirts einhängen. Auf deinem Heimserver ist das unkritisch, auf einem geteilten System eine bewusste Entscheidung.

Docker gegen ufw – der Nachweis

Jetzt der Teil, der diesen Artikel von den übrigen unterscheidet. Ubuntu-Server werden fast immer mit ufw abgesichert, meist mit dieser Grundregel:

sudo ufw allow 22/tcp
sudo ufw default deny incoming
sudo ufw enable

Der Zustand danach sieht beruhigend aus:

$ sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing), deny (routed)

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Eingehend ist alles verboten außer SSH. Nun ein Container mit veröffentlichtem Port:

$ sudo docker run -d --name test -p 8080:80 nginx:alpine
$ sudo docker ps --format "{{.Names}}  {{.Ports}}"
test  0.0.0.0:8080->80/tcp, [::]:8080->80/tcp

Port 8080 steht in keiner ufw-Regel. Er müsste also blockiert sein. Der Zugriff von einem anderen Rechner im Netz:

$ curl -s -o /dev/null -w "HTTP %{http_code}\n" http://ubuntu-server:8080
HTTP 200

Der Dienst ist offen. Trotz aktiver Firewall, trotz deny incoming, trotz fehlender Freigabe.

Warum das passiert

Docker verwaltet seine Netzwerkregeln selbst und trägt sie ganz vorn in die Filterketten ein:

$ sudo iptables -L FORWARD -n --line-numbers
Chain FORWARD (policy DROP)
num  target
1    DOCKER-USER
2    DOCKER-FORWARD
3    ufw-before-logging-forward
4    ufw-before-forward
5    ufw-after-forward

Die Reihenfolge ist die ganze Erklärung: Pakete laufen zuerst durch Dockers Ketten. Dort werden sie angenommen und weitergeleitet, bevor ufw überhaupt gefragt wird. Ergänzend zeigt die NAT-Tabelle die Umleitung in den Container:

$ sudo iptables -t nat -L DOCKER -n
Chain DOCKER (2 references)
DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.2:80

Das ist kein Fehler und keine Nachlässigkeit – Docker braucht diese Regeln, um Container überhaupt erreichbar zu machen. Es ist nur eine Eigenschaft, die man kennen muss.

Lösung 1: Nur an localhost binden

Für Heimserver der beste Weg. Ergänze die Adresse vor dem Port:

sudo docker run -d --name test -p 127.0.0.1:8080:80 nginx:alpine

oder in der Compose-Datei:

services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"
    restart: unless-stopped

Im Test lieferte das genau das erwartete Ergebnis – lokal erreichbar, von außen nicht:

$ sudo docker ps --format "{{.Names}}  {{.Ports}}"
test  127.0.0.1:8080->80/tcp

# auf dem Server selbst
$ curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8080
HTTP 200

# von einem anderen Rechner
$ curl -s -o /dev/null -w "HTTP %{http_code}\n" http://ubuntu-server:8080
HTTP 000

Brauchst du den Dienst doch im Netz, stellst du einen Reverse Proxy davor und veröffentlichst allein dessen Port. Dann entscheidet der Proxy über Zugriff und Verschlüsselung, nicht jeder einzelne Container.

Lösung 2: Regeln in der DOCKER-USER-Kette

Die Kette DOCKER-USER ist ausdrücklich für eigene Regeln vorgesehen – Docker überschreibt sie nicht. Sie wird vor Dockers eigenen Regeln ausgewertet und ist damit der einzige Ort, an dem du wirksam eingreifen kannst.

Zuerst den Namen deines Netzwerk-Interfaces ermitteln:

ip route show default

Dann alles verwerfen, was von außerhalb des eigenen Netzes kommt:

sudo iptables -I DOCKER-USER -i ens33 ! -s 192.168.1.0/24 -j DROP

Im Test wirkte eine solche Regel unmittelbar: Zugriff von außen HTTP 000, lokal weiterhin HTTP 200. Nach dem Entfernen der Regel mit -D statt -I war der Port sofort wieder offen.

Nicht vergessen: Diese Regeln überleben keinen Neustart. Dauerhaft machst du sie mit iptables-persistent:

sudo apt install iptables-persistent
sudo netfilter-persistent save

Was ufw-Regeln für Container nicht bringen

Ein häufiger Irrtum: sudo ufw deny 8080 hilft nicht. Die Regel landet in ufw-Ketten, die für Container-Verkehr nie erreicht werden. Du siehst eine Regel, die nichts tut – gefährlicher als gar keine, weil sie Sicherheit vortäuscht.

Ubuntu-Besonderheit: LXD im Weg

Ubuntu Server bringt LXD mit, Canonicals eigene Container-Verwaltung. Beide Systeme können parallel laufen, streiten sich aber gelegentlich um Netzwerkbrücken und Weiterleitungsregeln. Wenn Container plötzlich keine Verbindung nach außen bekommen, prüfe:

systemctl is-active snap.lxd.daemon
ip -brief link | grep -E 'lxdbr|docker'

Nutzt du LXD nicht, kannst du es abschalten:

sudo snap disable lxd

Wichtige Dateien und Verzeichnisse

Pfad Bedeutung
/var/lib/docker Abbilder, Container, Volumes – der Platzfresser
/etc/docker/daemon.json Konfiguration des Dienstes; existiert standardmäßig nicht
/etc/apt/sources.list.d/docker.sources Die Paketquelle aus Schritt 2
/var/run/docker.sock Der Socket zwischen Befehl und Dienst
/etc/ufw/ ufw-Regeln – für Container-Ports ohne Wirkung

Speicherverbrauch im Blick behalten mit docker system df, aufräumen mit docker system prune.

Hardware für den Ubuntu-Server

Für einen Heimserver mit mehreren Containern zählen RAM und eine ordentliche SSD mehr als die Prozessorleistung.

* Affiliate-Links: Als Amazon-Partner verdienen wir an qualifizierten Käufen. Bitte prüfe die Kompatibilität mit deinem System.

Häufige Fragen

Soll ich das Docker-Snap oder das offizielle Paket nehmen?

Das offizielle Paket. Das Snap läuft in einer Sandbox mit eigenen Pfaden, was Volumes, Netzwerke und Rechte verkompliziert. Es ist außerdem nicht der von Docker unterstützte Weg.

Warum erscheint mein Container-Port trotz ufw offen?

Weil Docker seine Regeln vor ufw einträgt – siehe den Abschnitt oben. Die Lösung ist 127.0.0.1: vor dem Port oder eine Regel in DOCKER-USER, nicht in ufw.

Funktioniert diese Anleitung auch auf Linux Mint oder Pop!_OS?

Ja, genau wegen UBUNTU_CODENAME. Diese Distributionen tragen dort die zugrunde liegende Ubuntu-Version ein, für die es Pakete gibt. Prüfe vorher mit . /etc/os-release; echo $UBUNTU_CODENAME, dass ein unterstützter Codename herauskommt.

Wie aktualisiere ich Docker?

sudo apt update && sudo apt upgrade. Neue Versionen kommen über die eingerichtete Quelle automatisch mit.

Kann ich Docker auf Ubuntu Desktop nutzen?

Ja, identisch zum Server. Nur die ufw-Frage entfällt meist, weil die Firewall auf Arbeitsplätzen oft nicht aktiv ist – was das Problem nicht kleiner macht, sondern nur unsichtbarer.

Wie entferne ich Docker vollständig?

sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo rm -rf /var/lib/docker /var/lib/containerd

Der zweite Befehl löscht alle Abbilder, Container und Volumes unwiderruflich.

Fazit

Die Installation selbst ist in fünf Minuten erledigt und unterscheidet sich von Debian nur in zwei Details: dem Repository-Pfad und UBUNTU_CODENAME statt VERSION_CODENAME.

Der eigentliche Unterschied liegt danach. Auf einem Ubuntu-Server, den du mit ufw abgesichert hast, ist die Annahme „die Firewall schützt meine Container“ falsch – und zwar auf eine Weise, die man nicht bemerkt, solange nichts passiert. Zwei Handgriffe reichen: Ports an 127.0.0.1 binden und, wo das nicht geht, eine Regel in DOCKER-USER setzen.

Anleitungen für andere Systeme stehen in der Übersicht: Docker auf jedem System installieren. Ein sinnvoller erster Dienst nach der Installation ist Portainer; wer Docker vor allem für Medien nutzen will, findet in Jellyfin einen guten Einstieg.

You may also like

Hinterlasse einen Kommentar