September 26, 2017·5 min read·engineeringdockerdockerfilepermissionsbuilds

Dockerfile copies can set file ownership directly

Docker 17.09 adds ownership selection to COPY and ADD, resolving user and group names within the destination image.

File ownership is part of the runtime environment, even when a build description treats it as a correction performed after copying files. Docker 17.09 lets the copy operation declare the intended owner directly. The change is useful because ownership then belongs to the instruction introducing the files, where the relationship between their contents and permissions is easiest to review.

The 17.09.0-ce release notes date the release to 26 September and introduce --chown for Dockerfile ADD and COPY. The implementation supports numeric identifiers as well as names. Understanding where it resolves those names is more important than the brevity of the new option.

I value a Dockerfile that describes the environment without relying on an operator to reconstruct hidden assumptions. An ownership declaration makes one of those assumptions explicit. It also avoids making a later command the only explanation of why an application's files belong to a particular identity.

The destination image supplies the identity database

The implementation patch carries work by Kara Alexandra and Phil Estes. Its completed implementation parses a user and optional group, then resolves names through files under the container root filesystem. The relevant paths are /etc/passwd and /etc/group within that filesystem.

This means the developer's host account database does not determine the meaning of a name in the instruction. The destination image must contain the corresponding identity information. A name that exists on the workstation but not in that image does not provide a valid lookup result.

The lookup functions first try to parse an integer. Numeric identifiers therefore bypass name lookup in the account files. That is useful when the intended runtime identity is already specified numerically, or when an image intentionally lacks account names. It also means a numeric value communicates a different dependency from a named value.

For a Linux build, a direct numeric copy can look like this:

FROM busybox:1.27
COPY --chown=1001:1002 application.conf /etc/application.conf

This assumes application.conf is present in the build context. The instruction names the desired user and group identifiers at the point of copying. It does not create a login account or choose the identity under which a future container process runs. File metadata and process identity remain separate parts of the environment.

Names and numbers can be combined

The patch supports combinations such as a user name with a numeric group. Its integration test creates account-file entries and then copies files with --chown=test1:1002. It checks both the rendered names and the numeric identifiers, which establishes that the requested group is not merely the user's displayed primary group.

If the instruction supplies only one component, the parser uses that text for both user and group lookup. With a numeric value, that produces the same numeric user and group. With a name, the code resolves the user and group independently from their respective files. The same spelling can therefore resolve to different numbers.

That is a meaningful detail for image authors. A short form containing only a name is not a request to infer whatever primary group the user record happens to contain. The implementation's parsing rule identifies the two lookup inputs. An explicit user:group expression makes the intended relationship easier to read when the account databases have differing conventions.

Errors are equally specific. More than two colon-separated parts produces an invalid-format error. A missing user or group name produces a lookup error identifying the unresolved component. These failures occur while the builder determines the ownership pair, before a successful copy can establish the requested metadata.

User namespaces require another translation

The parser does not finish after obtaining the container-visible numbers. It passes the pair through the builder's identity mappings to obtain the corresponding host identifiers. That is necessary when the daemon represents container identities through a user namespace mapping.

The patch includes tests for both mapped and unmapped identities. In its mapped example, a range beginning at container identifier zero corresponds to a host range beginning at 100000. The test for container identifiers 1 and 33 expects host identifiers 100001 and 100033. Those numbers are an example of the configured mapping, not fixed identifiers that Docker assigns to every build.

This distinction prevents a common misunderstanding of ownership declarations. A Dockerfile expresses the identity inside the container environment. The storage implementation may need different numeric ownership on the host to represent that identity correctly. The new flag participates in that translation instead of treating the provided numbers as unconditional host ownership.

The copy implementation also uses the ownership pair when creating new parent directories. Ownership is therefore involved in constructing the destination path, as well as assigning metadata to the copied file. Existing-directory handling has its own permission logic, which is why recursive copying requires more than applying one final change to an arbitrary destination tree.

Ownership becomes part of cache identity

The patch includes the requested ownership string in the copy instruction's recorded command used for cache probing. That detail matters because identical file bytes copied under different ownership requests should not silently reuse an image result representing the old request. The metadata choice participates in describing the build step.

The code retains the existing source hash while adding the ownership text to the instruction description. Contents and intended ownership are separate inputs to the operation. Bringing both into the cache-related representation makes the change more complete than adding a parser option that leaves the old build identity untouched.

The release also fixes unnecessary cloning while building from Git repositories. That is a separate builder change, affecting how source inputs arrive. The ownership option affects how files enter the destination filesystem. Keeping those changes distinct helps locate the improvement at the correct stage of image construction.

I prefer this direct representation because the Dockerfile can explain the final artifact through the operations that create it. A reviewer can see the intended owner alongside the source and destination. The runtime still needs an appropriate process identity and access policy, but the copied file's ownership no longer has to be inferred from a later cleanup command.

The implementation remains tied to the operating system's ownership model. The patch's Windows permission helper explicitly says that chown is unsupported there. This is a Linux-oriented file ownership control, and its useful promise is the concrete one demonstrated by the implementation and tests, selecting container-visible user and group ownership as files enter the image.