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).
Contents
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...
- observe and respond to signals
- exit upon completion with a meaningful exit code
- log to stdout
- 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 reference documentation