You can build Docker images directly from Git repositories without ever touching your local machine, which is pretty wild when you think about it.
Let’s see this in action. Imagine you have a simple Node.js app:
// server.js
const http = require('http');
const port = 8080;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from Git!\n');
});
server.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`);
});
And a Dockerfile in the same directory:
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY server.js .
CMD ["node", "server.js"]
Now, instead of cloning this repo, building locally with docker build -t my-app ., and then pushing, you can tell Docker Hub (or another registry that supports this) to do it for you. On Docker Hub, you’d navigate to "Builds" -> "Create a new build". You’d connect your GitHub/Bitbucket account, select the repository, and point it to the Dockerfile and the build context (which is typically the root of your repo, .).
When you push a commit to your connected repository, Docker Hub automatically triggers a build. It essentially clones the repo to a temporary build environment, executes the docker build command there using the specified Dockerfile and context, tags the resulting image (e.g., with the Git commit SHA), and pushes it to your Docker Hub repository. You can then pull this image anywhere using docker pull yourusername/your-repo:git-sha.
The core problem this solves is the disconnect between your code living in Git and your containerized application. Traditionally, you’d need a CI/CD pipeline to bridge this gap. This direct build feature simplifies that by baking the initial build step into the registry itself. It’s a way to get from code to container without explicitly setting up a separate build server or complex CI/CD configuration for the initial build.
Internally, when you configure this, you’re telling the registry: "Here’s my Git repo, here’s the directory containing my Dockerfile (the build context), and here’s how you should tag the resulting image." The registry then provisions a secure, ephemeral environment, clones your specified branch (or tag), navigates to the build context directory, and runs docker build --tag your-repo:tag --file path/to/Dockerfile . (where . is the build context). The --tag part is usually automatically generated based on commit SHAs or branch names.
The exact levers you control are:
- Source Repository: Which Git repository to pull from.
- Branch/Tag: Which specific branch or tag to build from.
- Build Context: The directory within the repository that Docker will use as the root for its build operations (e.g.,
.for the repo root, or a subdirectory likeapp/). - Dockerfile Location: The path to your
Dockerfilewithin the build context. - Image Tagging: How the built image should be named and tagged (e.g., using commit SHAs, branch names, or custom patterns).
One common point of confusion is the build context. When you specify a build context of ., Docker clones the entire repository into the build environment and then uses the specified directory as the root for the COPY and ADD instructions. If your Dockerfile is in the root and it copies server.js with COPY server.js ., it’s copying server.js from the cloned root directory to the working directory inside the container. If your Dockerfile was in a docker/ subdirectory and your build context was also docker/, then COPY . . would only copy files from the docker/ subdirectory into the container.
The next step is usually integrating this into a more robust CI/CD workflow, perhaps using the automatically built image as a base for further testing or deployment.