How to Configure Jenkins Pipelines and Jenkinsfile on RHEL 7

Jenkins Pipelines transform your CI/CD process from a series of fragile, click-configured jobs into version-controlled, repeatable automation defined entirely in code. A Jenkinsfile lives alongside your application source, meaning every change to the build process goes through the same review workflow as application code. This guide covers the Pipeline plugin, both Declarative and Scripted syntax, practical stage definitions, environment variables, credentials management, stash/unstash, agent directives, and post-build actions — everything you need to write production-quality pipelines on RHEL 7.

Prerequisites

  • Jenkins installed and running on RHEL 7 (see the companion guide “How to Install Jenkins on RHEL 7”)
  • The Pipeline plugin installed (included with “Suggested plugins” during setup)
  • The Credentials Binding plugin installed (also included in suggested plugins)
  • A Git repository containing your application source (local Gitea, GitLab, or GitHub)
  • At least one Jenkins agent or the built-in node configured with a JDK and build tools

Step 1: Understand the Pipeline Plugin

The Pipeline plugin adds a new type of Jenkins job — the Pipeline job — that reads build instructions from a Jenkinsfile. You can store the Jenkinsfile directly in the Jenkins UI (useful for prototyping) or, preferably, commit it to the root of your source repository so Jenkins retrieves it automatically on each build. To confirm the plugin is installed, navigate to Manage Jenkins > Manage Plugins > Installed and search for “Pipeline”. You should see Pipeline, Pipeline: Declarative, and Pipeline: Groovy all listed as installed.

Step 2: Declarative vs Scripted Pipeline Syntax

Jenkins supports two pipeline syntaxes. Declarative pipeline uses a structured, opinionated DSL that enforces a specific top-level format and is the recommended choice for most teams. Scripted pipeline is the older form — it is pure Groovy and offers maximum flexibility at the cost of more boilerplate.

A minimal Declarative pipeline looks like this:

pipeline {
    agent any
    stages {
        stage('Hello') {
            steps {
                echo 'Hello, Pipeline!'
            }
        }
    }
}

The equivalent in Scripted syntax:

node {
    stage('Hello') {
        echo 'Hello, Pipeline!'
    }
}

This guide focuses on Declarative syntax because it is more readable, supports post actions natively, and is easier to validate with the built-in linter.

Step 3: Create Your First Pipeline Job

In the Jenkins web UI, click New Item, enter a name such as my-app-pipeline, select Pipeline, and click OK. Scroll down to the Pipeline section. For a quick test, select Pipeline script and paste in the minimal example above, then click Save and Build Now. Once confirmed working, switch the definition to Pipeline script from SCM so Jenkins reads the Jenkinsfile from your repository.

Step 4: Define Stages — Checkout, Build, Test, Deploy

A real application pipeline typically progresses through four stages. The following Jenkinsfile example assumes a Java Maven project but the structure applies to any language:

pipeline {
    agent any

    tools {
        maven 'Maven-3.9'
        jdk   'Java-11'
    }

    stages {

        stage('Checkout') {
            steps {
                // Jenkins checks out the SCM configured in the job definition,
                // or you can be explicit:
                git branch: 'main',
                    url: 'https://gitlab.example.com/myteam/my-app.git',
                    credentialsId: 'gitlab-credentials'
            }
        }

        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }

        stage('Test') {
            steps {
                sh 'mvn test'
            }
            post {
                always {
                    junit '**/target/surefire-reports/*.xml'
                }
            }
        }

        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                echo 'Deploying to production...'
                sh './deploy.sh'
            }
        }

    }
}

The tools block references tool installations configured in Manage Jenkins > Global Tool Configuration. The when directive in the Deploy stage restricts execution to the main branch, preventing accidental deployments from feature branches.

Step 5: Use Environment Variables

The environment block defines variables available to all stages in the pipeline. You can define them globally at the top level or locally within a single stage:

pipeline {
    agent any

    environment {
        APP_NAME    = 'my-app'
        APP_VERSION = sh(script: 'cat VERSION', returnStdout: true).trim()
        DEPLOY_ENV  = 'production'
    }

    stages {
        stage('Build') {
            environment {
                BUILD_OPTS = '-DskipTests -Denv=prod'
            }
            steps {
                sh "mvn clean package ${BUILD_OPTS}"
                sh "echo Building ${APP_NAME} version ${APP_VERSION}"
            }
        }
    }
}

Jenkins also exposes a set of built-in environment variables you can reference anywhere in the pipeline:

BUILD_NUMBER     // e.g. 42
BUILD_URL        // full URL to this build
GIT_COMMIT       // SHA of checked-out commit
GIT_BRANCH       // branch name
WORKSPACE        // absolute path to workspace directory
JOB_NAME         // name of the Jenkins job

Step 6: Bind Credentials with withCredentials

Never hard-code passwords, API tokens, or SSH keys in a Jenkinsfile. Store secrets in Manage Jenkins > Credentials and inject them at runtime with the withCredentials step. The credentials are masked in the build console output.

First, add a credential in the Jenkins UI:

  1. Go to Manage Jenkins > Credentials > System > Global credentials > Add Credentials
  2. Choose the kind: Username with password, Secret text, or SSH Username with private key
  3. Set the ID field (e.g. docker-registry-creds) — this is what the pipeline references

Then inject it in the pipeline:

stage('Push Docker Image') {
    steps {
        withCredentials([usernamePassword(
            credentialsId: 'docker-registry-creds',
            usernameVariable: 'DOCKER_USER',
            passwordVariable: 'DOCKER_PASS'
        )]) {
            sh '''
                echo "$DOCKER_PASS" | docker login registry.example.com 
                    --username "$DOCKER_USER" --password-stdin
                docker push registry.example.com/my-app:latest
            '''
        }
    }
}

For a simple secret text (e.g. an API key):

withCredentials([string(credentialsId: 'deploy-api-key', variable: 'API_KEY')]) {
    sh 'curl -H "Authorization: Bearer $API_KEY" https://api.example.com/deploy'
}

Step 7: Stash and Unstash Artifacts Between Stages

When a pipeline runs across multiple agents, files from an earlier stage are not automatically available on a later agent. Use stash to save files from one stage and unstash to restore them on another:

stage('Build') {
    agent { label 'build-agent' }
    steps {
        sh 'mvn clean package -DskipTests'
        stash name: 'app-jar', includes: 'target/*.jar'
    }
}

stage('Deploy') {
    agent { label 'deploy-agent' }
    steps {
        unstash 'app-jar'
        sh 'cp target/*.jar /opt/myapp/'
    }
}

Stash/unstash works for any file set including configuration files, compiled binaries, and test reports. Stashed data is stored on the Jenkins controller and cleaned up after the build completes.

Step 8: Configure the Agent Directive

The agent directive tells Jenkins where to run the pipeline or individual stages. Common options:

// Run anywhere
agent any

// Run on a specific labelled agent
agent { label 'rhel7-builder' }

// Run in a Docker container (requires Docker Pipeline plugin)
agent {
    docker {
        image 'maven:3.9-eclipse-temurin-11'
        args  '-v /root/.m2:/root/.m2'
    }
}

// No global agent — each stage must declare its own
agent none

To label an agent, go to Manage Jenkins > Manage Nodes and Clouds, click the node, and enter a label in the Labels field. The pipeline will then route work to that specific machine.

Step 9: Post Actions — always, success, failure

The post block runs after a pipeline or stage completes. Conditions include always (runs regardless of result), success, failure, unstable, changed (result differs from last build), and cleanup (runs after all other post conditions):

pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }

    post {
        always {
            junit '**/target/surefire-reports/*.xml'
            archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
            cleanWs()
        }
        success {
            slackSend channel: '#builds',
                      color:   'good',
                      message: "Build #${env.BUILD_NUMBER} passed: ${env.BUILD_URL}"
        }
        failure {
            emailext subject: "BUILD FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                     body:    "See details at ${env.BUILD_URL}",
                     to:      '[email protected]'
        }
    }
}

cleanWs() (from the Workspace Cleanup plugin) deletes the workspace after every build, keeping disk usage under control on long-running Jenkins servers.

Step 10: Validate a Jenkinsfile with the Pipeline Linter

Jenkins ships with a built-in Pipeline linter accessible at http://<jenkins-url>/pipeline-model-converter/validate. You can also validate from the command line using the Jenkins CLI:

ssh -p 50000 [email protected] declarative-linter < Jenkinsfile

Or use the REST API with curl:

JENKINS_URL="http://jenkins.example.com"
JENKINS_USER="admin"
JENKINS_TOKEN="your-api-token"

curl --user "${JENKINS_USER}:${JENKINS_TOKEN}" 
     -X POST 
     -F "jenkinsfile=<Jenkinsfile" 
     "${JENKINS_URL}/pipeline-model-converter/validate"

A valid Jenkinsfile returns Jenkinsfile successfully validated.. Catching syntax errors before committing saves significant debugging time.

Conclusion

Jenkins Pipelines and the Jenkinsfile DSL give development teams a repeatable, auditable CI/CD process that lives in source control alongside the application it builds. You have now seen how to structure Declarative pipeline stages for checkout, build, test, and deploy; how to securely inject credentials with withCredentials; how to pass artifacts between agents with stash/unstash; and how to respond to build outcomes with post-action blocks. With these building blocks, you can model virtually any delivery workflow, from simple unit test runs to multi-environment blue-green deployments.