Build images
Control the build context
Keep secrets, dependencies, Git history, and unrelated files out of image builds with a small context and .dockerignore.
Remember the final . in docker build -t notes-api:dev .? That’s the build context: the directory Docker sends to the builder before running your Dockerfile. COPY can only read files from there. A file outside the context doesn’t exist as far as the build is concerned.
That sounds like a detail. It’s not, for two reasons.
Speed
Docker sends the whole context to the builder, every build. If your project folder has a node_modules with 300 MB of dependencies and a .git folder with years of history, each build starts by copying all that. You’ll see it in the output, in a line like transferring context: 312.4MB.
Safety
The second reason is worse. Anything in the context is available to COPY. A COPY . . copies your .env file with real credentials into the image. And here’s the trap: deleting it in a later RUN step doesn’t help. Each instruction is a layer, and the file is still in the earlier layer. Anyone with the image can dig it out.
So never copy a secret and then try to clean it up. Keep it out of the context in the first place.
.dockerignore
A .dockerignore file lists paths to exclude from the context. It works like .gitignore. Create it next to the Dockerfile:
node_modules
.git
.env
coverage
dist
*.log
Excluded files never reach the builder, so COPY can’t grab them by accident. node_modules is excluded because we’ll install dependencies inside the image, where they match the image’s operating system. dist is excluded for the same reason: we build inside the image.
See the context size
Run a build with plain progress output and read the context line:
docker build --progress=plain -t notes-api:dev .
Look for transferring context. For the Notes API it should be a few kilobytes.
If it’s unexpectedly large, check two things before anything else: the .dockerignore file, and the directory you passed as the last argument. Running docker build from your home folder with . as the context sends your entire home folder. It happens.
When the build really needs a secret
Sometimes an install step needs a token, for example to download a private npm package. Don’t copy it. Use a BuildKit secret mount, which makes the file available only to that one RUN step and never writes it into a layer. In the Dockerfile:
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
And at build time:
docker build --secret id=npmrc,src=$HOME/.npmrc -t notes-api:dev .
Try this: create .dockerignore, then add a temporary 100 MB file to the project with head -c 100000000 /dev/zero > big.bin. Build once with big.bin in .dockerignore and once without, and compare the context size in the output.
Lesson completed