Skip to content

Deploy docker image to Multiple Environments

In this tutorial, we will create a Jenkins declarative pipeline that builds a Java application, containerizes it using Docker, pushes the image to Docker Hub, and deploys it to Dev, QA, and Prod environments using SSH.

Jenkinsfile

Here is the complete Jenkinsfile. You can find the source code in the GitHub repository.

pipeline {
  agent any
  options {
    disableConcurrentBuilds()
    disableResume()
    buildDiscarder(logRotator(numToKeepStr: '10'))
    timeout(time: 1, unit: 'HOURS')
  }
  tools {
    maven 'maven-3.6.3' 
  }
  parameters {
    choice(name: 'ENVIRONMENT', choices: ['dev', 'qa', 'prod'], description: 'Choose Environment')
  }
  environment {
    DOCKER_CREDENTIAL_ID = "docker-credential"
    SSH_CREDENTIAL_ID = "ssh-pass-credential"
  }
  stages {
    stage ('Build') {
      steps {
        sh "mvn clean package"
      }
    }
    stage ('Docker Build') {
      steps {
        sh "docker build -t hello-world:1.${BUILD_NUMBER} ."
      }
    }
    stage ('Docker Push') {
      steps {
        withCredentials([usernamePassword(credentialsId: "${DOCKER_CREDENTIAL_ID}", passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) {
          sh """
            echo ${DOCKER_PASSWORD} | docker login -u ${DOCKER_USERNAME} --password-stdin
            docker tag hello-world:1.${BUILD_NUMBER} ${DOCKER_USERNAME}/hello-world:1.${BUILD_NUMBER}
            docker push ${DOCKER_USERNAME}/hello-world-java:1.${BUILD_NUMBER}
            docker logout
          """
        }
      }
    }
    stage ('Deploy to Dev') {
      when {
        environment name: "ENVIRONMENT", value: "dev"
      }
      steps {
        script {
          withCredentials([usernamePassword(credentialsId: "${SSH_CREDENTIAL_ID}", passwordVariable: 'SSH_PASSWORD', usernameVariable: 'SSH_USERNAME')]) {
            def remote = [:]
            remote.name = 'test'
            remote.host = '20.193.155.41'
            remote.user = "${SSH_USERNAME}"
            remote.password = "${SSH_PASSWORD}"
            remote.allowAnyHosts = true
            sshCommand remote: remote, command: "docker rm -f hello-world-java || true && docker run -d --name hello-world-java -p 8080:8080 rajasindhuradha/hello-world-java:1.${BUILD_NUMBER}"
          }
        }
      }
    }
    stage ('Deploy to Qa') {
      when {
        environment name: "ENVIRONMENT", value: "qa"
      }
      steps {
        script {
          withCredentials([usernamePassword(credentialsId: "${SSH_CREDENTIAL_ID}", passwordVariable: 'SSH_PASSWORD', usernameVariable: 'SSH_USERNAME')]) {
            def remote = [:]
            remote.name = 'test'
            remote.host = '20.197.20.30'
            remote.user = "${SSH_USERNAME}"
            remote.password = "${SSH_PASSWORD}"
            remote.allowAnyHosts = true
            sshCommand remote: remote, command: "docker rm -f hello-world-java || true && docker run -d --name hello-world-java -p 8080:8080 rajasindhuradha/hello-world-java:1.${BUILD_NUMBER}"
          }
        }
      }
    }
    stage ('Deploy to Prod') {
      when {
        environment name: "ENVIRONMENT", value: "prod"
      }
      steps {
        script {
          withCredentials([usernamePassword(credentialsId: "${SSH_CREDENTIAL_ID}", passwordVariable: 'SSH_PASSWORD', usernameVariable: 'SSH_USERNAME')]) {
            def remote = [:]
            remote.name = 'test'
            remote.host = '20.197.20.178'
            remote.user = "${SSH_USERNAME}"
            remote.password = "${SSH_PASSWORD}"
            remote.allowAnyHosts = true
            sshCommand remote: remote, command: "docker rm -f hello-world-java || true && docker run -d --name hello-world-java -p 8080:8080 rajasindhuradha/hello-world-java:1.${BUILD_NUMBER}"
          }
        }
      }
    }
  }
  post {
    always {
      deleteDir()
    }
  }
}

Explanation

Docker Build & Push

  • Docker Build: Builds the Docker image locally using docker build. It tags the image with 1.${BUILD_NUMBER} to create a unique version for each build.
  • Docker Push: Uses withCredentials to safely inject Docker Hub credentials.
    • It logs in to Docker Hub using docker login with the injected username and password.
    • Tags the image with the Docker Hub username.
    • Pushes the image to the repository.
    • Logs out for security.

Deployment via SSH

The deployment stages (Dev, QA, Prod) use the ssh-agent or ssh-steps plugin functionality (specifically sshCommand) to execute commands on remote servers.

  • withCredentials: Retrieves SSH credentials (SSH_CREDENTIAL_ID).
  • remote map: Defines the connection details for the remote server (host, user, password).
  • sshCommand: Connects to the remote server and executes:
    1. docker rm -f: Removes any existing container with the same name.
    2. docker run: Pulls the new image from Docker Hub and starts a new container on port 8080.

Reference

Important Tips

Tip

Docker Tagging: We effectively tag the image twice: once with latest (implicitly, if not specified in the build) or the specific version, and then re-tag it with the registry username prefix before pushing. This is required for Docker Hub.

Important

SSH Command Security: The sshCommand executes raw shell commands on the remote server. Ensure that the user account used for SSH (test in this example) has restricted permissions, only enough to run the necessary Docker commands.

Quick Quiz

#

Why is it recommended to use docker.withRegistry() or withCredentials when pushing Docker images in a pipeline?

#

Which command is used to authenticate with a Docker registry via the command line?

#

Why includes docker logout at the end of the script?

📬 DevopsPilot Weekly — Learn DevOps, Cloud & Gen AI the simple way.
👉 Subscribe here