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.

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.
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.
A Shared Library has three standard directories you must understand:
my-shared-library/
├── vars/
│ ├── buildDockerApp.groovy
│ └── notifyTeam.groovy
├── src/
│ └── com/company/
│ └── Utils.groovy
└── resources/
└── k8s/
└── deployment-template.yamlvars/ — 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 StepsThe 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:
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 UtilitiesFor heavier logic — parsing, version calculations, repeated string operations — use the src directory with packaged Groovy classes:
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:
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 FilesThe 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:
def template = libraryResource('k8s/deployment-template.yaml')
writeFile file: 'deployment.yaml', text: template
sh 'kubectl apply -f deployment.yaml'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:
@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.
Some habits production teams use to keep a library healthy:
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.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:
vars becomes the global step name; the call method is its implementation.src holds packaged Groovy classes for complex logic; resources holds static files.@Library annotation with a tag for locked versions, or a branch name for the latest development.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!