logo

Cheatsheet - Dockerfile

A Dockerfile is a text file that contains all the commands a user could call on the command line to assemble an image. It acts as a blueprint for building Docker images.

1. FROM

  • Purpose: Defines the base image for your new image. Every Dockerfile must start with FROM.
  • Syntax: FROM <image>[:<tag>]
  • Example:
    • Ubuntu: FROM ubuntu:22.04
    • Node LTS version: FROM node:lts-slim
  • Tip: Use a specific tag (e.g., 22.04) instead of latest for consistent builds.

2. LABEL

  • Purpose: Adds metadata to an image. This can include information like maintainer, version, or description.
  • Syntax: LABEL <key>=<value> [<key>=<value>...]
  • Example: LABEL maintainer="John Doe <[email protected]>" version="1.0" description="My Awesome App"
  • Tip: Useful for documentation and organizing images.

3. RUN

  • Purpose: Executes commands in a new layer on top of the current image and commits the results. Used for installing packages, creating directories, etc.
  • Syntax:
    • RUN <command> (shell form, default: /bin/sh -c on Linux)
    • RUN ["executable", "param1", "param2"] (exec form, preferred for clarity and avoiding shell string processing)
  • Example (shell form): RUN apt-get update && apt-get install -y git build-essential
  • Example (exec form): RUN ["/bin/bash", "-c", "echo Hello && echo World"]
  • Tip: Chain multiple RUN commands with && and use \ for readability to reduce image layers.

4. WORKDIR

  • Purpose: Sets the working directory for any RUN, CMD, ENTRYPOINT, COPY, and ADD instructions that follow it.
  • Syntax: WORKDIR /path/to/workdir
  • Example:
    WORKDIR /app
    COPY . . # Copies content to /app in the image
    
  • Tip: Use WORKDIR to keep your Dockerfile clean and avoid absolute paths in subsequent commands.

5. COPY

  • Purpose: Copies new files or directories from the host machine (<src>) to the filesystem of the container at path <dest>.
  • Syntax: COPY <src>... <dest>
  • Example:
    COPY ./src /app/src
    COPY requirements.txt /app/
    
  • Tip: Preferred over ADD for copying local files as it's more transparent.

6. ADD

  • Purpose: Similar to COPY but has additional features:
    • Can extract tar files from <src> (local or remote URL).
    • Can accept a URL for <src>.
  • Syntax: ADD <src>... <dest>
  • Example (local tar): ADD app.tar.gz /app/
  • Example (remote URL): ADD http://example.com/latest.zip /app/
  • Tip: Use COPY unless you specifically need the tar extraction or URL download features of ADD.

7. EXPOSE

  • Purpose: Informs Docker that the container listens on the specified network ports at runtime. This is purely for documentation and doesn't actually publish the port.
  • Syntax: EXPOSE <port> [<port>...]
  • Example: EXPOSE 80 443
  • Tip: To actually publish ports, use the -p or --publish flag with docker run.

8. ENV

  • Purpose: Sets environment variables. These variables persist when a container is run and can be accessed by applications inside the container.
  • Syntax: ENV <key>=<value> [<key>=<value>...]
  • Example: ENV NODE_ENV=production PORT=3000
  • Tip: Can be overridden with docker run -e <key>=<value>.

9. ARG

  • Purpose: Defines a variable that users can pass at build-time to the builder using docker build --build-arg <varname>=<value>.
  • Syntax: ARG <name>[=<default value>]
  • Example:
    ARG BUILD_VERSION=1.0
    RUN echo "Building version: $BUILD_VERSION"
    
  • Tip: ARG variables are not available after the image is built, unlike ENV.

10. VOLUME

  • Purpose: Creates a mount point with the specified name and marks it as holding externally mounted volumes from the native host or other containers.
  • Syntax: VOLUME ["/data"] or VOLUME /data
  • Example: VOLUME /var/log/app
  • Tip: Use volumes to persist data generated by and used by Docker containers, especially for database files or logs.

11. USER

  • Purpose: Sets the user name or UID to use when running the image and for any RUN, CMD, and ENTRYPOINT instructions that follow it.
  • Syntax: USER <user>[:<group>]
  • Example: USER appuser
  • Tip: Running containers as a non-root user is a security best practice.

12. HEALTHCHECK

  • Purpose: Tells Docker how to test a container to check if it is still working.
  • Syntax: HEALTHCHECK [OPTIONS] CMD command
  • Example: HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost/ || exit 1
  • Tip: Essential for robust container orchestration (e.g., Kubernetes) to determine container readiness.

13. ENTRYPOINT

  • Purpose: Configures a container that will run as an executable. It defines the command that will always be executed when the container starts.
  • Syntax:
    • ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
    • ENTRYPOINT command param1 param2 (shell form)
  • Example (exec form): ENTRYPOINT ["/usr/bin/supervisord", "-n"]
  • Tip: Often used in conjunction with CMD to provide default arguments to the ENTRYPOINT.

14. CMD

  • Purpose: Provides defaults for an executing container. There can only be one CMD instruction in a Dockerfile.

  • Syntax:

    • CMD ["executable","param1","param2"] (exec form, as the main command)
    • CMD ["param1","param2"] (as default arguments to ENTRYPOINT)
    • CMD command param1 param2 (shell form)
  • Example (with ENTRYPOINT):

        ENTRYPOINT ["nginx"]
        CMD ["-g", "daemon off;"]
        # Default arguments for nginx
    
  • Example (without ENTRYPOINT): CMD ["python", "app.py"]

  • Tip: When ENTRYPOINT is defined, CMD provides the default arguments for it. If ENTRYPOINT is not defined, CMD provides the default command to execute. The docker run command line arguments will override CMD.

CMD vs ENTRYPOINT

Both CMD and ENTRYPOINT instructions define what command gets executed when running a container. There are few rules that describe their co-operation.

  • Dockerfile should specify at least one of CMD or ENTRYPOINT commands.
  • ENTRYPOINT should be defined when using the container as an executable.
  • CMD should be used as a way of defining default arguments for an ENTRYPOINT command or for executing an ad-hoc command in a container.
  • CMD will be overridden when running the container with alternative arguments.

Dockerfile Best Practices:

  • Order matters: Place instructions that change less frequently at the top to leverage Docker's build cache.
  • Minimize layers: Combine RUN commands where possible using && and \ for efficiency.
  • Remove unnecessary files: Clean up temporary files, caches (apt-get clean, rm -rf /var/lib/apt/lists/*) to keep image size small.
  • Use .dockerignore: Exclude files and directories not needed in the image (like .git, node_modules, *.log).
  • Run as non-root: Create a dedicated user and group for your application for security.
  • Specific tags: Always use specific version tags for base images instead of latest.

Example: Containerize a Node app

Use this Dockerfile template:

# node
FROM node:12-slim

# the path inside the container
WORKDIR /usr/src/app

# copy the package.json and package-lock.json, and install dependencies
COPY package*.json ./
RUN npm install

# copy all the source code
COPY . .

# port
EXPOSE 8080

# run the command inside the container
CMD [ "node", "app.js" ]

And add a .dockerignore file:

Dockerfile
.dockerignore
node_modules
npm-debug.log

What is || : &&

|| : && creates a conditional execution flow:

  • The command before || is executed.
  • If it succeeds, the || : part is skipped, and the command after && is executed.
  • If it fails, the || : part is executed (doing nothing), and then the command after && is executed regardless.

This pattern is often used to ensure that subsequent commands in a RUN instruction are always executed, even if a previous command fails. This is useful for tasks like cleaning up or setting up configurations that should happen regardless of earlier failures.

RUN apt-get update || : && apt-get install -y --no-install-recommends some-package