Docker
Git
DevOps
Dockerfile
Software Development

Dockerfile strategies for Git

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Git and Docker are related in most build pipelines, but they should not always be used the same way inside a Dockerfile. The best strategy depends on whether you are building from a local working tree, a pinned remote revision, or a private repository that requires credentials.

A good Dockerfile keeps builds reproducible, avoids leaking secrets, and minimizes unnecessary cache invalidation.

Prefer Copying The Build Context

For application images, the simplest and most common approach is to build from the checked-out repository on your machine or in CI, then copy the source into the image:

dockerfile
1FROM node:20-alpine
2WORKDIR /app
3
4COPY package*.json ./
5RUN npm ci
6
7COPY . .
8RUN npm run build
9
10CMD ["npm", "start"]

This is usually better than running git clone in the Dockerfile because the build context already contains the code version selected by CI or the developer.

Why git clone Inside The Dockerfile Is Often A Bad Default

Cloning during the image build makes builds less reproducible unless you pin an exact commit or tag. It also complicates authentication for private repositories and can invalidate the cache more often than necessary.

If the Dockerfile says git clone main, then the same Dockerfile can build different images on different days. That is rarely what you want in deployment pipelines.

If You Must Clone, Pin The Revision

Sometimes a Docker build genuinely needs to fetch code during the build, for example in a multi-repo environment. In that case, pin the revision explicitly:

dockerfile
1FROM alpine:3.20
2RUN apk add --no-cache git
3WORKDIR /src
4RUN git clone https://github.com/example/project.git . \
5    && git checkout 9f3c2d1

Pinning a commit makes the result deterministic. Tags are better than branches, and exact commits are better than mutable tags.

Keep Git Metadata Out Of The Final Image

Most runtime images do not need the .git directory. Excluding it reduces image size and avoids leaking repository history.

Use a .dockerignore file:

text
.git
node_modules
build

If you do need Git metadata during the build, prefer a multi-stage build so the final stage contains only the compiled output.

Build Metadata Without Shipping Git

A common pattern is to capture the commit SHA as build metadata rather than copying the whole repository history:

dockerfile
1FROM golang:1.24 AS build
2WORKDIR /src
3COPY . .
4ARG GIT_SHA=unknown
5RUN go build -ldflags="-X main.commit=${GIT_SHA}" -o app .
6
7FROM alpine:3.20
8WORKDIR /app
9COPY --from=build /src/app ./app
10CMD ["./app"]

Then pass the SHA from CI:

bash
docker build --build-arg GIT_SHA=$(git rev-parse HEAD) -t myapp .

That gives the container version traceability without dragging Git internals into production.

Private Repositories And Secrets

If you must access a private repository during build, do not hardcode credentials in the Dockerfile. Use BuildKit SSH forwarding or secret mounts so the secret is available only during the build step.

That keeps tokens out of image layers and out of the Dockerfile itself.

Common Pitfalls

The biggest mistake is cloning a branch inside the Dockerfile and assuming the image is reproducible. Branch tips move, so the image can change without the Dockerfile changing.

Another pitfall is copying .git into the final image by accident. That increases image size and may expose information that the running container does not need.

A third issue is embedding repository credentials directly in the Dockerfile or image layers. Build-time secrets should be ephemeral and external to the image definition.

Summary

  • Prefer COPY . . from a checked-out repository for normal application builds.
  • Avoid git clone inside the Dockerfile unless there is a specific need.
  • If cloning is required, pin an exact commit or stable tag.
  • Exclude .git from the final image with .dockerignore or multi-stage builds.
  • Pass Git metadata into the build explicitly instead of shipping repository history.

Course illustration
Course illustration

All Rights Reserved.