fivenines
20/40

Theory Tutorial

Copy-on-Write at Runtime

Time
10m
Level
not specified
Artifacts
theory + practice
Progress0%
Lesson 20 · Runtime

Copy-on-Write at Runtime

Design claim: Runtime mutation belongs to a container-private writable layer, while image content remains immutable and reusable.

Starting model

  • You can produce either one supervised running container or no resources from a failed run attempt.
  • The useful vocabulary at this point is deliberately small: copy-up, first write, upper layer, whiteout.

A merged view hides multiple owners

An application must write files during execution without changing the image or the filesystem observed by sibling containers. Runtime behavior unfolds over time, so state and ownership must remain valid after the initiating request has returned.

The tempting shortcut is straightforward: write changed files back into the top image layer. The shortcut confuses one command or connection with the longer-lived process and resources it happens to touch. Image layers are shared by digest across containers, so mutating one would invalidate identity and leak state between instances. Asynchronous exit, retry, disconnect, or timeout exposes that mismatch immediately.

Promote on the first mutation

Opening a lower-layer file for writing triggers copy-up: the driver copies the whole file into the upperdir, then lets the write proceed on the copy. Every later read or write hits the upper copy (Lesson 9's shadowing). The costs are real and worth knowing: the first write to a large file pays a full-file copy — appending one log line to a 5 GB file in a lower layer copies 5 GB. Deletion inverts the trick: since lowers can't lose files, the driver plants a whiteout marker in the upper that masks the name; the merged view shows nothing, the lower still holds the bytes.

Copy-on-write promotes a lower-layer file into the private upper layer on first modification and directs later access to that copy. The mechanism records the durable fact separately from transient control and lets events update the model when reality changes.

Read the static view as custody and the dynamic view as evidence crossing that custody boundary.

Architecture — the write-path decision
  flowchart TB
    OP["file operation"] --> Q1{"write or delete?"}
    Q1 -- write --> Q2{"file already
in upperdir?"} Q2 -- yes --> W["write in place (cheap)"] Q2 -- "no (lower only)" --> CU["copy-up whole file,
then write the copy"] Q1 -- delete --> Q3{"file lives in
upper or lower?"} Q3 -- upper --> RM["remove from upper"] Q3 -- lower --> WO["plant whiteout marker
(lower bytes remain)"] style CU fill:#fff8ec,stroke:#f2ddb0

It shows identical lower content beneath separate container upper layers. Its central claim is that runtime writes never change digest-addressed image content or another container's upper layer; the labels therefore describe authority rather than decorative grouping.

Reads may share; writes must separate

Opening a lower-layer file for writing triggers copy-up: the driver copies the whole file into the upperdir, then lets the write proceed on the copy. Every later read or write hits the upper copy (Lesson 9's shadowing). The costs are real and worth knowing: the first write to a large file pays a full-file copy — appending one log line to a 5 GB file in a lower layer copies 5 GB. Deletion inverts the trick: since lowers can't lose files, the driver plants a whiteout marker in the upper that masks the name; the merged view shows nothing, the lower still holds the bytes.

A read resolves through the merged view, a first write triggers copy-up, the application edits the upper copy, and removal uses a private whiteout. The sequence matters because lifecycle decisions made from stale evidence become illegal or destructive operations.

The second figure tests the same model in motion: it traces lookup, copy-up, private modification, and later read precedence.

Sequence — first write vs. second write
  sequenceDiagram
    participant P as Process
    participant O as Overlay driver
    participant U as upperdir
    participant L as lower layers
    P->>O: append to /var/log/app.log (first write)
    O->>L: file found in lower (200 MB)
    O->>U: copy-up all 200 MB
    O->>U: apply the append
    O-->>P: done (slow — paid the copy)
    P->>O: append again
    O->>U: file present in upper
    O-->>P: done (fast — native write)
      

It traces lookup, copy-up, private modification, and later read precedence. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.

The image remains a reusable fact

Two containers edit the same configuration path, so each must continue to see an independent value while a new third container sees the image default. The failure case is authoritative input to the state model, not an exception to be hidden behind a successful command response.

Runtime writes never change digest-addressed image content or another container's upper layer. The reusable rule keeps process reality, operator intent, and retained resources from collapsing into one status flag.

Keep MiniDock's runtime layer private

MiniDock applies the union-filesystem design at runtime and keeps instance state on the living side of the image boundary. MiniDock must preserve the lifecycle guarantee while leaving the current integration move to the learner.

What carries forward

  • Runtime writes never change digest-addressed image content or another container's upper layer.
  • Trace runtime writes and deletes without mutating immutable image content.
  • The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
Next step

See what actually stuck.

Take the practice scenarios now.