Mark Ku's Blog

Motivation

I previously used PowerShell to remotely build Docker images on a test machine, but as the team grew that approach became too cumbersome. I eventually set up Jenkins and integrated it with GitHub credentials and remote Docker Desktop deployment.

Development Environment Overview

The expected flow: after a developer pushes a commit, Jenkins automatically pulls the GitHub repository, builds the image, and deploys it to the target Windows Docker Desktop host.

Step 1: Set Up the Jenkins Environment

Open PowerShell and create a directory for the Jenkins workspace

$workspacePath = "C:\jenkins_workspace"
New-Item -ItemType Directory -Path $workspacePath -Force | Out-Null

Run the following command to start a Jenkins container and mount the workspace to the local disk

docker run -d -p 8080:8080  -p 50000:50000 -v ${hostWorkspacePath}:/var/jenkins_home/workspace  -v /var/run/docker.sock:/var/run/docker.sock --name jenkins --restart=always jenkins/jenkins:lts 

Note: hostWorkspacePath is the folder path on the host machine.

When you see the initial password prompt, exec into the container to retrieve it

Jenkins initial setup screen requesting administrator password
Jenkins initial setup screen requesting administrator password

Install the suggested plugins

Jenkins Customize screen with 'Install suggested plugins' option highlighted
Jenkins Customize screen with 'Install suggested plugins' option highlighted

Wait for installation to complete, then access Jenkins at localhost:8080

Jenkins Getting Started page with plugin installation options
Jenkins Getting Started page with plugin installation options

Manage Jenkins > Plugins > Available plugins — install the following commonly used plugins

  • SSH
  • Publish Over SSH
  • Msbuild
  • .NET SDK Support
  • Office 365 Connector Version
  • PowerShell
  • Docker
  • Docker Pipeline
  • HTTP Request
  • Stage View
  • Pipeline Utility Steps
  • Pipeline: Input Step

Step 2: Install Docker Inside the Jenkins Container

Since I am more comfortable with Docker commands, I install Docker directly inside the Jenkins container:

docker logs -f jenkins // get the init password 
docker exec -it -uroot jenkins bash
apt-get update && apt-get -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common && curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable" && apt-get update && apt-get -y install docker-ce

Step 3: Configure Jenkins GitHub Global Credentials

Connect to the Jenkins container

docker exec -it -uroot jenkins bash

Install Docker CLI

apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
echo "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce-cli

Generate an SSH key pair (keep pressing Enter for defaults)

ssh-keygen -t rsa -C "root" 

Read the public key and add it to GitHub under SSH and GPG keys

cat /root/.ssh/id_rsa.pub

Read the private key and add it to Jenkins global credentials

Navigate to: Manage Jenkins > Credentials > System > Global credentials (unrestricted) > Add credentials (Copy the entire key including -----BEGIN OPENSSH PRIVATE KEY-----) > Create

cat /root/.ssh/id_rsa
Jenkins UI adding SSH username with private key credentials
Jenkins UI adding SSH username with private key credentials

After creating the credential, note the generated ID — you will paste it into the pipeline later

Jenkins global credentials page showing an SSH private key credential
Jenkins global credentials page showing an SSH private key credential

Disable host key verification: Manage Jenkins > Security > Git Host Key Verification Configuration > No verification

On the Jenkins host machine, grant the necessary permissions

sudo chmod -R 777 /var/jenkins_home/
sudo chown $USER /var/run/docker.sock
sudo gpasswd -a $USER docker
newgrp docker

Step 4: Create a New Build Job

Dashboard > New Item > Pipeline

Jenkins new item page with project types listed
Jenkins new item page with project types listed

Enable the GitHub hook trigger

Jenkins configuration showing GitHub project URL and hook trigger
Jenkins configuration showing GitHub project URL and hook trigger

Write the pipeline script

properties([pipelineTriggers([githubPush()])])
pipeline {
    agent any 

    environment {
        tag = ':latest'
        imageShortName = 'next-ap'
        imageName = "${imageShortName}${tag}"
        containerName = "${imageShortName}-1"
        containerUrl = "192.168.50.49:2375"
        dockerfile = "./Dockerfile"
        port = "30000:80"
    }   
   
    stages {
        stage("GitHub Pull") {
             steps {
                git branch: 'main', 
                credentialsId: 'b2ef50dd-xxxx-xxx-a4ef-xxx', 
                url:  '[email protected]:markku636/ec.git/'
            }
            
        }


        stage('Stop containers') {
            steps {
                script {
                    containerStatus = sh(script: "docker -H=\"${containerUrl}\" ps -a --filter=name=${containerName} -q", returnStdout: true).trim()
                    if (containerStatus != '') {
                        echo "Stopping container ${containerName}"
                        sh "docker -H=\"${containerUrl}\" stop ${containerName}"
                    } else {
                        echo "Container ${containerName} does not exist"
                    }
                }
            }
        }

        stage('Remove containers') {
            steps {
                script {
                    containerStatus = sh(script: "docker -H=\"${containerUrl}\" ps -a --filter=name=${containerName} -q", returnStdout: true).trim()
                    if (containerStatus != '') {
                        echo "Removing container ${containerName}"
                        sh "docker -H=\"${containerUrl}\" rm -f ${containerName}"
                    } else {
                        echo "Container ${containerName} does not exist"
                    }
                }
            }
        }

        stage('Remove image') {
            steps {
                script {
                    existingImages = sh(script: "docker -H=\"${containerUrl}\" images --filter=reference='${imageName}' -q", returnStdout: true).trim()
                    if (existingImages != '') {
                        echo "[Removing image] Removing the existing image.."
                        sh "docker -H=\"${containerUrl}\" rmi -f '${imageName}'"
                    } else {
                        echo "[Removing image] The image does not exist"
                    }
                }
            }
        }
        
        stage('Build image remotely') {
            steps {
                sh 'docker -H="${containerUrl}" build -t "${imageName}" . -f "${dockerfile}"'
            }
        }

        stage('Create and start container application') {
            steps {
                sh 'docker -H="${containerUrl}" run -d --name "${containerName}" --restart=always -p "${port}" "${imageShortName}"'
            }
        }
        
    }
}

After all that setup, the pipeline is finally up and running. Containerization keeps making it easier and faster to set up and migrate development environments.

Jenkins pipeline stage view displaying build times and statuses
Jenkins pipeline stage view displaying build times and statuses

Appendix — Fix the unix:///var/run/docker.sock: connect: permission denied error for non-root users on Linux

  1. Create a service file:

    sudo vim /etc/systemd/system/docker-sock-permissions.service
    
  2. Add the following content:

    [Unit]
    Description=Set permissions for Docker socket
    After=docker.service
    
    [Service]
    Type=oneshot
    ExecStart=/bin/chmod 666 /var/run/docker.sock
    RemainAfterExit=true
    
    [Install]
    WantedBy=multi-user.target
    
  3. Enable the service:

    sudo systemctl enable docker-sock-permissions.service
    
  4. Start the service (or wait for it to run automatically on next boot):

    sudo systemctl start docker-sock-permissions.service
    

References

Reference 1 Reference 2

Memo — Retrieve the admin initial password

cat /var/jenkins_home/secrets/initialAdminPassword

Author

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。Read More

Found this useful?

The author's free tools, daily podcasts and newsletter are all here.

Mark Ku · This article is licensed under CC BY 4.0. Credit the author and link back to the original when reusing it.

Comments

Subscribe to Newsletter

Subscribe to get new posts delivered instantly — never miss a tech share.

By submitting, you agree to receive emails. You can anytime.

Popular Posts

View all
Mark Ku
··602

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution
Mark Ku
··490

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.
Mark Ku
··333

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki
Mark Ku
··264

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning
Mark Ku
··221

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1
Mark Ku
··215

Setting Up Samba on Ubuntu to Share Folders with Windows 11

Setting Up Samba on Ubuntu to Share Folders with Windows 11