Homelab
Proof

U1 / 8 guides

Docker foundations

Installing Docker, keeping the firewall honest, stopping logs from eating the disk and updating containers without losing data. These are the failure modes that hit a server months after it was set up, reproduced on purpose.

Line drawing: a shipping container resting on a solid concrete foundation slab with a layer diagram of the slab visible at the edge.
  1. 2026-11-0129.9.2How much RAM does a homelab need? 35 stacks measured4 GB ran 12 stacks at once on this test box, with 378 MiB to spare and every stack answering; 8 GB ran the same set with 4,248 MiB available. 2 GB handled 4 light stacks easily and 8 stacks with only 173 MiB left, and 12 stacks in 2 GB failed in both attempts: once the machine stopped answering over SSH, once only 9 of the 12 stacks started and the kernel killed processes. These are idle figures with no users or content, so treat them as the floor.
  2. 2026-11-0129.9.2Install Docker on Ubuntu 24.04: 18 s, 388 MBInstall from Docker's own apt repo. On a fresh Ubuntu 24.04 VM the repo setup plus package install took a median 17.8 s, added 387.9 MB of installed packages, and gave Engine 29.9.2 with Compose 5.6.1. Ubuntu's docker.io took 11.4 s (14.0 s against 11.4 s for the package install alone, since docker.io needs no repo setup) but was Engine 29.1.3, and the two idle at about the same memory.
  3. 2026-11-0129.9.2Docker ignores UFW: fixes tested at runtimeYes, a Docker-published port bypasses UFW. On the test box, curl from outside got HTTP 200 3 of 3 times from a container on 0.0.0.0:8080 with UFW on default deny, and no answer from a normal process on the same port. Publishing on 127.0.0.1, or a DOCKER-USER rule that matches the original port, closed it.
  4. 2026-11-0129.9.2Docker logs grow forever by default: measured capsWith Docker's defaults a noisy container's json-file log grew at a median 99.5 KiB/s with no limit; one set to max-size=1m and max-file=3 never exceeded three files of about 1 MB (2.43 MiB at most in 12 samples over 120 s; the settings allow 2.86 MiB). A limit in daemon.json applied only to containers created after it, so existing containers kept growing until they were recreated.
  5. 2026-11-0129.9.2Docker restart policies after a reboot, testedUse unless-stopped for services that should survive reboots: it returned in 3 of 3 reboots and stayed down after a manual docker stop. always also returned after a manual stop, no never returned, and on-failure came back only when the process had been killed (exit 137), not when it had exited 0.
  6. 2026-11-0129.9.2Volume vs bind mount: no clear speed gap, ownership differsOn this VM, three fio runs each gave overlapping results for a named volume, a bind mount and the container's own layer, so the test found no speed reason to choose; choose on ownership and backups. The practical difference was permissions: a non-root image failed on a bind mount Docker created (owner 0:0) and on one made by the host user (owner 1000:1000), and ran on a fresh named volume.
  7. 2026-11-012.5.8 to 2.5.9Updating Docker containers safely without WatchtowerOur recommendation, which the test did not compare against unpinned tags, is to pin image tags, back up the data directory before every bump, and treat a rollback as a restore. Watchtower's repository is archived (last push 2025-12-17), a Postgres 16 data directory refused to start under 17 (exit 1), and Diun v4.33.1 reported a changed image at its next one-minute check (50 s after the push) without touching the running container.
  8. 2026-11-022.45.3Portainer vs Dockge vs Komodo on one compose stackAll three deployed the same compose, showed its logs and replaced a running image tag, so the choice is about what each costs and needs. Portainer CE was the smallest (27.6 MiB, one container), Dockge used 128.4 MiB and Komodo 242.9 MiB across three containers including MongoDB. Every one of them holds access to the Docker socket, which Docker's docs treat as root-level control of the host.