Yesterday, my Jenkins pipeline could build Docker images for backend and frontend.
But those images only existed on my local machine. They weren't accessible anywhere else, not on another developer's computer, not on a cloud server, not anywhere.
Today, I fixed that. I configured Jenkins to push Docker images to Docker Hub a public registry where anyone can pull my images.
Now my images are available everywhere.
First: What is a Docker Registry?
The Analogy
| Concept | Analogy |
|---|---|
| Docker image | A shipping container with your app |
| Docker registry | A shipping port where containers are stored |
| Docker Hub | The largest public shipping port |
| docker pull | Bringing a container FROM the port |
| docker push | Sending a container TO the port |
Why Push to a Registry?
| Without Registry | With Registry |
|---|---|
| Images only on your laptop | Images available anywhere |
| Can't share with team | Anyone can pull the image |
| Need to rebuild on every server | Pull once, run anywhere |
| No versioning | Tag versions (v1.0, latest) |
Step 1: Create Docker Hub Account
If you don't have one:
- Go to hub.docker.com
- Sign up for a free account
- Remember your username
Step 2: Add Docker Hub Credentials to Jenkins
Why Credentials?
Jenkins needs to log in to Docker Hub before it can push images.
Adding Credentials
- Open Jenkins:
http://localhost:8081 - Manage Jenkins -> Credentials
- System -> Global credentials -> Add Credentials
Fill out the form:
| Field | Value |
|---|---|
| Kind | Username with password |
| Username | Your Docker Hub username |
| Password | Your Docker Hub password |
| ID | docker-hub-credentials |
| Description | Docker Hub login for pushing images |
- Click "Create"
Step 3: Update Jenkinsfile with Push Stage
The Complete Jenkinsfile with Push
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'docker.io'
DOCKER_USERNAME = 'your-dockerhub-username' // Replace this!
BACKEND_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_USERNAME}/myapp-backend:latest"
FRONTEND_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_USERNAME}/myapp-frontend:latest"
}
stages {
stage('Checkout') {
steps {
echo 'Cloning repository...'
checkout scm
}
}
stage('Backend Dependencies') {
steps {
dir('backend') {
sh 'npm install'
}
}
}
stage('Frontend Build') {
steps {
dir('frontend') {
sh 'npm install'
sh 'npm run build'
}
}
}
stage('Docker Build') {
steps {
echo 'Building Docker images...'
sh 'docker build -t ${BACKEND_IMAGE} ./backend'
sh 'docker build -t ${FRONTEND_IMAGE} ./frontend'
}
}
stage('Docker Push') {
steps {
echo 'Pushing to Docker Hub...'
withCredentials([usernamePassword(
credentialsId: 'docker-hub-credentials',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh 'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'
sh 'docker push ${BACKEND_IMAGE}'
sh 'docker push ${FRONTEND_IMAGE}'
}
}
}
stage('Success') {
steps {
echo 'Pipeline completed successfully!'
echo "Images pushed to:"
echo " - ${BACKEND_IMAGE}"
echo " - ${FRONTEND_IMAGE}"
}
}
}
post {
failure {
echo 'Pipeline failed! Check the logs.'
}
success {
echo 'Images published to Docker Hub!'
}
}
}
Understanding New Parts
| Part | What it does |
|---|---|
environment { DOCKER_USERNAME } |
Stores your Docker Hub username |
BACKEND_IMAGE = "docker.io/username/image" |
Full image name including registry |
withCredentials() |
Safely uses credentials without exposing passwords |
docker login |
Authenticates with Docker Hub |
docker push |
Uploads the image to the registry |
Step 4: Replace the Username
Important: Change your-dockerhub-username to YOUR actual Docker Hub username!
DOCKER_USERNAME = 'johndoe' // Use your actual username
Understanding Credential Security
What NOT to do (Hardcoding passwords)
// DON'T DO THIS!
sh 'docker login -u johndoe -p MyRealPassword123'
Why not? Anyone who sees your Jenkinsfile can steal your password!
What TO do (Using Jenkins credentials)
withCredentials([usernamePassword(
credentialsId: 'docker-hub-credentials',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh 'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'
}
Why this is safe:
- Password never appears in logs
- Password not stored in Jenkinsfile
- Jenkins encrypts credentials
- Only authorized users can access
Step 5: Commit and Push
git add Jenkinsfile
git commit -m "ci: add Docker Hub push stage
- Tag images with Docker Hub registry
- Add credentials for Docker login
- Push backend and frontend to Docker Hub"
git push origin main
Step 6: Run the Pipeline
- Go to Jenkins -> microservices-ci
- Click "Build Now"
- Watch the console output
Console Output
[Pipeline] stage
[Pipeline] { (Docker Push)
[Pipeline] echo
Pushing to Docker Hub...
[Pipeline] withCredentials
[Pipeline] {
[Pipeline] sh
+ echo ****
+ docker login -u johndoe --password-stdin
Login Succeeded
[Pipeline] sh
+ docker push docker.io/johndoe/myapp-backend:latest
The push refers to repository [docker.io/mkangeth/myapp-backend]
latest: digest: sha256:abc123... size: 2420
[Pipeline] sh
+ docker push docker.io/johndoe/myapp-frontend:latest
The push refers to repository [docker.io/johndoe/myapp-frontend]
latest: digest: sha256:def456... size: 1024
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Success)
[Pipeline] echo
Pipeline completed successfully!
Images pushed to:
- docker.io/johndoe/myapp-backend:latest
- docker.io/johndoe/myapp-frontend:latest
Finished: SUCCESS
Step 7: Verify on Docker Hub
- Go to
https://hub.docker.com/u/your-username - You should see:
myapp-backendmyapp-frontend
Check Repository Details
Click on myapp-backend:
-
Tags:
latest - Size: ~135MB
-
Pull command:
docker pull yourusername/myapp-backend:latest - Last pushed: Just now
Step 8: Test Pulling from Any Computer
On any machine with Docker:
# Pull your images
docker pull yourusername/myapp-backend:latest
docker pull yourusername/myapp-frontend:latest
# Run them
docker run -d -p 3001:5000 yourusername/myapp-backend:latest
docker run -d -p 8080:80 yourusername/myapp-frontend:latest
# Test
curl http://localhost:3001/health
# Open browser to http://localhost:8080
It works anywhere in the world!
Before vs After
| Aspect | Before (Day 10) | After (Day 11) |
|---|---|---|
| Images location | Only on local machine | Docker Hub (worldwide) |
| Sharing | Send files manually | docker pull |
| Team access | Send images via USB/email | Anyone can pull |
| Deployment | Copy images to server | Pull from registry |
| Version tracking | Manual | Tags in registry |
Complete CI/CD Pipeline Now
+-----------------------------------------------------------------------------------+
| YOUR COMPLETE CI/CD PIPELINE |
+-----------------------------------------------------------------------------------+
| |
| git push -> Jenkins -> Build -> Docker -> Push -> Available |
| | | | | |
| v v v v |
| Pulls Installs Creates Uploads |
| code deps images to Hub |
| |
+-----------------------------------------------------------------------------------+
|
v
+-----------------+
| DOCKER HUB |
| (Your Images) |
+-----------------+
|
v
+---------------+---------------+
v v v
Your Laptop Cloud Server Team Member
(docker pull) (docker pull) (docker pull)
Key Takeaways
- Images need a home = Docker Registry (Docker Hub is the default)
- Credentials protect secrets = Never hardcode passwords in Jenkinsfile
- Tag images properly = Include registry and username in the tag
- Push once, pull anywhere = Images become globally available
- Latest is fine, versions are better = Consider using build numbers as tags
Resources
Let's Connect!
Have you pushed images to Docker Hub before? What registry do you use (Docker Hub, AWS ECR, GCR, others)?
Drop a comment or connect on LinkedIn. Let's learn together!
Top comments (0)