Learn Jenkins - Code Reusability with Jenkins Shared Libraries
Episode 9 of 21

Learn Jenkins - Code Reusability with Jenkins Shared Libraries

At enterprise scale, copy-pasting Jenkinsfiles into dozens of repositories is a ticking time bomb: fixing one bug must be repeated everywhere and configuration drift is unavoidable. This episode dissects the Jenkins Shared Libraries concept, the vars, src, and resources directory structure, and how to load and use them from a Jenkinsfile.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 8 we built a complete, secure Docker pipeline. Now imagine your company has not one, but thirty application repositories — each with a similar Jenkinsfile: build a Docker image, push to a registry, run tests, send notifications. What happens when those patterns are copy-pasted into every repo?

First, all that duplication becomes alive. A bug in the build pattern has to be fixed 30 times. Second, drift: after a few months, repo A has an added security scan, repo B does not; repo C has already updated to the new approach, the rest still use the old one. Third, review is hard: which one is correct? Most dangerously, a security fix that arrives late in one repo means one repo is still vulnerable.

The analogy is managing invoices: if every office makes its own invoice format and hardcodes the tax rules in each spreadsheet, a change in tax regulations means re-checking dozens of files. The solution is a single centralized template everyone uses. For Jenkins, the answer is Jenkins Shared Libraries: one Git repository that becomes the single source of truth for pipeline logic, and every Jenkinsfile just calls it.

Main Discussion

The Jenkins Shared Libraries Concept

A Shared Library is a separate Groovy repository that Jenkins loads to provide functions, classes, and static resources that all pipelines can use. The Jenkinsfile in each application repo becomes very thin — just stage orchestration, while the technical details (how to build, how to push, how to notify) live in one place.

The effect is similar to a programming language's standard library: you do not rewrite the sorting function in every program, you use the existing one. One change to the library applies immediately to all its users.

Shared Library Directory Structure

A Shared Library has three standard directories you must understand:

Shared library structure
my-shared-library/
├── vars/
   ├── buildDockerApp.groovy
   └── notifyTeam.groovy
├── src/
   └── com/company/
       └── Utils.groovy
└── resources/
    └── k8s/
        └── deployment-template.yaml
  • vars/global functions/steps. Every Groovy file here becomes a custom step callable directly from the Jenkinsfile. The file buildDockerApp.groovy produces the step buildDockerApp.
  • src/standard Groovy classes and utility code. It follows the package structure like com.company.Utils, used for more complex logic.
  • resources/static files: JSON, shell scripts, templates. Retrieved via the libraryResource helper and can be written to the workspace.

vars/: Custom Global Steps

The heart of a shared library is the vars directory. Every file inside defines one global step with the call method. The file name is the step name:

buildDockerApp.groovy in the vars directory
def call(String imageName, String tag = 'latest') {
    sh "docker build -t ${imageName}:${tag} ."
    sh "docker tag ${imageName}:${tag} ${imageName}:latest"
    echo "Image ${imageName}:${tag} berhasil dibangun"
}

The call method above accepts an image name and a tag with a latest default. Once the library is loaded, any pipeline can call it like a built-in Jenkins step: buildDockerApp 'ghcr.io/company/api-service' — note this is exactly like calling the sh or echo step, because Jenkins treats files in vars as global steps.

Tip

Rule of thumb: the more logic lives in vars and the less in the Jenkinsfile, the easier the library is to maintain. An application Jenkinsfile should contain almost no sh — just calls to custom steps and stage orchestration.

src/: Groovy Classes and Utilities

For heavier logic — parsing, version calculations, repeated string operations — use the src directory with packaged Groovy classes:

Utils class in package com.company
package com.company
 
class Utils {
    static String shortSha(String fullSha) {
        return fullSha.take(8)
    }
}

This class can be used from within steps in vars to leverage the same logic in many places:

Using the class from vars
def call(String imageName, String fullSha) {
    def utils = new com.company.Utils()
    def tag = utils.shortSha(fullSha)
    sh "docker build -t ${imageName}:${tag} ."
}

resources/: Static Files

The resources directory stores files that pipelines need without rewriting them in each repo — for example deployment templates, shell scripts, or JSON configuration. Retrieved with the libraryResource helper:

Using a static resource
def template = libraryResource('k8s/deployment-template.yaml')
writeFile file: 'deployment.yaml', text: template
sh 'kubectl apply -f deployment.yaml'

Loading a Shared Library in the Jenkinsfile

The library is registered in Jenkins via Manage Jenkins → System → Global Pipeline Libraries with the repo URL, credentials if private, and a default version. Once registered, the Jenkinsfile loads it with the @Library annotation at the top:

JenkinsLoading and using a shared library
@Library('my-shared-library@main') _
 
pipeline {
    agent any
 
    stages {
        stage('Build') {
            steps {
                buildDockerApp 'ghcr.io/company/api-service'
            }
        }
        stage('Notify') {
            steps {
                notifyTeam 'sukses'
            }
        }
    }
}

The syntax @Library('my-shared-library@main') _ above means: load the library named my-shared-library at the main branch version. The trailing underscore loads all global steps in vars into the top-level scope so they can be called directly. The version does not have to be a branch — it can be a tag: @Library('my-shared-library@1.2.3') _ for a locked, immutable version, or @Library('my-shared-library@main') _ to always follow the latest development.

Warning

A shared library is Groovy code that runs on the controller. Its code is not sandboxed like a normal Jenkinsfile — certain functions require admin approval (Script Approval). Treat the library like production code: version it with semantic versioning, review it through pull requests, and never put secrets or hardcoded credentials inside it.

Best Practices at Enterprise Scale

Some habits production teams use to keep a library healthy:

  • Versioning with tags. Release the library with a 1.2.3 tag and pin the Jenkinsfile to a specific version. The main branch is used for development, but production uses a locked version.
  • Test the library itself. A library that is never tested is a bomb. Create a tester Jenkinsfile in the library repo that calls every step and verifies its output.
  • Keep the Jenkinsfile thin. The Jenkinsfile only describes the stage order; all technical logic lives in the library.
  • Audit who can write to the library. Because the library runs on the controller, restrict write access to the library repo to trusted engineers only.

Conclusion

In episode 9 we solved the code duplication problem at enterprise scale: copy-pasting Jenkinsfiles into dozens of repositories is dangerous because fixes must be repeated everywhere and drift is unavoidable. The solution is Jenkins Shared Libraries — one centralized Groovy repository containing vars for custom global steps with the call method, src for Groovy classes and utilities, and resources for static files retrieved via libraryResource. The library is loaded with @Library('my-shared-library@main') _ and keeps Jenkinsfiles thin and consistent across the whole organization.

The key takeaways to carry with you:

  • A file name in vars becomes the global step name; the call method is its implementation.
  • src holds packaged Groovy classes for complex logic; resources holds static files.
  • The @Library annotation with a tag for locked versions, or a branch name for the latest development.
  • Library code runs on the controller — version it, review it, and restrict its write access.
  • A thin Jenkinsfile + a rich library = fix once, applies to all repos.

You now have a reusable foundation. But every build produces output — and that output must be managed. In episode 10 we discuss build artifacts and workspace management: storing build output with archiveArtifacts, sharing files between agents with stash and unstash, and cleaning the workspace so the disk never fills up. See you in episode 10!