7 New Dockerfile Features You Should Know About

Docker has evolved significantly since its introduction, with each release bringing new features and improvements to the Dockerfile specification. Some of these changes are revolutionary, making builds faster, more secure, and easier to maintain. This article explores seven of the most impactful recent Dockerfile features you should consider adopting in your containerization workflow.

Whether you're a Docker veteran or just starting your containerization journey, these features can significantly improve your build process and resulting images. Let's dive in!

1. BuildKit Mounts

v18.09+

BuildKit introduced a powerful mounting system that allows you to bind, cache, temporary storage, and even secrets during your build process. This is a game-changer for performance and security.

BuildKit Only Docker 18.09+
Traditional approach
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
With BuildKit mounts
# syntax=docker/dockerfile:1.4
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm install
COPY . .
RUN --mount=type=cache,target=/root/.npm \
    npm run build

Four types of mounts are supported:

  1. Cache mounts: Persist cache between builds, like package manager caches
  2. Bind mounts: Share content from other stages or the build context
  3. Tmpfs mounts: Use memory-based temporary storage
  4. Secret mounts: Securely access sensitive information during build

The most common use case is caching package manager files to speed up builds dramatically. The cache persists between builds but isn't included in the final image, keeping it lean.

2. Multi-stage Builds with External Images as Base

v17.05+

Multi-stage builds have been a staple of efficient Dockerfiles since their introduction, but a recent improvement allows you to use external images as stages, making your builds even more flexible.

Docker 17.05+
# Define a local build stage
FROM golang:1.18 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# Use an external image with a specific digest as a stage
FROM alpine:3.16.2@sha256:1304f174557314a7ed9eddb4eab12fed18eb1a1a3588322288286dc2c449f835 AS base
RUN apk add --no-cache ca-certificates tzdata

# Copy from the local build stage to the final image
FROM base
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]

This approach offers several benefits:

  • Enhanced security by using image digests instead of tags
  • Better modularity by referencing external images directly
  • Simplified CI/CD pipelines by leveraging pre-built components

This feature is perfect for creating a consistent base environment across multiple projects or when you want to leverage pre-built components from other teams or public images.

3. Heredoc Syntax Support

v20.10+

Gone are the days of awkward multi-line RUN commands with backslashes. BuildKit now supports "heredoc" syntax, making complex multi-line scripts much more readable in your Dockerfile.

BuildKit Only Docker 20.10+
Old approach with backslashes
FROM ubuntu:22.04
RUN apt-get update && \
    apt-get install -y python3 python3-pip && \
    pip3 install requests && \
    rm -rf /var/lib/apt/lists/* && \
    mkdir -p /app/data
New heredoc syntax
# syntax=docker/dockerfile:1.4
FROM ubuntu:22.04
RUN <

This syntax is especially useful for:

  • Complex installation scripts
  • Creating configuration files inline
  • Running multi-line commands

You can also use heredoc to insert files directly in your Dockerfile:

# syntax=docker/dockerfile:1.4
FROM nginx:alpine
RUN < /etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name localhost;
    
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}
EOF

This makes your Dockerfile more self-contained and easier to understand, as configuration files can be included directly in the Dockerfile rather than as separate files in your build context.

4. BuildKit's --mount=type=secret

v18.09+

One of the most significant security improvements in modern Docker builds is the ability to use secrets during build time without embedding them in the final image.

BuildKit Only Docker 18.09+
# syntax=docker/dockerfile:1.4
FROM alpine
RUN --mount=type=secret,id=mysecret,dst=/root/.secret \
    cat /root/.secret

And then build the image with:

$ echo "MY_SECRET_VALUE" > mysecret.txt
$ DOCKER_BUILDKIT=1 docker build --secret id=mysecret,src=mysecret.txt .

This solves a critical security issue in Docker builds. Previously, developers had to choose between:

  • Hard-coding secrets in Dockerfiles (extremely insecure)
  • Using build arguments (which remain visible in image history)
  • Complex multi-stage build patterns

With secret mounts, you can securely use:

  • API keys for downloading resources
  • Private repository credentials
  • Certificates and keys
  • Any sensitive data needed during build but not at runtime

Security Note

Secret mounts are only available during build time and do not persist in the resulting image layers, ensuring your secrets don't leak into your production images.

5. ARG Before FROM

v17.05+

A seemingly small but highly useful feature is the ability to use ARG instructions before FROM in your Dockerfile, which allows you to parameterize base images.

Docker 17.05+
# Use ARG before FROM to define base image parameters
ARG NODE_VERSION=16
ARG ALPINE_VERSION=3.16

# Use the ARG values in the FROM instruction
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION}

# The rest of your Dockerfile
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]

This feature enables several powerful use cases:

  • Create matrix builds with different base images
  • Centralize version management
  • Simplify CI/CD pipelines with parameterized builds
  • Test application compatibility across different runtime versions

You can override these arguments at build time:

$ docker build --build-arg NODE_VERSION=18 --build-arg ALPINE_VERSION=3.17 .

This approach is excellent for maintaining a single Dockerfile that can adapt to different environments or testing requirements, significantly reducing duplication in your containerization code.

6. Improved COPY with --link Flag

v23.0+

BuildKit introduced the --link flag for COPY commands, which enhances layer caching and makes your builds faster by tracking content changes more efficiently.

BuildKit Only Docker 23.0+
# syntax=docker/dockerfile:1.4
FROM node:18-alpine AS builder
WORKDIR /app
COPY --link package*.json ./
RUN npm ci
COPY --link . .
RUN npm run build

FROM nginx:alpine
COPY --link --from=builder /app/dist /usr/share/nginx/html

The --link flag changes how BuildKit tracks dependencies and can lead to more efficient caching. Rather than invalidating cache based on metadata changes (like file timestamps), it focuses on actual content changes.

Benefits include:

  • More reliable cache hits, even when file metadata changes
  • Improved performance when using --from in multi-stage builds
  • Better parallelization of build steps

This feature is particularly valuable in CI/CD environments where you want consistent, predictable build behavior regardless of how or when files were updated.

7. RUN --network and Network Control

v19.03+

BuildKit provides granular control over network access during build steps with the --network flag for RUN commands, helping to create more secure and deterministic builds.

BuildKit Only Docker 19.03+
# syntax=docker/dockerfile:1.4
FROM python:3.10-slim

# No network access for this step
RUN --network=none mkdir -p /app/src

# Default network for this step
COPY requirements.txt .
RUN --network=default pip install -r requirements.txt

# No network for security-sensitive operations
RUN --network=none adduser --disabled-password --gecos '' appuser

COPY . .
USER appuser
CMD ["python", "app.py"]

The --network flag accepts several values:

  • default: Standard Docker bridge network access
  • none: No network access (isolated)
  • host: Use the host network (careful, security implications)
  • custom-network-name: A specific Docker network

This feature offers important benefits:

  • Enhanced security by isolating build steps that don't need network access
  • Improved build determinism by controlling network dependencies
  • Better error detection for hidden network dependencies
  • Ability to connect to custom networks for specialized build requirements

Best Practice

Consider using --network=none as the default for all build steps, and only enable network access for steps that specifically require it. This follows the principle of least privilege and makes your builds more secure.

Compatibility and Adoption Considerations

Before implementing these features in your production Dockerfiles, it's important to consider compatibility with your current Docker environment.

Feature Requires BuildKit Min Docker Version CI/CD Compatibility
BuildKit Mounts 18.09+ Good; widely supported
External Images as Base 17.05+ Excellent; works everywhere
Heredoc Syntax 20.10+ Good; requires BuildKit
Secret Mounts 18.09+ Good; special setup needed in some CI systems
ARG Before FROM 17.05+ Excellent; works everywhere
COPY with --link 23.0+ Limited; requires recent Docker
RUN --network 19.03+ Good; requires BuildKit

To enable BuildKit in your environment (required for many of these features):

# Enable BuildKit for a single build
DOCKER_BUILDKIT=1 docker build .

# Enable BuildKit by default in Docker daemon
# Edit /etc/docker/daemon.json:
{
  "features": {
    "buildkit": true
  }
}

Conclusion

The Docker ecosystem continues to evolve rapidly, with BuildKit bringing significant improvements to the containerization workflow. By adopting these modern Dockerfile features, you can create more efficient, secure, and maintainable container images.

When implementing these features, consider:

  • Start with backward-compatible changes if you're working in a team
  • Update your CI/CD pipelines to enable BuildKit
  • Document your usage of advanced features for team awareness
  • Consider creating a custom Dockerfile syntax version header to clearly indicate which features you're using

Remember that while these features improve your Dockerfile, good containerization practices remain essential: keep your images small, minimize layer count, and follow the principle of least privilege.

Pro Tip

If your team uses different Docker versions, consider maintaining a "classic" and "modern" version of critical Dockerfiles. The classic version can serve as a fallback for environments without BuildKit support.

Share this article

Related Articles

5 Advanced Docker Caching Techniques

May 10, 2025

Learn how to optimize your Docker builds with five advanced caching techniques that can significantly reduce build times.

Case Study: Optimizing Build Pipelines for Microservices

Coming Soon

How a large financial company reduced their build times by 70% and image sizes by 45% by implementing advanced Docker build techniques.

10 Docker Security Best Practices

Coming Soon

Essential security practices for building and deploying Docker containers in production environments.

Comments

Comments system placeholder. In a real implementation, this would be integrated with a third-party comments system or custom solution.