Dockerfile

Dockerfile(5) is a declarative langauge for building a container image. Several container toolchains are capable of compiling Dockerfile files, including docker(1) and podman(1).


Example

# Setup
FROM alpine:latest
RUN apk update
RUN apk add postgres14

# Data persistence
VOLUME /var/lib/postgresql/data

# Configuration
COPY local/path/to/postgresql.conf /etc/postgresql/postgresql.conf

# The containerized process
USER postgres
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]


Syntax

A Dockerfile consists of lines like INSTRUCTION arguments. Instructions are case-insensitive but, by convention, should be capitalized.

Lines beginning with # are comments. A # anywhere else is treated as an argument.

Except for parser directives and global scope ARG instructions, a Dockerfile must begin with a FROM instruction.

Long lines can be split using \, as in:

RUN echo hello \
world

Note that comments are stripped before lines are parsed, so the above is equivalent to:

RUN echo hello \
# comment
world

Whitespace is preserved for arguments, but ignored for instructions and comments. By convention, there should never be whitespace ahead of an instruction or comment.


Instructions

From


Label

The LABEL command is used for image metadata.

LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."


Arg and Env


Run and Shell

Shell

The SHELL command overrides how RUN commands are handled. The default shell is ["/bin/sh", "-c"] on Linux and ["cmd", "/S", "/C"] on Windows.


Add


Copy

To copy a single file, try:

COPY index.html /usr/share/nginx/html/index.html
# or
COPY index.html /usr/share/nginx/html/

If and only if the destination ends in /, it is interpreted as a directory.

To copy multiple files, try:

COPY README.md package.json __BUILD_NUMBER /destination/path/
# or
COPY ["__init__.py", "__main__.py", "app.py", "/destination/path/"]
# or
COPY *.conf /destination/path/

The destination must be a directory when copying multiple files.

Note that COPY handles source directories differently. COPY dir1 dir2 /destination/path/ is actually COPY dir1/* dir2/* /destination/path/

Ownership

Files and directories are copied with uid and gid of 0. To set ownership, try:

COPY --chown=nginx:nginx index.html /usr/share/nginx/html/index.html

If a user name or group name is used, the container will depend on having /etc/passwd and /etc/group files. If this isn't feasible, consider using numeric uids and gids.

Copy from

COPY has a --from=CONTAINER option.

In a multi-stage build, files and binaries are copied from a build container into the production container. This keeps the statically-compiled dependencies outside of the production image.

FROM golang:latest AS builder
WORKDIR /app
COPY * ./
RUN go mod download
RUN go build -o /my-binary-name

FROM alpine:latest
WORKDIR /
COPY --from=builder /my-binary-name /my-binary-name
EXPOSE 8080
ENTRYPOINT ["/my-binary-name"]

Build Context

COPY depends on the build context (i.e. the working directory and all contained files). Files from outside this context cannot be accessed by COPY.

When building a piped Dockerfile (i.e. docker build - <Dockerfile.example), there is no build context and COPY cannot be used.


Volume


User


Workdir


Cmd

The CMD command is used to define the default process for an image. There can only be one CMD command in a Dockerfile.

Some best practices follow.

Run without a shell

If the command is a string, it is processed by a shell just like a RUN command.

If the command is a JSON array, it is executed directly. This is always preferable.

Run in foreground

Container images are designed to run singular services, and accordingly expect services to behave like userspace utilities. These expectations include...

  1. observe and respond to signals
  2. exit upon completion with a meaningful exit code
  3. log to stdout
  4. print errors to stderr

Most services can be run in the foreground, as init systems prefer to handle forking internally. Some can even be configured to use stdout and sterr correctly.

For services that cannot be made to fit this model, consider using a supervisor.

Use a supervisor

Supervisord is a minimal init system for use in containers. Consider the below Dockerfile:

FROM alpine:latest
RUN apk add supervisord
USER root
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

The container's main process will be supervisord(1), and the services will instead be managed by that process.

dumb-init is an even thinner init system, essentially just handling signals.

FROM alpine:latest
RUN apk add dumb-init
USER root
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/myapp/executable", "--with", "--args"]


Entrypoint

The ENTRYPOINT command is very similar to CMD, and in many cases they can be used interchangeably.

Entrypoint and Cmd

If ENTRYPOINT and CMD are used in the same Dockerfile, then ENTRYPOINT acts as the default program while CMD acts as the default argument.

FROM alpine:latest
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]


Expose


Healthcheck


See also

Dockerfile(5)

Dockerfile reference documentation


CategoryRicottone

Docker/Dockerfile (last edited 2023-04-04 16:40:37 by DominicRicottone)