Chapter 26Lesson 01180–240 min

Containers, Reproducible Runtimes, and Distributed Test Environments: Core Concepts and Mental Model

Build the correct mental model for running Robot Framework in containers: immutable image inputs, finite container processes, explicit filesystem and network boundaries, non-root execution, and durable result artifacts.

Robot Framework 7.4.2ContainersImage vs containerFilesystem & networkResult persistence

Learning objectives

  • Explain why a container is a process/filesystem/network boundary, not an automatic test-isolation guarantee.
  • Trace source and dependency inputs through an image into a Robot process and back out to durable result artifacts.
  • Distinguish container localhost, host localhost, Compose/service DNS names, and externally published endpoints.
  • Separate Robot variable/test state from container writable-layer, mount, network, process, and CI workspace state.
  • Identify the provenance, permission, secret, and capacity evidence required before containerized Robot execution is trusted.

Current compatibility baseline — verified 2026-08-31. Robot Framework 7.4.2 is the stable course baseline and requires Python 3.8+. The mandatory container lab uses the Docker Official Image python:3.12.14-slim-bookworm and installs the Robot Framework 7.4.2 universal wheel by exact version and SHA-256 hash. Docker image tags are mutable, so the lesson records the resolved image digest and explains full digest pinning for controlled pipelines. Pabot 5.2.2 is discussed as the current stable optional parallel executor; 5.3.0b1 is prerelease and is not required. No browser library, external API/database/SSH service, CI provider, Kubernetes cluster, paid platform, real credential, or production target is required.

1. The problem: “it runs in Docker” is not a reproducibility proof

Chapter 25 placed Robot Framework inside CI/CD jobs. Containers are often the next step because they can package the Python interpreter, Robot Framework, libraries, and operating-system dependencies into one runnable image. That is useful, but it solves only one part of reproducibility. The same image can still behave differently when it sees different mounted tests, environment variables, network targets, CPU limits, credentials, or writable directories.

A containerized Robot run therefore has two contracts. The image contract says what executable files and dependencies were packaged. The runtime contract says which tests, mounts, variables, users, networks, resources, and output locations were supplied when that image became a running container. Production evidence must record both.

The practical rule for this chapter is: never use “containerized” as a synonym for “isolated,” “secure,” or “reproducible.” Prove the boundary that matters.

2. Mental model: source → image → container process → external state → durable evidence

Containerized Robot Framework execution path
flowchart TD
    A[Source + dependency manifest] --> B[Container image]
    B --> C[Container process: python -m robot or pabot]
    D[Runtime inputs: mounts, variables, user, limits] --> C
    C --> E[Tests/resources/libraries]
    C --> F[Private service / browser / Remote endpoint]
    C --> G[Container writable layer]
    C --> H[Result volume or host artifact path]
    H --> I[Host / CI artifact retention]
    F --> C

The first arrow freezes—or at least records—the source and dependencies that become the image. The runtime-input arrow is separate because a container can be started many times with different mounts, variables, users, and limits. Robot reads test data and calls libraries inside the process. External services live behind network boundaries. Anything written only to the container writable layer disappears when an ephemeral container is removed; durable evidence must cross the boundary into a volume, bind mount, or explicit copy step before deletion.

3. Define the container-specific state before mutating it

Image. A content-addressed filesystem/configuration snapshot used to create containers. A human-friendly tag such as python:3.12.14-slim-bookworm can move; a digest identifies exact content.

Container. A running or stopped instance of an image with its own process namespace and writable layer plus explicitly attached mounts and networks. The image is not the running state.

Writable layer. Ephemeral container-local filesystem changes. They are convenient scratch state, but they are a poor place for evidence that must survive docker rm or --rm.

Bind mount. A host path exposed at a container path. It couples the run to host path/permission semantics and can accidentally make source writable.

Named volume. Docker-managed persistent storage mounted into containers. It is less coupled to a host path but still needs an explicit export/retention story in CI.

Container user. The UID/GID under which Robot executes. It controls access to mounted and image-owned files. It is independent of Robot variable scope and Python library scope.

Bridge network. A container network namespace in which containers communicate through container/service addresses. Within a container, 127.0.0.1 means that container itself.

Published port. A host-side port mapped to a container port. Publishing is a deliberate exposure boundary; it is not needed for container-to-container communication on the same user-defined/Compose bridge network.

Result artifact. output.xml, log.html, report.html, screenshots or other evidence copied/mounted out of the ephemeral execution boundary.

Pabot worker. One Robot subprocess/process-group scheduled by Pabot. A Pabot worker is not the same thing as a container or CI job; nested concurrency multiplies resource demand.

4. Which layer owns which state?

State Owned by Lifetime Failure if confused
Robot variables/test status Robot execution test/suite/process Assuming a new container resets external data or durable workflow state
Python library objects Python process / library scope test/suite/global within Robot process Assuming a container restart preserves in-memory library state
Image filesystem Image build until image changes Assuming a bind mount cannot replace files baked at the same path
Container writable layer Container instance until container removal Losing output.xml/log/report after --rm
Bind mount / named volume Host or Docker volume manager independent of container Permission/ownership collisions or stale evidence
Network namespace Container runtime container/network lifetime Calling 127.0.0.1 for a service that actually runs on host/another container
CI workspace/artifact store CI runner/provider job/retention policy Container passed but results were never uploaded/preserved
Pabot worker state Pabot subprocess worker lifetime Parallel tests sharing files/ports/accounts despite separate processes

5. Inspect first: prove source, runtime, image, and result locations without changing them

# Host-side, read-only inventory
python --version
python -m robot --version
docker version
docker info

git status --short
pwd

# If the base image is already present, record what tag resolves to.
docker image inspect python:3.12.14-slim-bookworm --format '{{json .RepoDigests}}'

# Once your own image exists, these are still read-only inspections.
docker image inspect rf26-robot:7.4.2 --format '{{.Id}} {{json .Config.User}}'
docker image history --no-trunc rf26-robot:7.4.2

docker version separates client and server/runtime versions. docker info shows the engine/host capabilities that actually execute Linux containers. git status proves whether the source tree being built contains uncommitted changes. docker image inspect gives immutable IDs/digests for provenance. None of these commands changes the system under test.

6. The four “localhost” questions you must answer

Caller Target Correct mental model Typical address
Host process Host service Same host network namespace 127.0.0.1:PORT
Container Same container process/service Same container namespace 127.0.0.1:PORT
Container A Container B on same Compose/user-defined bridge Docker DNS + container port http://service-name:PORT
Docker Desktop container Service on desktop host Special host gateway DNS host.docker.internal:PORT
External machine Published container service Host/network address + published port Explicit approved host/IP + port

Do not “fix” a connection failure by publishing every port or switching to host networking. First identify where the caller and target actually live. Private bridge/service DNS is normally the safer local topology for test-only dependencies.

7. Reproducibility has levels, not a binary switch

An exact Python patch tag plus an exact Robot wheel version is stronger than python:latest plus pip install robotframework, but Docker tags remain mutable. For audited pipelines, record the resolved base-image digest and migrate the FROM reference to a full digest when you need byte-identical provenance. Likewise, a requirements file with hashes proves exactly which Python distribution was installed.

Reproducibility also requires controlling the build context. A broad COPY . . can accidentally include local result files, credentials, caches, or editor state. A narrow .dockerignore and deliberate COPY list are part of the contract.

8. Non-root is a runtime ownership decision, not a badge

Robot Framework rarely needs root privileges. Running as an explicit numeric UID/GID limits accidental writes inside the image and makes ownership visible. The hard part appears at mount boundaries: the container UID must be allowed to write the result destination. On native Linux, host UID/GID and container UID/GID interact directly; Docker Desktop virtualizes parts of this mapping. A portable course must therefore diagnose permissions instead of recommending chmod 777.

Read-only source mounts are especially valuable. If a test suite needs to mutate its own source directory to pass, the suite is mixing source with runtime state. Put mutable fixtures/results in a separate owned location.

9. A passing container is not enough if its results vanish

The container runtime has an exit code and Robot Framework creates result artifacts. CI should preserve both. If the Robot command exits non-zero, the container should normally exit non-zero. If the container exits zero but the result volume is mounted at the wrong path, the run may look green while the evidence disappears with the container. Chapter 26 therefore treats “where did output.xml go?” as a first-class operational check.

10. Why this matters in DevOps

Containerized acceptance automation becomes a reusable release gate only when operators can answer: which image digest ran, which source revision was tested, which command and user ran, what network endpoints were reachable, which inputs were mounted, how much capacity was available, and where the artifacts were retained. Those facts turn a convenient local wrapper into an auditable execution environment.

Knowledge check

Does starting a fresh container automatically give every test fresh external state?

Why can 127.0.0.1 work on the host but fail from the test container?

Why is python:3.12.14-slim-bookworm still weaker than a digest pin?

What must survive an ephemeral container even when the tests fail?

11. Summary and bridge

A container packages the Robot runtime, but reproducibility emerges only when image provenance, runtime inputs, user/permissions, networks, capacity and artifact persistence are explicit. Lesson 2 builds that contract around a small deterministic suite and proves the difference between host execution, an image, a container instance and a result volume.

Next lesson

Containers, Reproducible Runtimes, and Distributed Test Environments: Guided Hands-On Workflow

Continue with Containers, Reproducible Runtimes, and Distributed Test Environments: Guided Hands-On Workflow. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

References and version anchors

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.