---
title: "Integrating Windows Docker Desktop, Jenkins in Docker, GitHub SSH Credentials, and Docker Image CI/CD"
description: "Integrating Jenkins in Docker, GitHub SSH credentials, and Windows Docker Desktop to achieve an automated CI/CD pipeline that builds and deploys a Docker image on every Git push."
canonical_url: "https://blog.markkulab.net/en/post/docker-jenkins-build-docker-image-and-github-integration-20230703"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/en/author/mark-ku"
site: "Mark Ku's Tech Notes"
date_published: "2023-07-03 01:01:35 +0800"
category: "DevOps"
tags: ["jenkins", "docker", "github", "cicd", "ssh", "devops", "pipeline", "automation"]
language: "en"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "when reusing or quoting, credit the author and link back to the original"
---

# Integrating Windows Docker Desktop, Jenkins in Docker, GitHub SSH Credentials, and Docker Image CI/CD

## Motivation

I previously used [PowerShell to remotely build Docker images on a test machine](https://blog.markkulab.net/remote-build-docker-image-with-powershell/), 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](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/uy3OV4o.png)

### Install the suggested plugins

![Jenkins Customize screen with 'Install suggested plugins' option highlighted](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/IWKJnTV.png)

### Wait for installation to complete, then access Jenkins at `localhost:8080`

![Jenkins Getting Started page with plugin installation options](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/tzP40Nk.png)

### 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](https://github.com/settings/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](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/eKKkFOm.png)

### 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](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/JeIwzXP.png)

### 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](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/HbhNSug.png)

### Enable the GitHub hook trigger

![Jenkins configuration showing GitHub project URL and hook trigger](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/Dpp0ywG.png)

### 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:  'git@github.com: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](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/WaHng5f.png)

### Appendix — Fix the `unix:///var/run/docker.sock: connect: permission denied` error for non-root users on Linux

1. **Create a service file:**
   ```bash
   sudo vim /etc/systemd/system/docker-sock-permissions.service
   ```

2. **Add the following content:**
   ```ini
   [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:**
   ```bash
   sudo systemctl enable docker-sock-permissions.service
   ```

4. **Start the service** (or wait for it to run automatically on next boot):
   ```bash
   sudo systemctl start docker-sock-permissions.service
   ```

## References

[Reference 1](https://medium.com/@nestor.panu/building-an-asp-net-6-application-on-docker-based-jenkins-d6c8aaf94756)
[Reference 2](https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=jenkins%20docker%20github&fenlei=256&oq=jenkins%2520docker&rsv_pq=94ae33af001910d3&rsv_t=b7b8i%2Bsui%2B0AJ%2FMYfQlpp%2FhXCfNXIDDSFm5zUnkMf2BWDdx8uxSiQ184PmM&rqlang=cn&rsv_dl=tb&rsv_enter=1&rsv_btype=t&inputT=2203&rsv_sug3=47&rsv_sug1=11&rsv_sug7=100&rsv_sug2=0&rsv_sug4=2374)

## Memo — Retrieve the admin initial password

```
cat /var/jenkins_home/secrets/initialAdminPassword
```

---

## About this article and its author

Originally published on [Mark Ku's Tech Notes](https://blog.markkulab.net/en/post/docker-jenkins-build-docker-image-and-github-integration-20230703)

License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — when reusing or quoting, credit the author and link back to the original

### About the author

**[Mark Ku](https://blog.markkulab.net/en/author/mark-ku)** — Software Solution Provider

- 10+ years senior software engineer, now an AI Builder
- Focused on large-platform architecture — North-American e-commerce, AI SaaS subscription billing
- Combining AI Agents and automation to build evolvable product foundations

### Free tools built by the author

All of these are free to use:

- [Free PDF Sign Tool](https://blog.markkulab.net/en/tools/pdf-sign): Online PDF sign tool — draw, type, or upload a signature, then drag, resize, and download. Everything runs in your browser; nothing is uploaded.
- [VS Code Refactory](https://blog.markkulab.net/en/tools/refactory): Refactory is a VS Code refactoring extension: 34 actions plus a 37-rule code-smell inspection layer with a Code Health dashboard, across 18 languages, backed by 534 tests. It learns your repo's conventions: where interfaces live, where DI is registered, whether 'use client' belongs. It ranks files by git churn × complexity so you know what to fix first, and hands any smell to the Claude Code already on your machine. Free to use, and your source never leaves your computer.
- [DB-Kit Database Manager](https://blog.markkulab.net/en/tools/db-kit): DB-Kit is a lightweight, cross-platform database manager built with Tauri + Rust + React. Manage MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite, MongoDB, Redis, Kafka, Elasticsearch and RabbitMQ from one consistent interface: passwords encrypted in the OS keychain, SSH tunnels, full CRUD, a visual query builder, stacked multi-statement result sets, cross-connection data transfer and compare/sync, Excel / CSV import & export, visualized execution plans, ER diagrams, scheduled backups, SQL stress testing with p50–p99 latency percentiles, a 15-rule SQL review engine, Kafka message browsing with monitoring & alerts, a bilingual UI (Traditional Chinese / English), a built-in AI assistant (natural-language SQL, AI review and tuning advice) and the dbk CLI. Free and open source (MIT), with installers for Windows, macOS and Linux.
- [VS Code Super Mermaid](https://blog.markkulab.net/en/tools/super-mermaid): Super Mermaid is a VS Code extension for beautiful Mermaid diagrams out of the box: auto-colored live preview, mouse pan & zoom, high-res PNG / SVG export, 21 templates and multiple themes. Free and open source (MIT).
- [React Super Mermaid](https://blog.markkulab.net/en/tools/react-super-mermaid): react-super-mermaid is an open-source React component library: render beautiful Mermaid diagrams with a single <MermaidViewer>, with built-in colorful / sketch themes, pan & zoom, in-diagram search, and high-res SVG / PNG export. Lightweight, SSR-safe, fully typed. Free and open source (MIT).
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/en/tools/jira-super-mermaid): An Atlassian Forge app: write Mermaid syntax directly inside a Jira issue or a Confluence page and get flowcharts, sequence diagrams, state machines and Gantt charts. 11 diagram types, SVG / PNG export, light and dark themes, full CJK support. Runs on Atlassian: your diagrams live in your own site and the app calls no third-party service. Free, coming soon to the Atlassian Marketplace.
- [Mermaid Live Preview](https://blog.markkulab.net/en/tools/mermaid-preview): Write Mermaid in your browser, see it render instantly, and share the whole diagram as a single link. No sign-up, nothing uploaded to a server, and mermaid.live share links work as-is.
- [React Intl Phone Number](https://blog.markkulab.net/en/tools/react-intl-phone-number): react-intl-phone-number is an open-source React component: framework-agnostic and antd-free, with E.164 in/out, a searchable flag / country-code dropdown, configurable validation levels (strict / mobile-strict / loose), themeable CSS, and i18n — phone logic powered by google-libphonenumber. Lightweight and fully typed. Free and open source (MIT).
- [Uptime Kuma Cluster](https://blog.markkulab.net/en/tools/uptime-kuma-cluster): Turn single-node Uptime Kuma into a highly available cluster: OpenResty + Lua smart load balancing, shared MariaDB state, health checks and automatic failover, plus cluster-management REST APIs. One Docker Compose command to start. Free and open source (MIT).
- [Special Education](https://blog.markkulab.net/en/education): Learning materials crafted for special education students

### Daily podcasts

- [Mark's Tech Insights — Daily AI News](https://blog.markkulab.net/en/category/tech-news): Daily curated AI and tech trends. Catch the latest developments via audio summaries — covering AI applications, software architecture, DevOps, and engineering practice. — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/en/category/ai-stock-chat): Every trading day, an AI-analyzed take on the Taiwan stock market, delivered as a two-host conversation covering the session and the next-day outlook. — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml
- [開源好物週報](https://blog.markkulab.net/en/category/open-source-weekly): A weekly two-host pick of free open-source tools surfaced from real Hacker News, GitHub, and Reddit buzz — what pain they solve and the fastest way to get started. — RSS: https://blog.markkulab.net/open-source-weekly/feed.xml

### Newsletter

[Subscribe to the newsletter](https://blog.markkulab.net/en/subscribe) — Be the first to know about new posts. No spam, unsubscribe anytime.
