Understand Docker's Isolation Boundary¶
Docker packages and runs an application with its dependencies. On a Linux Raspberry Pi, that does not give the application a separate guest kernel or automatically decide which host data it should access.
Outcome: review a proposed container's access and distinguish resource limits from data isolation. Complete the filesystem and cgroup lessons first. Docker is not required for the review. An optional, Pi-tested lab below runs a trusted official image with no host-data mount or published port; it requires an installed, authorized local engine.
Connect Docker to the mechanisms you just tested¶
| Mechanism | Your systemd exercise | What to review in Docker |
|---|---|---|
| Mount namespace and filesystem policy | Private temporary paths and read-only settings | Image filesystem, read-only mode, writable volumes and host bind mounts |
| Network namespace | A socket filter is a different control, not a network namespace | Network mode, shared namespaces and published ports |
| cgroup controllers | TasksMax, MemoryMax, CPUQuota |
PID, memory, swap and CPU constraints |
| Process identity and privileges | Ordinary user and NoNewPrivileges |
Container user, UID mapping, capabilities and security options |
| Kernel boundary | The same kernel runs the practice service | A normal Linux container also shares the host kernel |
The Docker security overview describes namespaces, cgroups and the daemon's attack surface. Docker is not merely a memory limit, and cgroups alone are not containers. Namespaces, identity and resource controls solve different parts of the problem; configuration determines which boundaries remain.
--network none illustrates that distinction: Docker creates only the container's loopback interface, rather than applying the previous lesson's socket-family filter. Loopback remains usable; this is not equivalent to denying every socket operation. See the none network driver.
Review a Raspberry Pi logger before containerizing it¶
Use the same hypothetical logger, not an unknown image or someone else's install script. Write down its needs:
| Proposed access | Ask first | Narrower starting point to investigate |
|---|---|---|
| Bind-mount your whole home | Does the logger need browser data or SSH keys? | A dedicated input/settings directory; no unrelated files |
| Writable settings mount | Must the application edit its own configuration? | Read-only settings, separate writable readings |
| GPIO/I2C device access | Which exact device and permissions are required? | Only the reviewed device and needed group access, not all of /dev |
| Host networking | Does it need the host's entire network view? | A separate network; deliberately choose any published address/port |
| Docker socket mount | Must the logger create/manage containers? | No daemon socket or daemon credentials |
| Unlimited workers/memory | What is its normal workload and failure response? | Measured limits, plus a plan for failed requests |
For a software-only log processor, hardware devices might not be needed at all. Do not add --privileged, host PID sharing or the host root filesystem just to suppress an error. A GPIO workload may justify device access, but it expands the review; the previous no-device labs cannot validate it.
Read-only is not secret. A read-only bind mount still lets the application read its contents. Conversely, a writable host bind mount can expose the original host data, not an expendable copy inside the image. The Docker bind-mount guide explains that connection. Mounting a volume also means removing a container is not the same as restoring or backing up its data.
Distinguish three meanings of root¶
- Container process user:
--userselects the application's UID/GID inside the container. A non-root app is useful, but it does not turn a rootful Docker daemon into a rootless one. - Host identity mapping: a user namespace can map container IDs to different host IDs. File ownership on bind mounts still needs deliberate review.
- Daemon identity and authority: who runs the engine, and who can ask it to create containers with powerful host access?
Access to a rootful daemon is security-sensitive. Adding an account to the docker group is not merely a convenient way to run a harmless command; controlling that daemon can confer host-level authority. See Docker's explicit group privilege warning. Do not grant it as a prerequisite for this course, and do not expose its API publicly.
Rootless Docker runs the daemon and containers without host root, subject to prerequisites and limitations. It is different from an image containing USER 1000. It also does not prevent a container from reading data you intentionally make available to it. Consider the daemon, workload identity and shared data together.
Optional read-only inspection of an existing container¶
Skip this if Docker is absent. Do not install it or change group membership for the exercise. On the Docker host, first verify the configured endpoint:
Read the endpoint and any DOCKER_HOST/DOCKER_CONTEXT overrides you have set. Stop if it targets a remote, production or unfamiliar engine; a local CLI does not guarantee a local daemon. Only after confirming your authorized practice endpoint, inspect it:
docker info may report missing controller support or rootless mode. A command permission error is a reason to stop, not to add sudo blindly or relax socket permissions.
For one existing practice container, replace container-name below with the exact name from your list:
Look for Privileged, NetworkMode, PidMode, ReadonlyRootfs, CapAdd, SecurityOpt, Devices, Memory, MemorySwap, NanoCpus/CpuQuota, and PidsLimit. Compare actual mounts and published addresses with the application's needs. Interpret defaults and zero/unset values using the container-run reference, not as a binary secure/insecure score. Inspecting a configured limit is not an enforcement test.
These outputs can include private paths, environment/security settings or endpoint details. Review them locally; do not paste them unredacted into a public issue. Do not dump .Config.Env looking for secrets as part of this exercise.
Optional Docker lab on Pi 4B¶
Confirm the local engine and image¶
For the verification run on 31 October 2026, Debian's docker.io and docker-cli packages had been installed on the Pi. Client/server version was 26.1.5+dfsg1, linux/arm64, cgroup v2 and the systemd cgroup driver. No reboot was performed during verification. This is a Debian-packaged engine, not Docker CE from Docker's own repository. Installation is an administrative change and starts host services; it is not part of the preceding unprivileged systemd labs.
The following commands run on the Pi, against its local rootful engine, with explicitly authorized sudo. Do not add yourself to the Docker group or change socket permissions to follow along. A rootless installation uses a different endpoint and should not blindly copy this sudo workflow.
Confirm unix:///var/run/docker.sock and the intended local engine. Stop if DOCKER_HOST, DOCKER_CONTEXT or another configuration selects a different endpoint. This Pi reports No memory limit support and No swap limit support. The lab does not set --memory, allocate large buffers or claim memory protection. A Docker flag cannot supply a missing kernel controller; see the observed cgroup limitation.
The lab uses the Docker Official Python image. Downloading it consumes network/disk space and running it executes its code. Review that source before choosing to proceed. The tested python:3.13-slim image resolved to the digest below; the command pins that content rather than silently following future tag changes:
If the image cannot be downloaded or does not support your architecture, stop rather than substitute an unknown image. A pinned digest records reproducibility, not a promise of future security updates; re-review and retest an updated image when maintaining a real deployment.
Observe the selected boundaries¶
Copy this whole block. It creates only an expendable container and a 1 MiB writable tmpfs inside it, with no bind mounts, hardware devices, daemon socket or published ports. --pull=never requires the reviewed image already to be present. Python sets an eight-second alarm; waiting workers have timeouts and the attempt count is fixed. It is not a stress test.
Expect a non-root UID, no effective capabilities, NoNewPrivs=1, an EROFS write denial, successful tmpfs output, loopback-only interfaces, matching cgroup controls and a recorded task denial, followed by PASS and exit status zero. The Pi allowed 15 waiting workers alongside Python's main thread. An unexpected error is not a passing restriction test.
The kernel release printed inside the container matched the Pi host's 6.18.50+rpt-rpi-v8: this is a shared-kernel Linux container, not an emulated guest OS. CPU quota was read back, not throughput-tested. The interface check is not an exhaustive network escape test, and the program deliberately does not contact an Internet host.
Check completion and retained data¶
--rm removes this container when it exits, including its temporary result. It does not remove the downloaded image, which stays cached for another run. No host files or named volumes were created by the exercise. Inspect rather than pruning other people's data:
If a container remains after an interrupted run, identify only this exercise's exact container ID before stopping/removing it. Never use a blanket prune or stop every container. Installing Docker also leaves its daemon/containerd services in place; container cleanup does not uninstall them. See the resource constraints documentation before relying on limits in another environment.
Make a boundary decision¶
For a trusted sensor application, a reviewed systemd service may be simpler than adding a container engine. Docker can be useful for dependency packaging and repeatable deployment, but it introduces image provenance, updates, daemon access and persistent-volume responsibilities. A VM adds a guest kernel boundary, with its own configuration and device-sharing considerations. None is a universal winner.
Your review should end with four explicit statements: what is shared, what is restricted, what evidence you have, and how data is recovered. If code is hostile or highly sensitive, these beginner labs are not sufficient assurance; seek a stronger, separately reviewed environment.
Checkpoint: would --memory=64m stop a container reading an SSH key bind-mounted into it? Would --user=1000 make a rootful daemon rootless? What host data survives container removal?
Next: restore and verify a backup. Return to Service Isolation.
Verification scope: reviewed and container-tested on 31 October 2026, on the Pi 4B and exact environment in verification scope. The bounded exercise checks the listed file, privilege, interface and task-limit behaviors only. No memory-limit enforcement, hardware passthrough, rootless setup, Internet reachability or container escape test is claimed.