Complete Jenkins Tutorial with Usage Examples

Table of Contents

1. What is Jenkins?

Jenkins is an open-source automation server that helps automate parts of the software development process, including building, testing, and deploying applications. It is widely used for implementing Continuous Integration (CI) and Continuous Delivery (CD) workflows, often referred to as CI/CD pipelines.

Jenkins makes it easier for developers to integrate code changes frequently and automatically test them, reducing integration problems and allowing for faster delivery of high-quality software.

Key Benefits:

2. Installation

Jenkins requires Java (OpenJDK 11 or 17 recommended). The installation process varies slightly depending on your operating system. Below are common methods:

A. Recommended: Docker

This is often the quickest and cleanest way to get Jenkins running for development or testing.

# Pull the Jenkins LTS image
docker pull jenkins/jenkins:lts

# Create a Docker volume to persist Jenkins data
docker volume create jenkins_home

# Run Jenkins container on port 8080 (web UI) and 50000 (agent communication)
docker run -d -p 8080:8080 -p 50000:50000 \
  --name jenkins-server \
  --restart=on-failure \
  -v jenkins_home:/var/jenkins_home \
  jenkins/jenkins:lts

After a few minutes, Jenkins will be accessible in your browser at `http://localhost:8080`.

B. Ubuntu/Debian

Install Java first, then add the Jenkins repository and install.

# 1. Install Java Development Kit (JDK)
sudo apt update
sudo apt install -y openjdk-17-jdk

# 2. Add Jenkins repository key
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
  https://pkg.jenkins.io/debian-stable/jenkins-keyring.asc

# 3. Add Jenkins repository to your sources list
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/" | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

# 4. Update apt cache and install Jenkins
sudo apt update
sudo apt install -y jenkins

# 5. Check Jenkins service status (should be running)
sudo systemctl status jenkins
# Output: active (running)

# 6. Adjust firewall (if UFW is active)
sudo ufw allow 8080 # Allow HTTP access to Jenkins
sudo ufw enable
sudo ufw status

C. CentOS/RHEL/Fedora

# 1. Install Java Development Kit (JDK)
sudo dnf install -y java-17-openjdk-devel

# 2. Add Jenkins repository
sudo wget -O /etc/yum.repos.d/jenkins.repo \
    https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key

# 3. Install Jenkins
sudo dnf install -y jenkins

# 4. Start and enable Jenkins service
sudo systemctl start jenkins
sudo systemctl enable jenkins

# 5. Adjust firewall (if Firewalld is active)
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --reload
After installation, Jenkins will be accessible at `http://:8080`.

3. Jenkins UI Basics

When you first access Jenkins, you'll go through a setup wizard.

A. Unlock Jenkins:

The first screen requires an administrator password. Retrieve it from your server's initial admin password file.

# For Docker:
docker logs jenkins-server # Look for the password in the logs

# For Linux installations:
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Paste the password and click **Continue**.

B. Install Plugins:

Choose "Install suggested plugins." This will install common plugins for SCM (Git), Pipelines, build tools, etc.

C. Create First Admin User:

Create your Jenkins administrator account. Remember these credentials!

D. Jenkins Dashboard:

After setup, you'll land on the Jenkins dashboard. Key elements:

4. Freestyle Projects

Freestyle projects are the simplest job type in Jenkins, configured entirely through the graphical user interface. They are great for straightforward tasks like compiling code, running simple scripts, or executing basic tests.

A. Create a Freestyle Project:

  1. From the Jenkins dashboard, click **New Item** in the left menu.
  2. Enter an **Item name** (e.g., `MyFirstFreestyleJob`).
  3. Select **Freestyle project**.
  4. Click **OK**.

B. Configure the Project:

You'll be directed to the project configuration page.

  1. General:
    • Add a **Description** (e.g., "A simple job to print a message.").
    • (Optional) **Discard old builds:** Configure build retention.
  2. Source Code Management (SCM):
    • Select `None` for a simple script, or `Git` if you want to pull code from a repository.
    • If Git: Provide **Repository URL** (e.g., `https://github.com/jenkins-docs/simple-java-maven-app.git`) and **Credentials** (if private repo).
    • **Branches to build:** (e.g., `main` or `*/master`).
  3. Build Triggers: Define when the job should run.
    • **Build periodically:** Schedule builds (e.g., `H/15 * * * *` for every 15 minutes).
    • **Poll SCM:** Check the SCM for changes at intervals (e.g., `H/5 * * * *` for every 5 minutes).
    • **GitHub hook trigger for GITScm polling:** Trigger builds when changes are pushed to GitHub (requires webhook setup in GitHub).
  4. Build Steps: Define the actions to perform.
    • Click **Add build step**.
    • Select **Execute shell** (for Linux/macOS agent) or **Execute Windows batch command** (for Windows agent).
    • Paste your commands:
      # For Linux/macOS
      echo "Hello from Jenkins Freestyle Job!"
      echo "Current directory: $(pwd)"
      ls -lh
      # For Windows
      echo Hello from Jenkins Freestyle Job!
      echo Current directory: %CD%
      dir
    • (For Java/Maven/Gradle projects, you'd choose "Invoke Maven top-level targets" or "Invoke Gradle script").
  5. Post-build Actions: Actions after the build completes.
    • Click **Add post-build action**.
    • **Archive the artifacts:** Save build outputs (e.g., `target/*.jar`).
    • **E-mail Notification:** Send notifications based on build status.
  6. Click **Save**.

C. Run the Build:

From the project dashboard, click **Build Now** in the left menu.

D. View Results:

5. Jenkins Pipelines (Declarative & Scripted)

Jenkins Pipelines are a suite of plugins that support implementing and integrating continuous delivery pipelines into Jenkins. "Pipeline as Code" allows you to define your entire CI/CD process in a `Jenkinsfile` (a Groovy script) stored in your source code repository.

This provides version control for your pipeline, making it auditable, reproducible, and easily shared.

A. Declarative Pipeline:

A more recent and structured syntax, easier to learn and read. It's defined within a `pipeline { ... }` block using predefined sections like `agent`, `stages`, `steps`, `post`, `options`, `parameters`, `environment`, `buildTriggers`.

B. Scripted Pipeline:

The original Pipeline syntax, based entirely on Groovy. Offers greater flexibility and control for complex or dynamic workflows, but requires more Groovy programming knowledge.

C. Example: Simple Declarative Pipeline (`Jenkinsfile`):

Create a file named `Jenkinsfile` at the root of your Git repository.

// Jenkinsfile (Declarative Pipeline)
pipeline {
    // Defines where the entire pipeline will run. 'any' means on any available agent.
    agent any 

    // Define environment variables accessible throughout the pipeline
    environment {
        // Build Number (built-in Jenkins variable)
        BUILD_ID = "${env.BUILD_NUMBER}" 
        // Custom variable
        APP_NAME = "MyWebApp"
    }

    // Define parameters that can be passed to the pipeline
    parameters {
        string(name: 'BRANCH_NAME', defaultValue: 'main', description: 'Git branch to build')
        booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Whether to run tests')
    }

    // Options for the pipeline
    options {
        // Discard old builds: keep a max of 5 builds and 3 days of artifacts
        buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '3')) 
        timestamps() // Add timestamps to console output
    }

    stages {
        stage('Checkout Code') { // First stage: Get code from SCM
            steps {
                echo "Checking out code for ${params.BRANCH_NAME}..."
                // Use a Git SCM checkout, assuming 'github.com/your-org/your-repo.git'
                // You'll need to configure Git credentials in Jenkins if your repo is private
                git branch: "${params.BRANCH_NAME}", url: 'https://github.com/your-org/your-repo.git'
            }
        }

        stage('Build') { // Second stage: Build the application (e.g., a Maven project)
            steps {
                echo "Building ${APP_NAME} build ${BUILD_ID}..."
                // This assumes Maven is configured in Jenkins under "Manage Jenkins > Tools"
                // and a pom.xml exists in your repo
                sh 'mvn clean install -DskipTests' 
            }
            // Post-build actions specific to this stage (e.g., if build fails)
            post {
                always {
                    archiveArtifacts artifacts: 'target/*.jar', fingerprint: true // Archive JAR file
                }
                failure {
                    echo 'Build failed! Check console output.'
                }
            }
        }

        stage('Test') { // Third stage: Run tests (conditional execution)
            when {
                expression { params.RUN_TESTS == true } // Only run if RUN_TESTS parameter is true
            }
            steps {
                echo "Running tests..."
                sh 'mvn test' // Run Maven tests
                // Publish JUnit test results (requires JUnit plugin)
                junit '**/target/surefire-reports/*.xml' 
            }
            post {
                unstable {
                    echo 'Tests had failures or skipped tests!'
                }
                failure {
                    echo 'Tests failed!'
                }
            }
        }

        stage('Deploy Staging') { // Fourth stage: Deploy to a staging environment
            steps {
                echo "Deploying to staging environment..."
                // Example: Copy artifact to a remote server via SSH (requires SSH Agent plugin and credentials)
                // sshagent(['your-ssh-credential-id']) {
                //     sh "scp target/*.jar user@staging-server:/var/www/html/app.jar"
                // }
                sh 'echo "Deployment to staging simulated."'
            }
        }

        stage('Approval for Production') { // Optional stage for manual approval
            input {
                message "Proceed to production deployment?"
                ok "Deploy to Production"
                submitter "admin,devops" // Specific users or groups who can approve
            }
            steps {
                echo "Approval received. Proceeding to production."
            }
        }

        stage('Deploy Production') { // Final stage: Deploy to production (conditional on approval)
            when {
                expression { previousStageStatus('Approval for Production') == 'SUCCESS' } // Only if approval stage passed
            }
            steps {
                echo "Deploying to production environment..."
                sh 'echo "Deployment to production simulated."'
            }
            post {
                success {
                    echo 'Deployment to Production successful!'
                    // Send email notification (requires Email Extension Plugin)
                    mail to: 'devops-team@example.com', \
                         subject: "Jenkins Pipeline: ${APP_NAME} Build ${BUILD_ID} - Production Deployment SUCCESS", \
                         body: "Build ${BUILD_ID} of ${APP_NAME} successfully deployed to production."
                }
                failure {
                    echo 'Deployment to Production FAILED!'
                    mail to: 'devops-team@example.com', \
                         subject: "Jenkins Pipeline: ${APP_NAME} Build ${BUILD_ID} - Production Deployment FAILED", \
                         body: "Build ${BUILD_ID} of ${APP_NAME} FAILED deployment to production. Check: ${env.BUILD_URL}"
                }
            }
        }
    }
    // Post-build actions for the entire pipeline
    post {
        always {
            echo 'Pipeline finished!'
        }
        cleanedUp {
            echo 'Cleaning up workspace...'
            // Clean workspace (requires Workspace Cleanup Plugin)
            deleteDir() 
        }
    }
}

D. Create a Pipeline Job in Jenkins:

  1. From the Jenkins dashboard, click **New Item**.
  2. Enter an **Item name** (e.g., `MyDeclarativePipeline`).
  3. Select **Pipeline**.
  4. Click **OK**.
  5. On the configuration page, scroll down to the **Pipeline** section.
  6. Definition: Choose `Pipeline script from SCM`.
  7. SCM: Select `Git`.
  8. Repository URL: Paste your Git repository URL (e.g., `https://github.com/your-org/your-repo.git`).
  9. Credentials: Select your Git credentials (if the repo is private).
  10. Branches to build: (e.g., `*/main`).
  11. Script Path: Enter `Jenkinsfile` (assuming your file is named that and in the root of your repo).
  12. Click **Save**.

E. Run the Pipeline:

From the project dashboard, click **Build Now**. You'll see the stages execute in the "Stage View" or "Blue Ocean" interface (if installed).

6. Multibranch Pipelines

A Multibranch Pipeline project automatically discovers, manages, and executes Pipelines for branches containing a `Jenkinsfile` within a repository. It's ideal for projects with multiple development branches (feature branches, hotfix branches, etc.).

A. Create a Multibranch Pipeline:

  1. From the Jenkins dashboard, click **New Item**.
  2. Enter an **Item name** (e.g., `MyMultiBranchApp`).
  3. Select **Multibranch Pipeline**.
  4. Click **OK**.

B. Configure the Project:

  1. Branch Sources:
    • Click **Add source** > **Git**.
    • Project Repository: Provide your Git repository URL.
    • Credentials: Select (if private repo).
    • (Optional) Configure "Discover branches" or "Discover pull requests" strategies.
  2. Build Configuration:
    • Mode: `By Jenkinsfile`.
    • Script Path: Enter `Jenkinsfile` (default).
  3. Click **Save**.

C. Usage:

Each branch can have a slightly different `Jenkinsfile` allowing for customized pipelines based on the branch's purpose (e.g., a `dev` branch might have extra static analysis steps).

7. Jenkins Plugins

Plugins are the backbone of Jenkins' extensibility, allowing it to integrate with almost any build tool, SCM, cloud provider, or reporting system.

A. Manage Plugins:

  1. From the Jenkins dashboard, go to **Manage Jenkins** > **Plugins**.
  2. Updates: Shows available updates for installed plugins.
  3. Available plugins: Search and select plugins you want to install. Click **Install without restart** or **Download now and install after restart**.
  4. Installed plugins: View currently installed plugins, enable/disable, uninstall.

B. Essential Plugins for CI/CD:

# Example: Installing a plugin from the CLI (if allowed by Jenkins security)
java -jar jenkins-cli.jar -s http://localhost:8080/ -webcli install-plugin blueocean

8. Agents and Distributed Builds

Jenkins allows you to offload build workloads from the main Jenkins controller to separate machines called "agents" (formerly "slaves"). This is crucial for scalability, isolating build environments, and performing builds on different operating systems.

A. Jenkins Architecture:

B. Add a Jenkins Agent (GUI - via SSH):

  1. Prepare Agent Machine:
    • Install Java (matching the controller's version or compatible).
    • Ensure SSH server is running.
    • Create a dedicated user for Jenkins (e.g., `jenkins_agent`).
  2. Create an SSH Key Pair for Jenkins (on the controller or locally):
    ssh-keygen -t rsa -b 4096 -C "jenkins-agent-key" -f ~/.ssh/jenkins-agent-key -q -N ""

    Copy the public key (`~/.ssh/jenkins-agent-key.pub`) to the agent machine's `~/.ssh/authorized_keys` file for the `jenkins_agent` user.

  3. Add SSH Credentials to Jenkins:
    • From Jenkins dashboard, go to **Manage Jenkins** > **Credentials** > **System** > **Global credentials (unrestricted)**.
    • Click **+ Add Credentials**.
    • Kind: `SSH Username with private key`.
    • Scope: `Global`.
    • ID: (e.g., `jenkins-agent-ssh-key`).
    • Username: `jenkins_agent` (or your agent user).
    • Private Key: Select `Enter directly` and paste the content of your `~/.ssh/jenkins-agent-key` (the private key).
    • Click **Create**.
  4. Configure New Node (Agent) in Jenkins:
    • From Jenkins dashboard, go to **Manage Jenkins** > **Nodes**.
    • Click **New Node** in the left menu.
    • Node name: (e.g., `my-linux-agent`).
    • Select **Permanent Agent**. Click **Create**.
    • Configure Node Details:
      • Name: `my-linux-agent`.
      • Description: (Optional).
      • # of executors: (e.g., `2` - number of parallel jobs this agent can run).
      • Remote root directory: `/home/jenkins_agent/workspace` (or `/var/jenkins_home/workspace`).
      • Labels: (e.g., `linux build-server`). Used to target jobs to specific agents.
      • Launch method: `Launch agents via SSH`.
      • Host: IP address or hostname of your agent machine.
      • Credentials: Select the SSH credential ID you created (e.g., `jenkins-agent-ssh-key`).
      • Host Key Verification Strategy: For testing, choose `Non verifying Verification Strategy`. For production, use `Known hosts file` or `Manually provided key`.
      • Click **Save**.
  5. Verify Agent Connection: The agent should attempt to connect and its status should turn green (online).

C. Assigning Jobs to Agents:

9. Credentials Management

Jenkins securely stores various types of credentials (usernames/passwords, SSH keys, secret text, Kubernetes secrets) to prevent them from being hardcoded in jobs or scripts.

A. Add Credentials:

  1. From Jenkins dashboard, go to **Manage Jenkins** > **Credentials** > **System** > **Global credentials (unrestricted)**.
  2. Click **+ Add Credentials**.
  3. Kind: Select the type (e.g., `Username with password`, `Secret text`, `SSH Username with private key`).
  4. Fill in details (ID, description, actual credential data).
  5. Click **Create**.

B. Using Credentials in Pipelines:

The `withCredentials` step is used to access credentials securely within a Pipeline.

pipeline {
    agent any
    stages {
        stage('Login to Docker Registry') {
            steps {
                // Assuming you have a "Username with password" credential with ID 'docker-hub-creds'
                withCredentials([usernamePassword(credentialsId: 'docker-hub-creds', passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) {
                    sh "docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} registry.hub.docker.com"
                }
            }
        }
        stage('Deploy via SSH') {
            steps {
                // Assuming you have an "SSH Username with private key" credential with ID 'deploy-ssh-key'
                sshagent(['deploy-ssh-key']) {
                    sh "scp -o StrictHostKeyChecking=no target/my-app.jar user@remoteserver:/var/www/html/"
                }
            }
        }
        stage('Use a Secret Text') {
            steps {
                // Assuming you have a "Secret text" credential with ID 'my-api-key'
                withCredentials([string(credentialsId: 'my-api-key', variable: 'API_KEY')]) {
                    sh "curl -H \"Authorization: Bearer ${API_KEY}\" https://api.example.com/data"
                }
            }
        }
    }
}

10. Environment Variables

Environment variables allow you to pass dynamic values and configurations to your Jenkins jobs and pipelines.

A. Built-in Environment Variables:

Jenkins provides many predefined variables automatically (e.g., `BUILD_NUMBER`, `JOB_NAME`, `WORKSPACE`, `BUILD_URL`).

pipeline {
    agent any
    stages {
        stage('Show Env Vars') {
            steps {
                echo "Build Number: ${env.BUILD_NUMBER}"
                echo "Job Name: ${env.JOB_NAME}"
                echo "Workspace: ${env.WORKSPACE}"
                echo "Build URL: ${env.BUILD_URL}"
                sh 'printenv' // Prints all environment variables in shell
            }
        }
    }
}

B. Global Environment Variables:

Accessible across all jobs and pipelines within a Jenkins instance.

  1. From Jenkins dashboard, go to **Manage Jenkins** > **System**.
  2. Scroll down to **Global properties** section.
  3. Check "Environment variables".
  4. Click **Add** and define `Name` and `Value` (e.g., `GLOBAL_VAR_MESSAGE`, `This is a global message`).
  5. Click **Save**.
  6. Now `echo "${env.GLOBAL_VAR_MESSAGE}"` will work in any pipeline.

C. Local/Stage-level Environment Variables (in Pipeline):

Defined within an `environment` block in a Declarative Pipeline, scoped to that pipeline or stage.

pipeline {
    agent any
    // Pipeline-level environment variable
    environment {
        APP_VERSION = "1.0.0" 
    }
    stages {
        stage('Stage 1') {
            steps {
                echo "App Version (from pipeline env): ${env.APP_VERSION}"
            }
        }
        stage('Stage 2') {
            // Stage-level environment variable (overrides pipeline-level if name is same)
            environment {
                BUILD_TYPE = "Production" 
            }
            steps {
                echo "App Version: ${env.APP_VERSION}"
                echo "Build Type: ${env.BUILD_TYPE}"
            }
        }
    }
}

11. Shared Libraries

Shared Libraries allow you to define reusable pieces of Pipeline code (e.g., common functions, steps, or utility scripts) in an external Git repository. This promotes code reuse and maintains a clean `Jenkinsfile`.

A. Shared Library Structure (in Git repository):

/my-jenkins-shared-lib
├── vars/
│   └── myCustomStep.groovy
│   └── commonUtils.groovy
├── src/
│   └── com/
│       └── example/
│           └── MyHelper.groovy
├── resources/
│   └── config.json
└── Jenkinsfile (optional, for testing the library itself)

B. Example: `myCustomStep.groovy` in `vars/`

// vars/myCustomStep.groovy
def call(String message = 'Default message') {
    echo "--- Custom Step Started ---"
    echo "Message: ${message}"
    // Perform some common actions, e.g., deploy an artifact, run security scan
    echo "--- Custom Step Finished ---"
}

C. Configure Shared Library in Jenkins:

  1. Push your shared library Git repository (e.g., `https://github.com/your-org/my-jenkins-shared-lib.git`).
  2. From Jenkins dashboard, go to **Manage Jenkins** > **System**.
  3. Scroll down to **Global Pipeline Libraries** (or "Global Shared Libraries").
  4. Click **+ Add**.
    • Name: (e.g., `my-shared-lib`).
    • Default version: `main` (or a specific branch/tag).
    • Retrieval Method: `Git`.
    • Project Repository: Paste the Git URL.
    • Credentials: Select (if private).
  5. Click **Save**.

D. Use Shared Library in Pipeline:

@Library('my-shared-lib') _ // Load the shared library (the '_' is conventional)

pipeline {
    agent any
    stages {
        stage('Example Stage') {
            steps {
                myCustomStep 'Hello from the pipeline!' // Call the global function
                myCustomStep() // Call with default message

                script {
                    // Access a class from src/
                    // def helper = new com.example.MyHelper()
                    // helper.doSomething()
                }
            }
        }
    }
}

12. CI/CD Integration Examples

Jenkins plays a central role in automating CI/CD workflows by integrating with various tools.

A. Git/GitHub/GitLab Integration:

B. Build Tools (Maven, Gradle, npm):

C. Testing Frameworks (JUnit, Jest, Selenium):

D. Containerization (Docker):

E. Deployment Targets (SSH, Kubernetes, Cloud Providers):

13. Jenkins Security

Securing your Jenkins instance is paramount, as it can have access to sensitive credentials and infrastructure.

14. Best Practices

15. Troubleshooting

Automate with Jenkins!

Jenkins is a powerful tool at the heart of modern DevOps practices. By understanding its core functionalities, mastering Pipeline as Code, and leveraging its vast plugin ecosystem, you can build robust, automated CI/CD pipelines that streamline your software delivery process and enhance your team's productivity. Continuous learning and active participation in the Jenkins community will further deepen your expertise.