Grigor Khachatryan

Director of Engineering, Platform | Los Angeles, CA


Optimize Your Docker Builds with Dockerfile v1.7.0 Innovations

Published October 5, 2024

Docker continues to innovate, making containerization more efficient and accessible. The release of Dockerfile version 1.7.0 introduces a suite of powerful features that enhance the way we build Docker images. Whether you’re new to Docker or a seasoned pro, these updates are designed to streamline your workflow and optimize your builds.

In this article, we’ll delve into all the new capabilities introduced in Dockerfile v1.7.0 and the experimental v1.7-labs, complete with examples and explanations to help you get the most out of these enhancements.

1. Heredoc Support for Multi-line Commands

Simplifying Multi-line Scripts

Dockerfile v1.7.0 introduces heredoc syntax, allowing you to write multi-line commands more cleanly. This feature improves readability and maintainability, especially when dealing with complex scripts.

Example:

# syntax=docker/dockerfile:1.7.0  
FROM alpine  
  
RUN <<EOF  
echo "Starting setup..."  
apk update  
apk add --no-cache python3 py3-pip  
pip3 install --no-cache-dir flask  
EOF

Explanation:

  • The <<EOF indicates the start of a heredoc block.
  • You can write multiple lines of commands without chaining them with &&.
  • The heredoc ends when EOF is encountered.

Heredoc: A Powerful Tool for Scripting and Programming

2. Conditional Builds with Build Expressions

Making Builds More Dynamic

Conditional builds allow you to include or exclude parts of your Dockerfile based on build-time variables, making your builds more flexible.

Example:

# syntax=docker/dockerfile:1.7.0  
FROM node:14  
  
ARG INSTALL_TOOLS=false  
  
RUN <<EOF  
{% if $INSTALL_TOOLS == 'true' %}  
apt-get update && apt-get install -y vim  
{% endif %}  
EOF

Explanation:

  • The ARG instruction defines a build-time variable INSTALL_TOOLS.
  • The heredoc uses a simple conditional syntax.
  • You can control whether to install additional tools by setting INSTALL_TOOLS when building.

Building with Conditional Argument:

docker build --build-arg INSTALL_TOOLS=true -t my-node-app .

3. Improved RUN --mount Syntax

Enhanced Build-Time Mounts

The RUN --mount flag has been improved to provide more flexibility with build-time mounts, such as caching and binding directories.

Example: Caching Package Downloads

# syntax=docker/dockerfile:1.7.0  
FROM ubuntu:20.04  
  
RUN --mount=type=cache,target=/var/cache/apt \  
    apt-get update && apt-get install -y build-essential

Explanation:

  • type=cache creates a persistent cache mount at /var/cache/apt.
  • Speeds up subsequent builds by caching package downloads.

Example: Binding a Directory

# syntax=docker/dockerfile:1.7.0  
FROM python:3.9  
  
RUN --mount=type=bind,source=./local_requirements.txt,target=/requirements.txt \  
    pip install -r /requirements.txt

Explanation:

  • type=bind mounts a local file or directory into the build.
  • Useful for including files at build-time without adding them to the image.

4. Mount Options in COPY and ADD

More Control Over File Operations

You can now use mount options directly with COPY and ADD, providing greater control over how files are handled during the build.

Example: Copying with Cache Mount

# syntax=docker/dockerfile:1.7.0  
FROM golang:1.17  
  
COPY --mount=type=cache,target=/go/pkg/mod \  
     . /app  
  
WORKDIR /app  
  
RUN go build -o myapp .

Explanation:

  • The --mount flag with COPY allows caching Go modules.
  • Reduces build times by avoiding re-downloading dependencies.

5. Enhanced Secrets Handling

Securely Managing Sensitive Data

Dockerfile v1.7.0 improves the handling of secrets during the build process, ensuring sensitive information doesn’t end up in the final image.

Example: Using Secret Files

# syntax=docker/dockerfile:1.7.0  
FROM alpine  
  
RUN --mount=type=secret,id=mysecret,dst=/run/secrets/mysecret \  
    cat /run/secrets/mysecret > /root/secret_info

Explanation:

  • type=secret mounts a secret into the build at the specified destination.
  • Secrets are not stored in the image layers.

Passing Secrets During Build:

docker build --secret id=mysecret,src=mysecret.txt -t my-secure-app .

6. Variable Expansions

Dynamic Value Substitutions

With Dockerfile v1.7-labs, you can perform variable expansions within your Dockerfile, allowing for more dynamic and flexible builds.

Example: Using Variable Expansions

# syntax=docker/dockerfile:1.7-labs  
FROM alpine  
  
ARG VERSION=1.0.0  
ENV APP_DIR=/opt/myapp-$VERSION  
  
RUN mkdir -p $APP_DIR

Explanation:

  • Variables like $VERSION and $APP_DIR are expanded in the RUN and ENV instructions.
  • Allows you to use build-time arguments and environment variables seamlessly.

Building with Custom Version:

docker build --build-arg VERSION=2.0.0 -t myapp:2.0.0 .

7. Copy with Keeping Parent Directories

Preserving Directory Structure

Dockerfile v1.7-labs introduces the ability to copy files while keeping their parent directories, which is useful for maintaining the original directory hierarchy.

Example: Copying with Parent Directories

# syntax=docker/dockerfile:1.7-labs  
FROM alpine  
  
COPY --keep-parent dir1/dir2/file.txt /destination/

Explanation:

  • The --keep-parent flag tells Docker to preserve the parent directories.
  • The file structure dir1/dir2/file.txt will be replicated under /destination/.

Resulting Directory Structure:

/destination/dir1/dir2/file.txt

8. Exclusion Filters

Fine-Grained Control Over File Copying

Exclusion filters allow you to exclude specific files or patterns when using COPY or ADD, giving you greater control over what gets included in your image.

Example: Using Exclusion Filters

# syntax=docker/dockerfile:1.7-labs  
FROM node:14  
  
COPY . /app/ \  
    --exclude=*.test.js \  
    --exclude=docs/  
  
WORKDIR /app  
RUN npm install

Explanation:

  • The --exclude flag specifies patterns to exclude during the copy.
  • In this example, all .test.js files and the docs/ directory are excluded.
  • Helps reduce image size by omitting unnecessary files.

9. Contexts from Archives and URLs

Flexibility in Build Context Sources

You can now specify build contexts from tar archives or URLs, making it easier to build images from different sources.

Example: Using a Tar Archive as Context

docker build -f Dockerfile https://example.com/myapp.tar.gz -t myapp:latest

Explanation:

  • The build context is provided as a URL pointing to a tar archive.
  • Docker fetches and uses the archive as the build context.

10. Platform-Specific FROM Instructions

Building Multi-Platform Images

The FROM instruction now supports specifying the target platform, enabling multi-platform builds within a single Dockerfile.

Example: Multi-Platform Base Images

# syntax=docker/dockerfile:1.7.0  
FROM --platform=$BUILDPLATFORM golang:1.17 AS builder  
  
WORKDIR /app  
COPY . .  
  
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o myapp .  
  
FROM alpine  
  
COPY --from=builder /app/myapp /usr/local/bin/myapp

Explanation:

  • $BUILDPLATFORM and $TARGETPLATFORM are automatic variables.
  • Allows building binaries for different architectures.

11. Build-Time Arguments in FROM

Dynamic Base Images

You can now use build-time arguments in the FROM instruction to dynamically select the base image.

Example: Using Arguments in FROM

# syntax=docker/dockerfile:1.7.0  
ARG BASE_IMAGE=python:3.9-slim  
  
FROM ${BASE_IMAGE}  
  
WORKDIR /app  
COPY . .  
  
RUN pip install -r requirements.txt

Explanation:

  • ARG before FROM defines a variable usable in FROM.
  • You can change the base image during the build.

Building with a Different Base Image:

docker build --build-arg BASE_IMAGE=python:3.8-slim -t my-python-app .

12. Support for Multiple Build Contexts

Leveraging Additional Contexts

Dockerfile v1.7.0 allows you to specify additional build contexts, making it easier to include files from different locations.

Example: Using Multiple Contexts

# syntax=docker/dockerfile:1.7.0  
FROM ubuntu:20.04  
  
COPY --from=assets /images /usr/share/app/images

Building with Multiple Contexts:

docker build \  
    --build-context assets=./path/to/assets \  
    -t my-multi-context-app .

Explanation:

  • --build-context specifies additional contexts with a name.
  • --from=assets refers to the context named assets.

13. Getting Started with Dockerfile v1.7.0 and v1.7-labs

To take advantage of these new features, ensure you’re using the latest Docker version and specify the Dockerfile syntax version.

For Stable Features:

# syntax=docker/dockerfile:1.7.0

**For Experimental Features (**v1.7-labs):

# syntax=docker/dockerfile:1.7-labs

Note: The v1.7-labs syntax includes experimental features like variable expansions, parent directory preservation in COPY, and exclusion filters.

Enable BuildKit, Docker’s advanced build engine:

export DOCKER_BUILDKIT=1

Alternatively, use the Docker build command with BuildKit enabled:

docker buildx build -t my-app .

Conclusion

Dockerfile v1.7.0 and the experimental v1.7-labs bring significant improvements to the Docker build process. From heredoc support and conditional builds to variable expansions and exclusion filters, these features empower you to create more efficient and secure Docker images.

By incorporating these capabilities into your workflow, you can streamline development, optimize builds, and maintain greater control over your containerized applications.

Further Reading

Docker Best Practices: Images