Hello! Welcome back to our journey into containerization with Docker.
In our last lesson, we mastered the art of externalizing configuration. We learned how to build a single, portable Docker image that can be deployed across different environments by injecting secrets and environment-specific settings at runtime. This was a crucial step in adhering to the twelve-factor app methodology and making our microservice truly cloud-native.
Now that we have a well-behaved, configurable image, the next logical question is: where do we store it? Just as you wouldn't email JAR files to your servers, you don't build Docker images directly on your production machines. You need a centralized, versioned repository.
Today's lesson focuses on exactly that. Our learning outcome is to build and push a Docker image to a container registry (e.g., Docker Hub, ECR, GCR). This is the bridge between local development and cloud deployment, and a fundamental skill for any developer working with microservices.
1. The Role of a Container Registry
A container registry is to Docker images what a Git repository is to source code, or what Maven Central is to Java libraries. It is a storage system for your container images, providing:
- Centralization: A single source of truth for all your application images.
- Versioning: The ability to store and retrieve specific versions of an image using tags.
- Access Control: Security mechanisms to control who can pull (download) and push (upload) images.
- Distribution: A scalable way to distribute images to developers, CI/CD systems, and container orchestrators like Kubernetes.
The basic workflow is straightforward: you build an image on a machine (your local laptop or a CI/CD build agent), push it to a registry, and then other systems can pull it from that registry to run it.

Popular registries include:
- Docker Hub: A public registry that also offers private repositories. It's the default for the Docker CLI.
- Amazon Elastic Container Registry (ECR), Google Container Registry (GCR), Azure Container Registry (ACR): Registries integrated into the major cloud platforms.
- GitHub Container Registry (GHCR): Integrated with GitHub repositories and Actions.
- Self-hosted registries: Tools like Harbor or JFrog Artifactory that you can run in your own infrastructure.
2. The Manual Workflow: Build, Tag, Push
Let's walk through the fundamental commands to push an image to Docker Hub. This manual process is essential to understand, even though you will typically automate it later.
Step 1: Naming and Tagging for the Registry
A Docker image needs to be named correctly to be pushed to a specific registry. The standard format is:
[registry-hostname]/[username-or-organization]/[repository-name]:[tag]
registry-hostname: Optional for Docker Hub (it's the default), but required for others (e.g.,123456789012.dkr.ecr.us-east-1.amazonaws.com).username-or-organization: Your namespace in the registry (e.g., your Docker Hub ID).repository-name: The name of your application (e.g.,order-service).tag: The version identifier (e.g.,1.0.0,latest).
Let's assume your Docker Hub username is testuser and you've built an image for your inventory-service using the Dockerfile from our previous lessons.
First, you might build it with a simple local tag:
docker build -t inventory-service:1.0.0 .
To prepare it for pushing, you must create a new tag that includes your Docker Hub username. The docker tag command creates an alias; it doesn't duplicate the image data.
docker tag inventory-service:1.0.0 testuser/inventory-service:1.0.0
Now, docker images will show both tags pointing to the same image ID.
Step 2: Authenticate with the Registry
Before you can push, you must log in. For Docker Hub, the command is simple:
docker login
You'll be prompted for your username and password. For other registries like AWS ECR, you'd use a cloud-specific command to get a temporary login token.
Step 3: Push the Image
With the image correctly tagged and your credentials authenticated, you can now push it:
docker push testuser/inventory-service:1.0.0
Docker will upload the image layers to the registry. Because Docker images are layered, subsequent pushes are often faster, as only the changed layers are uploaded.
Test your understanding!
You have just built a Docker image for a microservice called shipping-service. You tagged it locally as shipping-service:2.5.1-hotfix. Your company uses a private Docker Hub organization called my-fintech. What are the two essential docker commands you need to run to get this specific image version into the company's registry? (Assume you are already logged in).
Show answer
-
Tag the image with the full repository name:
docker tag shipping-service:2.5.1-hotfix my-fintech/shipping-service:2.5.1-hotfixThis creates the correctly formatted tag that Docker needs to identify the target repository on Docker Hub.
-
Push the newly tagged image:
docker push my-fintech/shipping-service:2.5.1-hotfixThis command uploads the image to the
shipping-servicerepository within themy-fintechorganization.
3. A Better Way for Java Developers: Google Jib
The manual docker CLI workflow is universal, but for Java applications, there's a more integrated and efficient method: Google Jib.
Given your background in Java and Spring Boot, Jib is a tool you should definitely know about. It's a Maven (and Gradle) plugin that containerizes your application without requiring a Dockerfile or even a Docker daemon installation. This is a massive advantage in CI/CD environments.
Dockerizing Java Apps using Jib
The article 'Dockerizing Java Apps using Jib' from Baeldung provides a great practical walkthrough. Please read the following sections to understand how to use Jib to build and push your Spring Boot application image.
Start with 'Introduction to Jib' to grasp its core benefits. Then, review 'Preparing the Deployment' to see how to configure authentication for your registry. Finally, look at 'Deploying to Docker Hub With Jib' and 'Simplifying the Maven Command' to see the actual commands and pom.xml configuration. Note how it mentions other registries like GCR and ECR.
To summarize the key steps from the article:
-
Add the Jib plugin to your
pom.xml:<plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>3.3.2</version> <!-- Use a recent version --> <configuration> <to> <!-- The full name of the image you want to create --> <image>testuser/inventory-service:${project.version}</image> </to> </configuration> </plugin> -
Configure Credentials: For Docker Hub, you can add server credentials to your Maven
settings.xmlfile (~/.m2/settings.xml). This avoids interactive logins.<servers> <server> <id>registry.hub.docker.com</id> <username>your-dockerhub-username</username> <password>your-dockerhub-password-or-token</password> </server> </servers>(Note: Using an access token instead of your password is more secure.)
-
Build and Push: Now, a single Maven command handles everything: compiling your code, building an optimized Docker image, and pushing it to the registry.
mvn compile jib:build
Jib is intelligent. It separates your application into layers (dependencies, resources, classes) automatically, leading to faster rebuilds and uploads when only your code changes. For a Java developer, this is often the preferred method for containerizing applications.
4. The Big Picture: Pushing from a CI/CD Pipeline
In a professional environment, you rarely push images from your local machine to production registries. This process is automated within a Continuous Integration/Continuous Deployment (CI/CD) pipeline. When you push code to a Git repository, a pipeline automatically triggers to build, test, and push the new container image.

This automation is where all the concepts we've learned come together. The CI/CD script will execute commands very similar to what we did manually, but in a scripted, repeatable manner.
Docker Best Practices: A Comprehensive Technical Guide
To see what this looks like in practice, let's review a section from the 'Docker Best Practices' guide. It contains an excellent example of a CI/CD workflow using GitHub Actions.
Please read the subsection 'CI/CD Build and Push Workflow'. Focus on the 'Typical workflow' list to understand the sequence of events. Then, examine the 'GitHub Actions example' YAML file. You don't need to understand every line, but notice the key steps: 'Log into ... Registry', 'Build ... image', and 'Push image'. This is the automated version of what we did manually.
The key takeaway from the CI/CD workflow is automation and traceability. The pipeline automatically:
- Logs in to the registry using secure secrets.
- Builds the image.
- Tags the image with meaningful versions, often combining semantic versioning with the Git commit SHA for full traceability (e.g.,
my-fintech/shipping-service:2.5.1-a1b2c3d). - Pushes the tagged image to the registry.
This ensures that every image in your registry can be traced back to the exact version of the code that produced it, a critical requirement for production stability and security.
Conclusion
You've now closed the loop from code to a distributable artifact. We have a container image that is not only built and configured correctly but is now stored in a central, secure, and versioned repository, ready for deployment.
Key Takeaways:
- Container Registries are Essential: They are the central hub for storing and distributing your Docker images.
- The
build-tag-pushflow is fundamental: Understand how to usedocker build,docker tag,docker login, anddocker pushto manually manage images. - Image Naming is Key: The
registry/namespace/repository:tagformat dictates where your image will be stored. - Leverage Integrated Tools: For Java projects, Google Jib streamlines the containerization process, bypassing the need for a local Docker daemon and Dockerfile.
- Automation is the Goal: In production, image pushing is handled by automated CI/CD pipelines for consistency, security, and traceability.
In our next lesson, we'll tackle a very practical topic: "Debug a running container by accessing its shell, viewing logs, and inspecting its network." Now that we can build and push an image, we need to be prepared to troubleshoot it when it's running.
Can't find a good explanation? Sign up and we'll make it for you
Sign up