Jenkins Pipeline (formerly Workflow) allows defining the entire CI/CD build, test, and deploy process as code in a Jenkinsfile committed to the source repository alongside the application code. This “Pipeline as Code” approach means the build process is version-controlled, reviewable via pull requests, and reproducible across environments. Jenkins supports two Pipeline syntaxes: Declarative Pipeline (structured, opinionated format recommended for most use cases) and Scripted Pipeline (Groovy-based, flexible for complex logic). A Declarative Pipeline describes the pipeline in a series of stages with explicit steps, environment variables, and conditions. This guide covers writing Jenkinsfiles for Node.js and Docker-based projects on RHEL 9, using Jenkins agents, and managing build credentials securely.

Prerequisites

  • Jenkins LTS installed on RHEL 9
  • Jenkins plugins: Pipeline, Git, Docker Pipeline (install via Manage Jenkins → Plugins)

Step 1 — Basic Declarative Jenkinsfile

# Jenkinsfile — place in the root of the Git repository
pipeline {
    agent any  # Run on any available Jenkins agent

    environment {
        APP_NAME = 'myapp'
        NODE_ENV = 'test'
    }

    stages {
        stage('Checkout') {
            steps {
                git branch: 'main',
                    url: 'https://github.com/myorg/myapp.git'
            }
        }

        stage('Install') {
            steps {
                sh 'npm ci'
            }
        }

        stage('Test') {
            steps {
                sh 'npm test -- --reporter=junit'
            }
            post {
                always {
                    junit 'test-results/*.xml'  # Publish test results
                }
            }
        }

        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }

        stage('Deploy') {
            when {
                branch 'main'  # Only deploy from main branch
            }
            steps {
                sh 'rsync -av dist/ deploy@production-server:/var/www/html/'
            }
        }
    }

    post {
        failure {
            emailext to: '[email protected]',
                     subject: "BUILD FAILED: ${currentBuild.fullDisplayName}",
                     body: "Check console output at ${env.BUILD_URL}"
        }
    }
}

Step 2 — Docker Build and Push Pipeline

# Jenkinsfile for Docker image build and push
pipeline {
    agent any

    environment {
        REGISTRY = 'registry.example.com'
        IMAGE    = "${REGISTRY}/myapp"
        TAG      = "${env.BUILD_NUMBER}-${env.GIT_COMMIT[0..6]}"
    }

    stages {
        stage('Build Image') {
            steps {
                script {
                    docker.build("${IMAGE}:${TAG}")
                }
            }
        }

        stage('Push Image') {
            steps {
                script {
                    // 'registry-credentials' is a Jenkins credential ID
                    docker.withRegistry("https://${REGISTRY}", 'registry-credentials') {
                        docker.image("${IMAGE}:${TAG}").push()
                        docker.image("${IMAGE}:${TAG}").push('latest')
                    }
                }
            }
        }

        stage('Deploy to Kubernetes') {
            steps {
                withKubeConfig([credentialsId: 'k8s-admin']) {
                    sh "kubectl set image deployment/myapp myapp=${IMAGE}:${TAG} -n production"
                    sh "kubectl rollout status deployment/myapp -n production"
                }
            }
        }
    }
}

Step 3 — Store and Use Credentials

# In the Jenkinsfile, access credentials via withCredentials():
stage('Deploy') {
    steps {
        withCredentials([
            usernamePassword(
                credentialsId: 'deploy-ssh-credentials',
                usernameVariable: 'SSH_USER',
                passwordVariable: 'SSH_PASS'
            )
        ]) {
            sh 'sshpass -p "$SSH_PASS" rsync -av dist/ $SSH_USER@server:/var/www/'
        }
    }
}

Conclusion

Jenkins Declarative Pipelines on RHEL 9 provide a readable, version-controlled CI/CD workflow. The most impactful Pipeline practices are: always use the when { branch 'main' } directive to prevent accidental deployments from feature branches; store all credentials in the Jenkins Credentials Manager (never hardcode them in Jenkinsfiles); and use the post { always {} } block to ensure test result archiving and notifications happen even when the build fails. The BUILD_NUMBER + short Git commit hash as the Docker image tag provides a unique, traceable identifier for every built image.

Next steps: How to Install Jenkins on RHEL 9, How to Install GitLab CE on RHEL 9, and How to Register a GitHub Actions Runner on RHEL 9.