Understanding Docker Secret Mounts
A comprehensive guide to using Docker secret mounts correctly, avoiding common mistakes, and implementing secure practices for handling sensitive data in containers.
One of the most common mistakes developers make is accidentally exposing secrets in their Docker images. Consider this problematic example:
# DON'T DO THIS
ARG NPM_TOKEN
ENV NPM_TOKEN=$NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
This approach seems convenient but is fundamentally flawed because:
- The secret becomes part of the image layer history
- Anyone with access to the image can extract the secret
- The secret persists even after being "removed"
The Right Way: Using Secret Mounts
Docker provides a secure way to handle secrets during build time using secret mounts. Here's the correct approach:
# DO THIS INSTEAD
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) \
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
Key Characteristics of Secret Mounts
-
Temporary Access
- Secrets are only available during the specific build step
- They're not persisted in the final image or layer history
-
Filesystem Mounting
- Secrets are mounted at
/run/secrets/<secret_name>
- Each secret gets its own file in this directory
- Secrets are mounted at
-
Build-time Only
- Secrets mounted this way are only available during build time
- They're automatically cleaned up after the build step completes
Using Secrets in Docker Compose
For runtime secrets in a Docker Compose environment, you can define them in your compose file:
services:
myapp:
image: myapp:latest
secrets: - my_secret
build:
context: .
secrets: - npm_token
secrets:
my_secret:
file: ./my_secret.txt
npm_token:
environment: NPM_TOKEN
How to use multi-stage builds with secrets
- Use Build-time Secrets for Dependencies
Imagine that you have a private npm package that you need to install for a docker container that you need to distribute. However, you don't want the consumers of the docker container to have access to the npm token. Baking that into the image via an ARG
or ENV
instruction is a bad idea. Instead, you can use a multi-stage build to install the npm package and then remove the secret.
# Example for private npm packages
RUN --mount=type=secret,id=npm_token \
npm config set //registry.npmjs.org/:\_authToken=$(cat /run/secrets/npm_token) && \
npm install && \
npm config rm //registry.npmjs.org/:\_authToken
- Multi-stage Builds with Secrets
FROM node:alpine AS builder
RUN --mount=type=secret,id=npm_token \
# Use secret for building
FROM node:alpine
# Final image has no trace of secrets
COPY --from=builder /app/dist ./dist