Top 7 Dockerfile Security Tips You Should Know

I built a tool to uncover security flaws in Dockerfiles. While refining it, I collected the most common misconfigurations—and practical ways to avoid them. Don’t use default or latest images. When you skip the version tag — Docker pulls the latest image. The latest versions of images are unpredictable and lead to issues with reproducibility and security. ARG version=latest FROM ubuntu as u1 # skipping version tag FROM ubuntu:latest as u2 # using the directly latest version FROM ubuntu:$version as u3 # using the latest tag from the argument FROM u3 # using the previous latest version of image Don’t run containers as root. You leave a broad attack surface if you don’t set a non-root user. That opens the door to privilege escalation and potential access to the host machine. Note: This applies only to the final user in your Dockerfile. If you switch to a safer user before the container runs, you can run a build steps as root. # Example 1: Implicitly using the default user (can be overridden) FROM ubuntu:20.04 RUN whoami # Example 2: Explicitly setting the user to root FROM ubuntu:20.04 USER root RUN whoami # Example 3: Dynamic user assignment via an environment variable (risky if overridden) ARG APP_USER FROM ubuntu:20.04 USER $APP_USER RUN whoami Don’t store secrets in ENV. When you put secrets in ENV keys, they are stored in your image layers. Anyone with access to the image can extract them. Keep sensitive data out of your Dockerfile — use secret managers or external tools instead. FROM ubuntu:20.04 USER nobody ENV PASSWORD=supersecret123 # this will be stored in image layers Don’t use ARG variables in RUN commands. ARG values can be overridden at build time with --build-arg. If your RUN steps depend on them, an attacker with access to your CI/CD system—like Jenkins—could inject malicious code into the image. FROM ubuntu:20.04 USER nobody ARG INSTALL_PACKAGE=build-essential RUN apt-get update && apt-get install -y $INSTALL_PACKAGE # this variable could be overridden Don’t pipe curl or wget output directly to a shell. Using a pipe (|) or redirection (>) with curl or wget runs scripts without verification. If the source isn’t trusted, an attacker could replace the script with malicious code—opening the door to security breaches. FROM ubuntu:20.04 USER nobody RUN curl -sSL http://example.com/script.sh | sh # this will execute code without verification Don’t expose insecure ports like 22 (SSH). Port 22 is commonly used for SSH — and exposing it opens the door to unauthorized access. Containers rarely need SSH, so exposing it adds risk without real benefit.  Note: The EXPOSE command doesn’t open the port  but marks it as intended for use. FROM ubuntu:20.04 USER nobody EXPOSE 22 Don’t use ADD when you just need to copy files. ADD provides extra features like unpacking tar files and downloading from URLs. If you only need to move files into the image. Use COPY for simpler, more predictable builds. FROM ubuntu:20.04 USER nobody ADD ./app /app # there is nothing special, just example of using ADD command These rules are built into my Dockerfile linter plugin for IntelliJ IDEA , so you can catch these issues automatically when you code. Check out my previous story if you’re curious about how I built the plugin and what I learned. If this article helped you — or if you want more tips on Docker and development best practices — follow me and react to the article. It motivates me to write more.

Apr 9, 2025 - 22:04
 0
Top 7 Dockerfile Security Tips You Should Know

I built a tool to uncover security flaws in Dockerfiles. While refining it, I collected the most common misconfigurations—and practical ways to avoid them.

Don’t use default or latest images.

When you skip the version tag — Docker pulls the latest image. The latest versions of images are unpredictable and lead to issues with reproducibility and security.

ARG version=latest
FROM ubuntu as u1 # skipping version tag
FROM ubuntu:latest as u2 # using the directly latest version
FROM ubuntu:$version as u3 # using the latest tag from the argument
FROM u3 # using the previous latest version of image

Don’t run containers as root.

You leave a broad attack surface if you don’t set a non-root user. That opens the door to privilege escalation and potential access to the host machine.
Note: This applies only to the final user in your Dockerfile. If you switch to a safer user before the container runs, you can run a build steps as root.

# Example 1: Implicitly using the default user (can be overridden)
FROM ubuntu:20.04
RUN whoami

# Example 2: Explicitly setting the user to root
FROM ubuntu:20.04
USER root
RUN whoami

# Example 3: Dynamic user assignment via an environment variable (risky if overridden)
ARG APP_USER
FROM ubuntu:20.04
USER $APP_USER
RUN whoami

Don’t store secrets in ENV.

When you put secrets in ENV keys, they are stored in your image layers. Anyone with access to the image can extract them. Keep sensitive data out of your Dockerfile — use secret managers or external tools instead.

FROM ubuntu:20.04
USER nobody
ENV PASSWORD=supersecret123 # this will be stored in image layers

Don’t use ARG variables in RUN commands.

ARG values can be overridden at build time with --build-arg. If your RUN steps depend on them, an attacker with access to your CI/CD system—like Jenkins—could inject malicious code into the image.

FROM ubuntu:20.04
USER nobody
ARG INSTALL_PACKAGE=build-essential
RUN apt-get update && apt-get install -y $INSTALL_PACKAGE # this variable could be overridden

Don’t pipe curl or wget output directly to a shell.

Using a pipe (|) or redirection (>) with curl or wget runs scripts without verification. If the source isn’t trusted, an attacker could replace the script with malicious code—opening the door to security breaches.

FROM ubuntu:20.04
USER nobody
RUN curl -sSL http://example.com/script.sh | sh # this will execute code without verification

Don’t expose insecure ports like 22 (SSH).

Port 22 is commonly used for SSH — and exposing it opens the door to unauthorized access. Containers rarely need SSH, so exposing it adds risk without real benefit. 
Note: The EXPOSE command doesn’t open the port  but marks it as intended for use.

FROM ubuntu:20.04
USER nobody
EXPOSE 22

Don’t use ADD when you just need to copy files.

ADD provides extra features like unpacking tar files and downloading from URLs. If you only need to move files into the image. Use COPY for simpler, more predictable builds.

FROM ubuntu:20.04
USER nobody
ADD ./app /app # there is nothing special, just example of using ADD command

These rules are built into my Dockerfile linter plugin for IntelliJ IDEA , so you can catch these issues automatically when you code. Check out my previous story if you’re curious about how I built the plugin and what I learned.

If this article helped you — or if you want more tips on Docker and development best practices — follow me and react to the article. It motivates me to write more.