- Vrealmatic
- Ubuntu Server
- Docker
Bezpečný Docker na Ubuntu Serveru
Co je Docker a jak ho na serveru bezpečně nainstalovat a nastavit, krok za krokem
Instalace z ověřeného zdroje, přístup jen pro správce, porty, které neobejdou firewall, a kontejnery s minimem práv. Jako alternativa režim, ve kterém Docker neběží s právy správce vůbec.
Obsah návodu
00 · Přehled
Bezpečný Docker v kostce
Návod vysvětlí, co je Docker, a krok za krokem ho bezpečně nainstaluje a nastaví na Ubuntu Serveru. Žádný hromadný skript: u každého kroku je napsané, co dělá, proč je potřeba a jak ověřit, že se povedl.
Celé nastavení v kostce
- Ověřená instalace - Docker z oficiálního zdroje, pravost balíčků ověřená podpisovým klíčem
- Přístup jen pro správce - Docker ovládá jen
sudo, skupinadockerzůstane prázdná - Bezpečné výchozí nastavení - logy nezaplní disk, porty kontejnerů se otevírají jen pro server samotný
- Firewall i pro kontejnery - omylem otevřený port zůstane zvenku zavřený
- Kontejnery s minimem práv - bez práv správce, bez možnosti měnit vlastní soubory, s omezeným výkonem a pamětí
Co potřebujete vyřešit?
| Otázka | Kde najdete odpověď |
|---|---|
| Co je Docker a potřebuji ho? | Co je Docker → |
| Proč je Docker bezpečnostní riziko? | Bezpečnostní model → |
| Jak ho bezpečně nainstalovat? | Instalace → |
| Můžu se přidat do skupiny docker? | Kdo smí Docker ovládat → |
| Proč je kontejner dostupný z internetu, i když ho firewall blokuje? | Síť a firewall → |
| Jak má vypadat bezpečný compose.yaml? | Kontejnery → |
| Jde to úplně bez práv správce? | Rootless režim → |
| Něco nefunguje. | Řešení problémů → |
Ověřeno podle oficiální dokumentace k 27. 10. 2026: Docker Engine 29.9, Docker Compose 5.6, Ubuntu Server 22.04, 24.04 a 26.04 LTS. Docker Desktop a Swarm návod neřeší.
01 · Docker
Co je Docker a k čemu slouží
Docker spouští aplikace v kontejnerech. Kontejner obsahuje aplikaci a vše, co ke svému běhu potřebuje (knihovny, nastavení). Na serveru běží jako běžný program, vidí ale jen své soubory a svou síť a má omezený výkon a paměť. Aplikace proto běží všude stejně a navzájem si nepřekáží.
Kontejner vs. virtuální stroj
| Kontejner (Docker) | Virtuální stroj | |
|---|---|---|
| Operační systém | Sdílí jádro systému se serverem | Má vlastní celý systém |
| Náročnost | Start za sekundy, minimum paměti navíc | Start v desítkách sekund, stovky MB až GB paměti |
| Oddělení od serveru | Slabší, záleží na správném nastavení | Silnější |
| Hodí se pro | Aplikace a služby: web, databáze, automatizace | Jiný operační systém, nedůvěryhodný kód |
Kontejner tedy neodděluje aplikaci od serveru tak pevně jako virtuální stroj. Jeho izolace je dobrá, ale jen když je správně nastavená. O tom je zbytek návodu.
Pojmy, které budete potřebovat
| Pojem | Význam |
|---|---|
| Image | Šablona, ze které se kontejner spouští (např. nginx:1.29). Stahuje se z registry, typicky Docker Hub. |
| Digest | Jedinečný otisk konkrétní verze image (sha256:…). Na rozdíl od označení verze (tagu) se nikdy nezmění. |
| Kontejner | Spuštěná image. Co aplikace zapíše dovnitř, zmizí se smazáním kontejneru. |
| Volume | Úložiště pro data, která mají přežít smazání kontejneru (např. databáze). |
| Démon (dockerd) | Služba na pozadí, která kontejnery spouští a spravuje. Příkaz docker jí jen posílá pokyny. |
| Socket (docker.sock) | Soubor, přes který příkaz docker s démonem komunikuje. Kdo do něj smí zapisovat, ovládá Docker. |
| Root, sudo | Root je správce serveru s neomezenými právy. sudo spustí jeden příkaz s právy roota po ověření hesla. |
| Port | Číslo „dveří“ na serveru, na kterých služba přijímá spojení (web 80 a 443, SSH 22). |
| Publikovaný port | Port serveru, který Docker přesměruje do kontejneru. Bez něj je kontejner zvenku nedostupný. |
| Firewall (UFW) | Pravidla, která rozhodují, která spojení na server pustit. UFW je firewall Ubuntu. |
| Capabilities | Jednotlivá dílčí práva roota (např. měnit síť). Kontejner by jich měl mít co nejméně. |
| Docker Compose | Popis kontejnerů a jejich nastavení v souboru compose.yaml, spouští se příkazem docker compose. |
Kdy Docker na serveru dává smysl
Docker ano
- Aplikace, které výrobce dodává jako image (Home Assistant, n8n, Nextcloud…)
- Víc aplikací na jednom serveru, které potřebují různé verze knihoven
- Nastavení uložené v souborech, snadno přenositelné a verzovatelné v Gitu
Spíš klasická instalace
- Jedna služba, kterou Ubuntu nabízí jako balíček (
nginx,postgresql) – aktualizuje se sama - Nedůvěryhodný kód nebo potřeba silného oddělení – patří do virtuálního stroje
02 · Rizika
Kde je Docker zranitelný
Docker má čtyři slabá místa. Každý další krok návodu řeší některé z nich.
- Docker běží s právy správce (roota). Kdo mu může zadávat příkazy, převezme celý server: stačí spustit kontejner, který vidí všechny soubory serveru.
- Přístup k Dockeru = práva správce. Skupina
dockerproto dává plná práva bez hesla. Socketdocker.socknesmí do kontejnerů ani na síť. - Docker obchází firewall. Spojení na publikovaný port přesměruje do kontejneru dřív, než ho UFW zkontroluje. A port ve výchozím stavu otevře na všech adresách serveru, tedy i do internetu.
- Kontejnery sdílí jádro serveru. Výchozí omezení jsou rozumná, ale volby jako
privilegednebonetwork_mode: hostje ruší.
INPUT). Spojení do kontejneru jde jinudy (FORWARD), kam pravidla UFW nedosáhnou. Pravidla pro kontejnery proto patří do řetězce DOCKER-USER (kapitola 07).- Instalace z ověřeného zdroje (kapitola 04).
- Docker ovládá jen správce přes sudo, nebo běží bez práv správce (kapitoly 05 a 09).
- Bezpečné výchozí nastavení a firewall i pro kontejnery (kapitoly 06 a 07).
- Každý kontejner s minimem práv (kapitola 08).
03 · Než začnete
Předpoklady a volba režimu
Co připravit předem, jaký režim zvolit a které hodnoty v příkazech nahradit svými.
- Ubuntu Server 22.04, 24.04 nebo 26.04 LTS (64bit).
- Uživatel
<ADMIN_USER>s právemsudo(správa uživatelů) a přihlášení SSH klíčem. - Zapnutý firewall UFW (průvodce Ubuntu Serverem). Ideálně SSH dostupné jen přes Tailscale nebo z lokální sítě.
- Automatické bezpečnostní aktualizace (unattended-upgrades). Kontejnery sdílí jádro serveru, jeho aktuálnost je proto zásadní.
Běžný, nebo rootless režim?
| Běžný (rootful) | Rootless | |
|---|---|---|
| Docker běží jako | správce (root) | obyčejný uživatel |
| Když útočník prolomí kontejner | získá celý server | získá jen uživatele <DOCKER_USER> |
| Firewall UFW | porty kontejnerů obchází (řeší kapitola 07) | platí i pro kontejnery |
| Omezení | žádná | porty pod 1024 až po úpravě, některé image nefungují, kontejner nevidí skutečnou IP návštěvníka |
| Kdy zvolit | Většina serverů, funguje se vším | Chcete co nejmenší dopad případného útoku a omezení vám nevadí |
Instalace (kapitola 04) je pro oba režimy stejná. Kapitoly 05–08 popisují běžný režim, rootless navazuje v kapitole 09.
Hodnoty, které nahradíte svými
| Placeholder | Význam | Příklad / kde ho zjistit |
|---|---|---|
<ADMIN_USER> | Váš uživatel s právem sudo | vladimir |
<DOCKER_USER> | Uživatel bez sudo pro rootless režim (jen kapitola 09) | dockerapp |
<EXT_IF> | Síťové rozhraní serveru směrem do internetu | ip -4 route show default (slovo za dev) |
<LAN_SUBNET> | Vaše lokální síť (volitelné) | 192.168.88.0/24 |
<SERVER> | Adresa, přes kterou se k serveru připojujete SSH | Tailscale IP, IP v lokální síti, hostname |
<SSH_PORT> | Port SSH | sudo sshd -T | grep -i '^port' |
<PUBLIC_IP> | Veřejná IP serveru (u serveru doma IP routeru) | panel poskytovatele / router |
<PROJECT> | Název projektu (složka v /opt/stacks/) | web |
<DIGEST> | Otisk verze image | kapitola 08 |
Samotná instalace přístup k serveru neohrozí. Riziková je jen úprava firewallu v kapitole 07: chyba v souboru může zabránit načtení firewallu. Předem si proto ověřte jiný přístup k serveru (konzole poskytovatele, fyzický přístup, SSH z lokální sítě).
04 · Instalace
Instalace z oficiálního repozitáře
Docker instalujeme z jeho oficiálního repozitáře (zdroje balíčků pro APT). Má nejnovější verze a bezpečnostní opravy dostává jako první. Než mu systém začne věřit, ověříme podpisový klíč: ten zaručí, že balíčky opravdu pocházejí od Dockeru.
Nepoužíváme
- Skript
get.docker.com(curl | sh) – spustí cizí kód bez kontroly; Docker ho doporučuje jen pro testování - Snap
docker– data jinde a vlastní aktualizace, návody s ním nepočítají - Balíček
docker.ioz Ubuntu spolu s balíčky Dockeru – navzájem kolidují
Používáme
- Oficiální repozitář Dockeru
- Podpisový klíč platný jen pro tento repozitář
- Kontrolu klíče a původu balíčků před instalací
Krok 1: Odstraňte konfliktní balíčky
Starší neoficiální balíčky kolidují s oficiálními. První příkaz vypíše, které jsou nainstalované; když nevypíše nic, druhý přeskočte. Data kontejnerů zůstanou.
# list unofficial packages that conflict with Docker's own (no output = nothing to remove)
dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null
# remove only those that are installed; /var/lib/docker (images, volumes) is kept
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null | cut -f1)# only if Docker was installed as a snap (this deletes the snap's data - back it up first)
sudo snap remove dockerKrok 2: Stáhněte a ověřte podpisový klíč
Klíč stáhneme do dočasného souboru a porovnáme jeho otisk s oficiální hodnotou 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88. Výstup musí odpovídat druhému bloku.
sudo apt update
sudo apt install ca-certificates curl gnupg
# download the key to a temporary file and inspect it before trusting it
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /tmp/docker.asc
gpg --show-keys --with-fingerprint --keyid-format long /tmp/docker.ascpub rsa4096/8D81803C0EBFCD88 2017-02-22 [SCEA]
Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
uid Docker Release (CE deb) <docker@docker.com>
sub rsa4096/7EA0A9C3F273FCD8 2017-02-22 [S]Nepokračujte. Smažte /tmp/docker.asc a zjistěte příčinu (proxy, podvržená adresa, změna klíče oznámená na docs.docker.com).
Krok 3: Uložte klíč
Klíč uložíme tak, aby mu systém věřil jen u repozitáře Dockeru, ne u ostatních.
# keyring for locally added repositories; the key is trusted only by the repo that points to it
sudo install -d -m 0755 -o root -g root /etc/apt/keyrings
sudo install -m 0644 -o root -g root /tmp/docker.asc /etc/apt/keyrings/docker.asc
rm /tmp/docker.ascKrok 4: Přidejte repozitář
Příkaz zjistí verzi Ubuntu a typ procesoru, vypíše je ke kontrole a zapíše repozitář. Druhý blok ověří, že se Docker bude instalovat odtud.
CODENAME="$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")"
ARCH="$(dpkg --print-architecture)"
echo "$CODENAME $ARCH" # e.g. noble amd64
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $CODENAME
Components: stable
Architectures: $ARCH
Signed-By: /etc/apt/keyrings/docker.asc
EOF
cat /etc/apt/sources.list.d/docker.sourcessudo apt update
apt-cache policy docker-ce
# Candidate: 5:29.x… from https://download.docker.com/linux/ubuntu <CODENAME>/stableVe výpisu musí být verze 5:29.x a adresa download.docker.com. Chyba NO_PUBKEY znamená, že nesedí cesta ke klíči.
Krok 5: Nainstalujte Docker a vyzkoušejte ho
Nainstaluje Docker, příkaz docker a rozšíření pro sestavování images a pro Compose. Docker se spustí sám a startuje se serverem. Testovací kontejner hello-world ověří, že vše funguje; pak ho smažeme.
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsystemctl status docker containerd --no-pager
sudo docker version
sudo docker run --rm hello-world
sudo docker image rm hello-worldPo přechodu na novou verzi Ubuntu upravte v docker.sources řádek Suites: na její codename. Přechod cizí repozitáře vypíná.
05 · Přístup
Kdo smí Docker ovládat
Mnoho návodů radí přidat se do skupiny docker, aby nebylo nutné psát sudo. Na serveru to nedělejte: člen skupiny docker má bez hesla plná práva správce. Stačí mu jeden příkaz:
# anyone in the docker group gets a root shell on the host with this one line:
docker run --rm -it -v /:/host alpine chroot /hostObejde tím heslo, 2FA i záznam v logu, které vyžaduje sudo. Útočníkovi stačí ukradený SSH klíč. Docker proto spouštějte přes sudo docker. Kdo chce Docker bez sudo, má použít rootless režim.
Skupina docker musí zůstat prázdná
getent group docker # → docker:x:<GID>: (nobody after the last colon)
# if someone is there, remove them:
sudo gpasswd -d <USER> dockerNezapínejte vzdálený přístup k Dockeru (port 2375/2376, volba hosts, „Expose daemon on tcp“). Kdo se k němu připojí, ovládá server. Server spravujte přes SSH.
docker.sock do kontejneru nepatří
Některé nástroje (Portainer, Traefik, Watchtower, různé dashboardy) chtějí do svého kontejneru připojit docker.sock. Kdo pak ovládne takový kontejner, ovládne celý server. Pokud nástroj opravdu potřebujete, dejte mezi něj a socket prostředníka (socket proxy), který povolí jen čtení.
06 · Nastavení
Bezpečné výchozí nastavení
Výchozí nastavení Dockeru je pohodlné, ne bezpečné. Soubor /etc/docker/daemon.json ho změní tak, aby jedna chyba v nastavení kontejneru neotevřela port do internetu ani nezaplnila disk. Pokud soubor už existuje, hodnoty doplňte a nic nezdvojujte.
sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "5"
},
"live-restore": true,
"no-new-privileges": true,
"icc": false,
"ip": "127.0.0.1",
"default-network-opts": {
"bridge": {
"com.docker.network.bridge.host_binding_ipv4": "127.0.0.1"
}
},
"default-address-pools": [
{ "base": "172.30.0.0/16", "size": 24 }
]
}
EOF
sudo chmod 0644 /etc/docker/daemon.json| Nastavení | Co dělá |
|---|---|
log-driver, log-opts | Výchozí logy se nikdy nemažou a časem zaplní disk. Takto se komprimují a mají nejvýš 5 × 10 MB na kontejner. |
live-restore | Kontejnery běží dál i při restartu nebo aktualizaci Dockeru. |
no-new-privileges | Program v kontejneru si nemůže sám zvýšit práva (např. přes sudo nebo su). |
icc | Kontejnery ve výchozí síti spolu nemohou komunikovat. Sítě Compose řeší kapitola 08. |
ip | Porty kontejnerů se ve výchozím stavu otevřou jen pro server samotný (127.0.0.1), ne do internetu. |
default-network-opts | Totéž pro sítě, které vytváří Compose. |
default-address-pools | Rozsah adres pro sítě kontejnerů. Výchozí rozsah může kolidovat s vaší sítí nebo VPN a část sítě pak přestane fungovat. |
Krok 1: Zkontrolujte soubor a restartujte Docker
Kontrola odhalí chybu dřív, než by kvůli ní Docker nenastartoval.
sudo dockerd --validate --config-file=/etc/docker/daemon.json # → configuration OK
sudo systemctl restart docker
sudo docker info --format '{{.LoggingDriver}} live-restore={{.LiveRestoreEnabled}}'
sudo docker info | grep -A5 'Security Options' # apparmor, seccomp (builtin), cgroupnsV části Security Options musí být apparmor, seccomp a cgroupns – výchozí bezpečnostní omezení kontejnerů.
Krok 2: Vytvořte existující kontejnery znovu
Nové nastavení platí jen pro nově vytvořené kontejnery a sítě. Data ve volumes zůstanou.
# daemon defaults apply only to newly created containers and networks
cd /opt/stacks/<PROJECT>
sudo docker compose down && sudo docker compose up -d # volumes are keptNěkteré image při startu volají sudo nebo su a s no-new-privileges selžou. Povolte výjimku jen pro daný kontejner (security_opt: ["no-new-privileges:false"]), nebo najděte image, která práva správce nepotřebuje.
Pokročilá volba "userns-remap": "default" zařídí, že správce v kontejneru není správcem na serveru. Komplikuje ale práva k souborům a vylučuje některé volby. Stejnou ochranu dá jednodušeji rootless režim.
07 · Síť
Porty a firewall
Cíl: kontejner není z internetu dostupný, pokud to výslovně nechcete. A když se port otevře omylem, zastaví ho firewall.
Kam publikovat porty
| Kdo má ke službě přistupovat | Nastavení v compose.yaml | Poznámka |
|---|---|---|
| Jen jiné kontejnery (např. databáze) | žádné ports: | Kontejnery ve stejné síti se najdou podle názvu služby. |
| Jen server samotný | "127.0.0.1:8080:80" | Adresu pište vždy, i když je díky kapitole 06 výchozí. |
| Vy, vzdáleně (administrace) | "127.0.0.1:8080:80" + SSH tunel | Port se nikam neotevírá, viz níže. |
| Kdokoli z internetu (web) | "0.0.0.0:443:443" | Jen u jednoho kontejneru, který web zpřístupňuje (reverse proxy), a s výjimkou v DOCKER-USER. |
Ke službě na 127.0.0.1 se vzdáleně dostanete přes SSH tunel. Port se tak nikam neotevírá a funguje to i s Tailscale, který povoluje jen SSH:
# on your PC: forward local port 8080 to 127.0.0.1:8080 on the server
ssh -N -L 8080:127.0.0.1:8080 -p <SSH_PORT> <ADMIN_USER>@<SERVER>
# then open http://127.0.0.1:8080 in the browser on your PCJde to, ale při startu serveru může být Docker rychlejší než Tailscale a kontejner pak nenastartuje (chyba cannot assign requested address). SSH tunel tento problém nemá.
Nepoužívejte network_mode: host (každý port kontejneru je pak port serveru) ani "iptables": false (rozbije síť kontejnerů a zpřístupní je z lokální sítě).
Firewall i pro kontejnery: řetězec DOCKER-USER
Pravidla v řetězci DOCKER-USER platí pro spojení do kontejnerů a Docker je nikdy nepřepíše. Pravidla níže zahodí každé spojení zvenku do kontejneru kromě portů, které výslovně povolíte. Kontejnery se přitom ven připojit mohou (aktualizace, API). Uložíme je do /etc/ufw/after.rules, takže je UFW načte při každém startu.
Krok 1: Zjistěte síťové rozhraní a zálohujte soubor
ip -4 route show default # → default via … dev <EXT_IF> …
sudo cp -a /etc/ufw/after.rules /etc/ufw/after.rules.bakKrok 2: Zapněte pojistku
Za 10 minut vrátí původní soubor a pravidla smaže. Když vše funguje, v kroku 5 ji zrušíte.
# safety net: in 10 minutes restore the original file and empty DOCKER-USER
sudo systemd-run --unit=rollback-docker-user --on-active=10min \
/bin/sh -c 'cp -a /etc/ufw/after.rules.bak /etc/ufw/after.rules; iptables -F DOCKER-USER; ufw reload'Krok 3: Přidejte pravidla
Blok vložte na úplný konec /etc/ufw/after.rules (sudo nano /etc/ufw/after.rules) a <EXT_IF> nahraďte názvem rozhraní. Řádky s porty 80 a 443 nechte jen pro veřejný web; bez nich není zvenku dostupný žádný kontejner.
# BEGIN DOCKER-USER
*filter
:DOCKER-USER - [0:0]
# replies to existing connections (including those a container opened to the outside)
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
# explicitly allowed public ports (original destination port, before DNAT)
-A DOCKER-USER -i <EXT_IF> -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j RETURN
-A DOCKER-USER -i <EXT_IF> -p tcp -m conntrack --ctorigdstport 443 --ctdir ORIGINAL -j RETURN
# optional: your whole LAN may reach published ports
# -A DOCKER-USER -i <EXT_IF> -s <LAN_SUBNET> -j RETURN
# drop everything else coming from the external interface into containers
-A DOCKER-USER -i <EXT_IF> -j DROP
COMMIT
# END DOCKER-USERKrok 4: Načtěte pravidla a zkontrolujte je
sudo ufw reload
sudo ufw status verbose # must say "Status: active", no errors
sudo iptables -S DOCKER-USER
sudo iptables -S FORWARD | head -5 # the first rules jump to DOCKER-USERUFW musí hlásit Status: active a výpis DOCKER-USER musí obsahovat vaše pravidla.
Krok 5: Ověřte přihlášení a zrušte pojistku
V novém okně se přihlaste přes SSH. Když to funguje, pojistku zrušte, jinak za 10 minut pravidla smaže.
sudo systemctl stop rollback-docker-user.timer
systemctl list-timers rollback-docker-user.timer # → 0 timers listedPravidla platí pro IPv4. Sítě kontejnerů mají IPv6 ve výchozím stavu vypnuté; pokud ho zapnete, přidejte stejný blok i do /etc/ufw/after6.rules. Nepřepínejte Docker na experimentální nftables – řetězec DOCKER-USER pak neexistuje.
08 · Kontejnery
Bezpečné spouštění kontejnerů
Kontejnery popisujte v souboru compose.yaml, ne dlouhými příkazy. Nastavení je pak přehledné, dá se zkontrolovat a uložit do Gitu. Každý projekt má vlastní složku, do které smí zapisovat jen root: kdo může soubor změnit, ovládá kontejner.
sudo install -d -m 0750 -o root -g root /opt/stacks/<PROJECT>
cd /opt/stacks/<PROJECT>Příklad: zabezpečený kontejner
Webový server (nginx) s omezeními, která se hodí pro většinu kontejnerů. Komentáře vysvětlují jednotlivé řádky:
services:
web:
# fixed version + digest: the image never changes without you knowing
image: nginxinc/nginx-unprivileged:1.29-alpine@sha256:<DIGEST>
user: "101:101" # do not run as root inside the container
read_only: true # root filesystem is read-only
tmpfs:
- /tmp # writable only where the app needs it
cap_drop:
- ALL # no Linux capabilities
security_opt:
- no-new-privileges:true # also in daemon.json; keep it visible here
ports:
- "127.0.0.1:8080:8080" # server only / SSH tunnel
mem_limit: 256m
cpus: 0.50
pids_limit: 200
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8080/"]
interval: 30s
timeout: 5s
retries: 3
networks:
- frontend
networks:
frontend: {}Digest pro řádek image: zjistíte prvním příkazem. Druhý blok nastavení zkontroluje a kontejner spustí:
# the "Digest:" line is the value for @sha256:…
sudo docker buildx imagetools inspect nginxinc/nginx-unprivileged:1.29-alpinesudo docker compose config # prints the resolved file and reports errors
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail=50Pravidla pro každý kontejner
| Pravidlo | Proč |
|---|---|
| Image jen od důvěryhodného vydavatele (Docker Official Image, Verified Publisher, výrobce aplikace) | Image je program, který spouštíte. Neznámá může obsahovat malware. |
Pevná verze s digestem, nikdy latest | Image se nezmění bez vašeho vědomí (kapitola 10). |
Bez práv správce (user:) | Když útočník kontejner prolomí, nezíská práva správce. |
cap_drop: [ALL] | Kontejner nemá žádná dílčí práva roota. Většina aplikací žádná nepotřebuje. |
read_only: true | Útočník nemůže v kontejneru nic uložit ani přepsat. Zápis jen tam, kde ho aplikace potřebuje. |
Limity mem_limit, cpus, pids_limit | Jedna aplikace nevyčerpá celý server. |
Oddělené sítě, databáze v síti internal: true | Kontejner vidí jen služby, které potřebuje. Interní síť nemá přístup do internetu. |
Hesla v souborech (secrets:) | Hesla v proměnných prostředí jdou snadno vypsat a končí v logech. |
privileged: true, network_mode: host, pid: host, cap_add: [SYS_ADMIN], apparmor:unconfined, seccomp:unconfined a připojení složek /, /etc, /root, /home nebo /var/run/docker.sock. Pokud je návod aplikace vyžaduje, zvažte jiné řešení (balíček z APT, virtuální stroj).
Hesla
Compose předá heslo kontejneru jako soubor /run/secrets/<jméno>. Většina oficiálních image ho umí načíst přes proměnnou s koncovkou _FILE:
# password file readable by root only, never typed into the shell history
sudo install -d -m 0700 /opt/stacks/<PROJECT>/secrets
openssl rand -base64 32 | sudo tee /opt/stacks/<PROJECT>/secrets/db_password >/dev/null
sudo chmod 0600 /opt/stacks/<PROJECT>/secrets/db_passwordservices:
db:
image: postgres:18-alpine@sha256:<DIGEST>
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- db-data:/var/lib/postgresql
networks:
- backend
networks:
backend:
internal: true # no internet access, no published ports
volumes:
db-data: {}
secrets:
db_password:
file: ./secrets/db_passwordSoubor s heslem musí umět přečíst uživatel, pod kterým běží aplikace v kontejneru. Když ho nepřečte, změňte vlastníka souboru na tohoto uživatele; práva pro všechny nenastavujte.
09 · Rootless
Varianta: Docker bez práv správce
V rootless režimu běží Docker i kontejnery pod obyčejným uživatelem bez práv správce. Kdo prolomí kontejner, nezíská server, jen tohoto uživatele. Použijeme samostatného uživatele bez sudo, aby útočník nezískal ani vaše SSH klíče.
Krok 1: Připravte balíčky a uživatele
Potřebuje repozitář z kapitoly 04. Příkazy vypnou běžný Docker a vytvoří uživatele pro kontejnery. Spusťte je jako <ADMIN_USER>:
sudo apt install uidmap dbus-user-session docker-ce-rootless-extras systemd-container
# stop the system-wide (rootful) daemon - rootless does not need it
sudo systemctl disable --now docker.service docker.socket
sudo rm -f /var/run/docker.sock
# a dedicated user for containers: no password, no sudo
sudo adduser --disabled-password --gecos "" <DOCKER_USER>
grep '^<DOCKER_USER>:' /etc/subuid /etc/subgid # at least 65536 IDs eachUbuntu 24.04 a novější vyžaduje pro rootless režim výjimku v AppArmoru. Balíček docker-ce-rootless-extras ji nainstaluje sám.
Krok 2: Zapněte start se serverem
Docker uživatele se spustí se serverem, i když není nikdo přihlášený.
# start the user's daemon at boot, even when nobody is logged in
sudo loginctl enable-linger <DOCKER_USER>Krok 3: Nainstalujte Docker pod uživatelem
Do účtu uživatele se přepněte přes machinectl shell. Přes sudo -iu nebo su by nefungovala správa služeb.
# a full systemd session for the user (sudo -iu / su - do not create one)
sudo machinectl shell <DOCKER_USER>@
# --- now as <DOCKER_USER> ---
dockerd-rootless-setuptool.sh install
systemctl --user enable docker
docker context use rootless
docker info | grep -Ei 'context|rootless|cgroup'Výpis musí obsahovat Context: rootless a Cgroup Version: 2. Nastavení tohoto Dockeru je v ~/.config/docker/daemon.json; převezměte do něj z kapitoly 06 logy, no-new-privileges a default-address-pools.
Krok 4: Povolte limity výkonu
Bez tohoto kroku systém limity procesoru a disku u rootless kontejnerů tiše ignoruje. Spusťte jako <ADMIN_USER>:
sudo install -d -m 0755 /etc/systemd/system/user@.service.d
sudo tee /etc/systemd/system/user@.service.d/delegate.conf >/dev/null <<'EOF'
[Service]
Delegate=cpu cpuset io memory pids
EOF
sudo systemctl daemon-reload
# takes effect once the user's systemd instance restarts (simplest: reboot)Porty a firewall
V rootless režimu platí firewall UFW i pro kontejnery a řetězec DOCKER-USER není potřeba. Port, který má být dostupný zvenku, proto povolte v UFW (sudo ufw allow 443/tcp). Porty pod 1024 (např. 80 a 443) obyčejný uživatel otevřít nesmí; povolte to jen programu rootlesskit:
# allow ports below 1024 only for rootlesskit, not for every user
sudo setcap cap_net_bind_service=ep "$(command -v rootlesskit)"
# as <DOCKER_USER>:
systemctl --user restart dockerPo každé aktualizaci balíčku docker-ce-rootless-extras příkaz zopakujte, aktualizace oprávnění smaže.
Další omezení
- Kontejner ve výchozím stavu nevidí skutečnou IP návštěvníka (vadí např. u logů webu). Řešení popisuje dokumentace.
- Volba
privilegednedává plná práva apingv kontejneru nemusí fungovat. - Data jsou v
~/.local/share/dockeruživatele, ne v/var/lib/docker. Myslete na to u záloh.
10 · Aktualizace
Aktualizace Dockeru a aplikací
Aktualizovat je potřeba dvě věci: samotný Docker a images aplikací. Automatické aktualizace Ubuntu řeší jen první.
Docker – varianta A: automaticky
Přidá Docker do automatických aktualizací. Kontejnery díky live-restore aktualizaci přežijí bez restartu.
sudo tee /etc/apt/apt.conf.d/52unattended-upgrades-docker >/dev/null <<'EOF'
Unattended-Upgrade::Origins-Pattern {
"site=download.docker.com";
};
EOF
# dry run: Docker packages must show up as allowed
sudo unattended-upgrade --dry-run --debug 2>&1 | grep -iE 'docker|containerd'Docker – varianta B: ručně
Pro server, kde restarty plánujete sami. Sledujte přehled vydání a bezpečnostní oznámení. Aktualizujte pravidelně (např. jednou měsíčně) a hned po bezpečnostní opravě.
sudo apt update
apt list --upgradable 2>/dev/null | grep -E 'docker|containerd'
sudo apt install --only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginImages aplikací
- Sledujte nové verze aplikace a bezpečnostní oznámení výrobce.
- V
compose.yamlpřepište verzi a digest. - Stáhněte novou image, nasaďte ji, zkontrolujte stav a smažte staré images:
cd /opt/stacks/<PROJECT>
# after changing tag + digest in compose.yaml:
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker image pruneNástroje, které kontejnery aktualizují automaticky, potřebují přístup k docker.sock a nasadí cokoli nového bez kontroly. Na server nepatří. Známé zranitelnosti v images odhalí skener (Trivy, Docker Scout).
11 · Ověření
Kontrola nastavení
Kontrola po instalaci a po každé větší změně. Komentáře u příkazů říkají, co má vyjít.
# 1) nobody in the docker group, no API on the network
getent group docker
sudo ss -tlnp | grep -E ':2375|:2376' # → nothing
# 2) daemon security options
sudo docker info | grep -A5 'Security Options'
sudo docker info --format '{{.LoggingDriver}} live-restore={{.LiveRestoreEnabled}}'
# 3) where ports are published (0.0.0.0 / [::] only for ports meant to be public)
sudo docker ps --format 'table {{.Names}}\t{{.Ports}}'
sudo ss -tlnp | grep -E 'docker-proxy|rootlesskit'
# 4) firewall safety net
sudo iptables -S DOCKER-USER
# 5) settings of every running container
sudo docker inspect --format '{{.Name}} user={{.Config.User}} ro={{.HostConfig.ReadonlyRootfs}} priv={{.HostConfig.Privileged}} caps={{.HostConfig.CapDrop}} sec={{.HostConfig.SecurityOpt}}' $(sudo docker ps -q)Test zvenku
Z počítače mimo vaši síť (jiný server, telefon na mobilních datech) zjistěte, které porty jsou otevřené. Otevřené (open) smí být jen ty, které jste povolili, typicky 80 a 443 u webu:
# from a machine OUTSIDE your network
nmap -Pn -p 80,443,2375,2376,5432,6379,8080,<SSH_PORT> <PUBLIC_IP>Firewall pro kontejnery vyzkoušíte kontejnerem, který schválně otevře port do internetu. Zvenku musí být port filtered (zablokovaný):
# on the server: publish a test port on all addresses on purpose
sudo docker run --rm -d --name test-leak -p 0.0.0.0:8081:8080 nginxinc/nginx-unprivileged:alpine
# from outside: port 8081 must be "filtered", not "open"
nmap -Pn -p 8081 <PUBLIC_IP>
# on the server: clean up
sudo docker stop test-leakPodrobný kontrolní seznam nabízí CIS Docker Benchmark. Na malém serveru není nutné splnit vše.
12 · Problémy
Když něco nefunguje
Nejčastější potíže a jejich řešení.
| Co vidíte | Příčina | Co udělat |
|---|---|---|
permission denied … docker.sock | nejste ve skupině docker (záměr) | použijte sudo docker |
| Docker po úpravě daemon.json nenastartuje | chyba v souboru, nebo stejné nastavení i v systemd | sudo dockerd --validate --config-file=/etc/docker/daemon.json |
| Služba není zvenku dostupná | port je jen na 127.0.0.1, nebo ho blokuje DOCKER-USER | veřejný port povolte vědomě (porty, firewall) |
| Kontejner nemá internet | síť má internal: true, nebo provoz blokuje VPN | zkontrolujte síť v compose.yaml a sudo iptables -S DOCKER-USER |
| Po spuštění kontejneru nefunguje část sítě | adresy kontejnerů kolidují s vaší sítí nebo VPN | default-address-pools (kapitola 06), pak sítě vytvořte znovu |
| Disk se plní | staré images, mezipaměť sestavování, logy | docker system df, image prune, builder prune |
Image nefunguje s read_only nebo cap_drop | aplikace potřebuje někam zapisovat nebo jedno dílčí právo | z logů zjistěte co a povolte jen to (tmpfs, cap_add) |
Rootless: Failed to connect to bus | přepnutí přes sudo -iu nebo su | sudo machinectl shell <DOCKER_USER>@ |
| Rootless: po restartu serveru nic neběží | chybí start se serverem | kapitola 09, krok 2 |
| Po přechodu na novou verzi Ubuntu nejde Docker aktualizovat | přechod cizí repozitáře vypíná | Suites: v docker.sources na nový codename |
sudo dockerd --validate --config-file=/etc/docker/daemon.json
journalctl -u docker -b --no-pager | tail -50sudo docker system df
sudo docker image prune # unused images
sudo docker builder prune # build cache- Docker jen z oficiálního repozitáře s ověřeným klíčem, nikdy curl | sh.
- Skupina docker = správce bez hesla. Na serveru sudo docker, nebo rootless.
- Porty kontejnerů obchází firewall: otevírat je na 127.0.0.1 a pojistit pravidly v DOCKER-USER.
- Každý kontejner bez práv správce, jen pro čtení, s limity a pevnou verzí image.
13 · Odinstalace
Odinstalace
Smazáním /var/lib/docker přijdete o všechny images, kontejnery a volumes včetně databází. Nejdřív zálohujte.
sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm -rf /var/lib/docker /var/lib/containerd
sudo rm -f /etc/apt/sources.list.d/docker.sources /etc/apt/keyrings/docker.asc
sudo rm -f /etc/apt/apt.conf.d/52unattended-upgrades-docker
sudo rm -rf /etc/docker
sudo apt updateZ /etc/ufw/after.rules smažte blok BEGIN DOCKER-USER … END DOCKER-USER a spusťte sudo ufw reload. Rootless režim odinstalujte ještě před apt purge:
# as <DOCKER_USER>, before apt purge:
dockerd-rootless-setuptool.sh uninstall
rootlesskit rm -rf ~/.local/share/docker
# as <ADMIN_USER>:
sudo loginctl disable-linger <DOCKER_USER>Zdroje
Oficiální dokumentace a další čtení
Návod vychází z oficiální dokumentace Dockeru (ověřeno k 27. 10. 2026):
- Install Docker Engine on UbuntuPodporované verze, repozitář, instalační skript a firewall.
- Linux post-installation stepsSkupina docker a její práva, rotace logů.
- Docker Engine securityRizika démona, dílčí práva, user namespaces.
- Protect the Docker daemon socketVzdálený přístup jen přes SSH nebo TLS.
- Packet filtering and firewallsDocker a UFW.
- Docker with iptablesŘetězec DOCKER-USER.
- Port publishing and mappingPublikování portů a výchozí adresa.
- dockerd referenceVšechna nastavení daemon.json.
- Rootless modeInstalace rootless režimu.
- Rootless – tips and troubleshootingAppArmor, nízké porty, limity, odinstalace.
- Compose file referenceVšechny volby compose.yaml.
- OWASP Docker Security Cheat SheetPřehled bezpečnostních doporučení.
Související
- Průvodce Ubuntu ServeremInstalace, SSH, firewall a základní zabezpečení.
- Tailscale na Ubuntu ServeruSSH jen přes soukromou síť.
- SSH klíčePřihlášení klíčem místo hesla.
- Unattended upgrades (anglicky)Automatické bezpečnostní aktualizace.
Jak citovat tuto publikaci (ČSN ISO 690:2022)NEPOR, Vladimír. Docker na Ubuntu Serveru: bezpečná instalace a nastavení. Online. Vrealmatic, 2026. Aktualizováno 27. 10. 2026. Dostupné z: https://vrealmatic.com/ubuntu-server/docker-instalace. [citováno ].
Potřebujete bezpečně nastavit server?
Navrhneme, zabezpečíme a zdokumentujeme sítě i servery.