Docker security notes
I had a small collection of links related to Docker security best practices and other security tips but unfortunately some of them disappeared forever. These notes are based on the Docker Security Cheat Sheet manual which rules were included in many other articles and I decided to archive some of them here as well.
Do not expose the Docker daemon socket (even to the containers)
Docker socket /var/run/docker.sock is the UNIX socket that Docker is listening to. This is the primary entry point
for the Docker API. The owner of this socket is root. Giving someone access to it is equivalent to giving unrestricted
root access to your host.
Do not enable tcp Docker daemon socket
If you are running docker daemon with -H tcp://0.0.0.0:XXX you are exposing un-encrypted and unauthenticated direct
access to the Docker daemon.
Do not expose /var/run/docker.sock to other containers
By using volumes -v /var/run/docker.sock://var/run/docker.sock the socket can be exposed to other containers.
Set a user
Configuring the container to use an unprivileged user is the best way to prevent privilege escalation attacks. This can be accomplished in different ways as follows:
During runtime using -u option of docker run command
docker run -u 4000 alpine
During build time. Simple add user in Dockerfile and use it.
FROM alpine
RUN groupadd -r myuser && useradd -r -g myuser myuser
<ROOT USER ACTIONS>
USER myuser
Limit capabilities
Grant only specific capabilities needed by a container. Docker, by default, runs with only a subset of capabilities.
You can change it and drop some capabilities (using --cap-drop) to harden your docker containers, or add some
capabilities (using --cap-add) if needed. Remember not to run containers with the --privileged flag - this will
add ALL Linux kernel capabilities to the container.
The most secure setup is to drop all capabilities
docker run --cap-drop all --cap-add CHOWN alpine
Add –no-new-privileges flag
Always run your docker images with --security-opt=no-new-privileges in order to prevent escalate privileges using
setuid or setgid binaries.
Disable inter-container communication
By default, inter-container communication (ICC) is enabled - it means that all containers can talk with each other
(using docker0 bridged network). This can be disabled by running docker daemon with --icc=false flag.
If ICC is disabled it is required to tell which containers can communicate using --link=CONTAINER_NAME_or_ID:ALIAS option.
Limit resources
The best way to avoid DoS attacks is by limiting resources. You can limit memory, CPU, maximum number of restarts
--restart=on-failure:<number_of_restarts>, maximum number of file descriptors --ulimit nofile=<number> and maximum
number of processes --ulimit nproc=<number>.
Set filesystem and volumes to read-only
Run containers with a read-only filesystem using --read-only flag.
docker run --read-only alpine sh
If an application inside a container has to save something temporarily, combine --read-only flag with --tmpfs
docker run --read-only --tmpfs /tmp alpine sh -c 'echo "whatever" > /tmp/file'
In addition volumes also can be mounted as read-only
docker run -v volume-name:/path/in/container:ro alpine
Mount secrets
--mount=type=secret is a Docker build feature that allows you to securely pass sensitive data (like API keys, credentials
or private tokens) into Dockerfile without embedding them in the image layers or build history.
- Temporary access only - The secret is available only during the specific build step where it’s mounted, then discarded.
- Not stored in the image - The secret never gets baked into the Docker image, so it won’t be exposed if someone inspects the image.
- BuildKit required - This feature requires Docker BuildKit to be enabled (
DOCKER_BUILDKIT=1).
RUN --mount=type=secret,id=my_secret cat /run/secrets/my_secret
Pass the secret to docker build
DOCKER_BUILDKIT=1 docker build --secret id=my_secret,src=/path/to/secret/file .
Or inline
DOCKER_BUILDKIT=1 docker build --secret id=my_secret,src=/dev/stdin . <<< "my-secret-value"
Access it in the Dockerfile
FROM python:3.12
RUN --mount=type=secret,id=my_api_key \
API_KEY=$(cat /run/secrets/my_api_key) && \
pip install some-package --with-api-key=$API_KEY
Mount SSH
--mount=type=ssh is a Docker build feature that allows you to securely use SSH keys during the build process without
embedding them in the image.
- SSH agent forwarding - It mounts your SSH key(s) from the host into the container during the build, making them available to SSH commands.
- Temporary access - The SSH key is only available during that specific RUN step, then removed.
- Not stored in the image - Your SSH keys are never baked into the Docker image layers.
- BuildKit required - Requires Docker BuildKit (
DOCKER_BUILDKIT=1).
RUN --mount=type=ssh <command that uses SSH>
SSH forwarding
DOCKER_BUILDKIT=1 docker build --ssh default=$SSH_AUTH_SOCK .
Or explicitly specify the key
DOCKER_BUILDKIT=1 docker build --ssh default=~/.ssh/id_rsa .
Use in Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y git openssh-client
RUN --mount=type=ssh \
git clone git@github.com:user/private-repo.git /app
RUN --mount=type=ssh \
pip install git+ssh://git@github.com/user/private-repo.git