Picture this: It’s a Friday afternoon, and your Jenkins instance, the tireless workhorse of your CI/CD pipeline, suddenly grinds to a halt. Builds are failing, new jobs can’t start, and you’re getting cryptic messages about disk space. You log in, eyes scanning the dashboard for clues, and there it is, glaring at you from the system information: “Disk Usage: 98%”. Panic sets in. Your initial thought? “Oh man, how do I purge build history in Jenkins before the entire system comes crashing down and ruins my weekend?”
The good news is, you’re not alone, and this is a common challenge for many Jenkins administrators. To quickly and precisely answer your question: You can purge build history in Jenkins primarily through three main methods: using the built-in “Discard Old Builds” feature directly within your job configurations, leveraging the Jenkins Script Console for more granular or global control, or, in extreme cases, manually deleting files from the server’s file system (though this last method comes with significant caveats and risks). Each approach offers different levels of control and is suitable for various scenarios, from routine maintenance to emergency clean-up operations.
Let’s dive deep into these methods, exploring their nuances, best practices, and the considerations you should keep in mind to keep your Jenkins instance running smoothly, your disk space optimized, and your weekends stress-free.
Why Purge Build History in Jenkins? The Indispensable Act of Digital Housekeeping
Before we jump into the “how,” it’s crucial to understand the “why.” Why bother purging build history in Jenkins in the first place? Isn’t all data valuable? While historical data can be immensely useful for trend analysis, debugging, and auditing, an ever-growing build history can quickly become a significant burden on your Jenkins environment. Here’s why this digital housekeeping is absolutely indispensable:
- Disk Space Conservation: This is arguably the most common and pressing reason. Every build, especially those with large workspaces, numerous artifacts, and extensive console outputs, consumes precious disk space. Over time, these accumulate, leading to disk full errors, system instability, and outright service disruption.
- Performance Improvement: A bloated Jenkins instance with millions of build records can suffer from performance degradation. Navigating build history, loading job pages, and even performing routine operations can become agonizingly slow as Jenkins struggles to process vast amounts of data. Purging helps streamline the system.
- Backup and Restore Efficiency: Backing up a Jenkins instance with terabytes of build history is a time-consuming and resource-intensive task. Smaller data footprints mean faster, more efficient backups and, critically, quicker recovery times should disaster strike.
- Reduced I/O Operations: Jenkins constantly reads and writes to its home directory. A smaller, more manageable data set reduces the input/output operations on your server’s disk, leading to better overall system responsiveness.
- Clarity and Relevance: Sometimes, too much data obscures what’s important. Keeping only relevant build history can make it easier for teams to find pertinent information, analyze recent trends, and debug current issues without sifting through years of obsolete data.
In essence, purging build history isn’t just about freeing up disk space; it’s about maintaining a healthy, performant, and manageable Jenkins environment that reliably serves your CI/CD needs. It’s a fundamental aspect of Jenkins administration that often gets overlooked until an emergency strikes.
Methods for Purging Jenkins Build History: Your Toolkit for Cleanliness
Let’s roll up our sleeves and explore the practical ways to tackle that ever-growing mountain of build data. Each method has its ideal use cases, pros, and cons.
The Built-in “Discard Old Builds” Feature: Your First Line of Defense
This is by far the safest, easiest, and most recommended method for routine build history management. Jenkins provides a fantastic feature called “Discard Old Builds” directly within each job’s configuration. It’s designed for automated, policy-driven cleanup.
How to Configure “Discard Old Builds” for a Job:
- Navigate to Your Job: Go to the specific job (or “project”) you want to manage.
- Access Configuration: Click “Configure” on the left-hand menu.
- Find “Discard Old Builds”: Scroll down to the “General” section. You’ll see a checkbox labeled “Discard Old Builds.” Check this box.
-
Set Your Retention Policy: Once checked, a set of options will appear, allowing you to define your retention strategy. Here’s a breakdown:
- Days to keep builds: This field specifies the maximum number of days you want to retain build records. Any build older than this threshold will be automatically discarded. For instance, if you set this to `30`, Jenkins will keep builds from the last 30 days.
- Max # of builds to keep: This field specifies the maximum number of recent builds to retain, regardless of age. If you set this to `50`, Jenkins will always keep your 50 most recent builds, even if some of them are older than your “Days to keep builds” setting.
-
Advanced: Clicking “Advanced” reveals more granular options, which are super handy:
- Days to keep successful builds: Similar to “Days to keep builds” but applies only to successful builds.
- Max # of successful builds to keep: Similar to “Max # of builds to keep” but applies only to successful builds.
- Days to keep failed builds: Self-explanatory, for failed builds.
- Max # of failed builds to keep: Self-explanatory, for failed builds.
- Artifacts: This is a critical one! You can also configure separate retention policies for build artifacts. This means you might keep build records longer but discard large artifacts sooner to save disk space. You’ll see “Days to keep artifacts” and “Max # of artifacts to keep.”
- Save Changes: Don’t forget to click “Save” at the bottom of the page.
Jenkins will then apply this policy periodically, usually after a new build completes, or as part of its internal cleanup tasks. It’s a proactive measure, ensuring your build history doesn’t spiral out of control.
My Two Cents on “Discard Old Builds”:
From my experience, a common mistake is setting overly generous retention policies. Teams often think they need to keep years of build history, only to realize they rarely look back beyond a few weeks or months. Be pragmatic! Discuss with your development and QA teams what the actual minimum retention period is for debugging and auditing. For most jobs, especially those that run frequently (like unit tests or integration tests), keeping a few hundred builds or a month’s worth of history is usually more than sufficient. For deployment jobs to production, you might want to keep more, perhaps a year’s worth of successful deployments, but even then, artifacts might not need to stick around that long. It’s all about finding that sweet spot between utility and resource consumption.
Using the Jenkins Script Console: For Global Control and Advanced Scenarios
Sometimes, configuring each job individually is simply not feasible, especially if you have hundreds or thousands of jobs. Or maybe you need to perform a one-off, immediate, and comprehensive cleanup. This is where the Jenkins Script Console, powered by Groovy, becomes your best friend. It allows you to execute scripts directly against your Jenkins instance, offering powerful automation capabilities.
Accessing the Script Console:
- Log in as Administrator: You need administrative privileges.
-
Navigate to Script Console: Go to
Manage Jenkins>Script Console(usually found under the “Tools and Actions” section).
Here are some powerful Groovy scripts you can use to purge build history. Always exercise extreme caution when running scripts from the console, as they can have immediate and irreversible effects. It’s highly recommended to back up your Jenkins home directory before executing any significant script.
Script 1: Discard Builds for a Single Job
This script mimics the “Discard Old Builds” feature but can be run instantly for a specified job.
def jobName = "Your-Specific-Job-Name" // Replace with your job name
def maxBuildsToKeep = 50 // Keep the 50 most recent builds
def daysToKeep = 30 // Keep builds from the last 30 days
def job = Jenkins.instance.getItemByFullName(jobName)
if (job == null) {
println "Job '${jobName}' not found."
} else if (job instanceof hudson.model.FreeStyleProject || job instanceof org.jenkinsci.plugins.workflow.job.WorkflowJob) {
def builds = job.getBuilds()
def buildsToDiscard = []
builds.each { build ->
// Check age (in milliseconds)
def buildTime = build.getTimeInMillis()
def thirtyDaysAgo = System.currentTimeMillis() - (daysToKeep * 24 * 60 * 60 * 1000L)
// Check build number for 'maxBuildsToKeep'
// We'll sort and take 'maxBuildsToKeep' later to ensure we keep the *most recent*
// For now, mark for discard if older than 'daysToKeep'
if (buildTime < thirtyDaysAgo) {
buildsToDiscard.add(build)
}
}
// Now, let's handle maxBuildsToKeep. We want to keep the N most recent builds regardless of age.
// So, we need to get all builds, sort them by number descending, and then discard anything beyond 'maxBuildsToKeep'.
def allBuildsSorted = builds.sort({ b1, b2 -> b2.number <=> b1.number }) // Sort descending
// Identify builds to explicitly keep by number (the N most recent)
def buildsToExplicitlyKeepByNumber = allBuildsSorted.take(maxBuildsToKeep)
def buildNumbersToExplicitlyKeep = buildsToExplicitlyKeepByNumber.collect { it.number }
def finalBuildsToDiscard = []
buildsToDiscard.each { build ->
if (!buildNumbersToExplicitlyKeep.contains(build.number)) {
finalBuildsToDiscard.add(build)
}
}
// Additionally, check any build that wasn't considered old by date, but might fall outside maxBuildsToKeep
allBuildsSorted.drop(maxBuildsToKeep).each { build ->
// Only add if it's not already in finalBuildsToDiscard (though it shouldn't be based on logic)
// and if it's not one of the explicitly kept ones (which it shouldn't be by definition of drop)
if (!finalBuildsToDiscard.contains(build)) {
finalBuildsToDiscard.add(build)
}
}
if (finalBuildsToDiscard.isEmpty()) {
println "No builds to discard for job '${jobName}' based on the specified policy."
} else {
println "Found ${finalBuildsToDiscard.size()} builds to discard for job '${jobName}'."
finalBuildsToDiscard.each { build ->
println "Discarding build #${build.number} from job '${jobName}'..."
build.delete()
}
println "Finished discarding builds for job '${jobName}'."
}
} else {
println "Job '${jobName}' is not a FreeStyleProject or Pipeline job, or its type is not handled."
}
return null // Important for Groovy scripts in Jenkins console
Note: This script directly calls build.delete(), which removes the build record, console output, and artifacts. Be very sure about your retention criteria.
Script 2: Discard Builds for ALL Jobs (Global Cleanup)
This is a powerful script for a global cleanup. It iterates through all jobs and applies a uniform discard policy. This is often used for emergency cleanups or applying a baseline policy across a large Jenkins instance.
def maxBuildsToKeep = 50 // Keep the 50 most recent builds per job
def daysToKeep = 30 // Keep builds from the last 30 days per job
println "Starting global build history cleanup..."
Jenkins.instance.getAllItems(hudson.model.Job.class).each { job ->
if (job instanceof hudson.model.FreeStyleProject || job instanceof org.jenkinsci.plugins.workflow.job.WorkflowJob) {
println "Processing job: ${job.name}"
def builds = job.getBuilds()
def buildsToDiscard = []
// First, identify builds older than 'daysToKeep'
def thirtyDaysAgo = System.currentTimeMillis() - (daysToKeep * 24 * 60 * 60 * 1000L)
builds.each { build ->
if (build.getTimeInMillis() < thirtyDaysAgo) {
buildsToDiscard.add(build)
}
}
// Now, let's handle maxBuildsToKeep. We want to keep the N most recent builds regardless of age.
def allBuildsSorted = builds.sort({ b1, b2 -> b2.number <=> b1.number }) // Sort descending
// Identify builds to explicitly keep by number (the N most recent)
def buildsToExplicitlyKeepByNumber = allBuildsSorted.take(maxBuildsToKeep)
def buildNumbersToExplicitlyKeep = buildsToExplicitlyKeepByNumber.collect { it.number }
def finalBuildsToDiscard = []
buildsToDiscard.each { build ->
if (!buildNumbersToExplicitlyKeep.contains(build.number)) {
finalBuildsToDiscard.add(build)
}
}
// Additionally, check any build that wasn't considered old by date, but might fall outside maxBuildsToKeep
allBuildsSorted.drop(maxBuildsToKeep).each { build ->
if (!finalBuildsToDiscard.contains(build)) {
finalBuildsToDiscard.add(build)
}
}
if (finalBuildsToDiscard.isEmpty()) {
println " No builds to discard for '${job.name}' based on the specified policy."
} else {
println " Found ${finalBuildsToDiscard.size()} builds to discard for '${job.name}'."
finalBuildsToDiscard.each { build ->
println " Discarding build #${build.number} from '${job.name}'..."
try {
build.delete()
} catch (e) {
println " Error discarding build #${build.number} from '${job.name}': ${e.message}"
}
}
println " Finished discarding builds for '${job.name}'."
}
} else {
println "Skipping non-FreeStyle/Pipeline job: ${job.name}"
}
}
println "Global build history cleanup finished."
return null
This script is a powerful tool for administrators. It gives you the ability to enforce a consistent policy across all jobs or perform a rapid mass cleanup when disk space becomes critically low.
Manual Deletion on the File System: The Last Resort (Use with Extreme Caution!)
I cannot stress this enough: Manually deleting files from the Jenkins home directory on the file system should be considered a last resort. This method is highly risky and can easily corrupt your Jenkins instance if not done precisely. Jenkins stores its build history in $JENKINS_HOME/jobs/. Each build is a subdirectory named after its build number (e.g., 1, 2, 3, etc.).
When you might consider manual deletion:
- Your Jenkins instance is so bogged down that the UI or Script Console is unresponsive.
- You need to delete a massive number of builds for a single job and other methods are failing or too slow.
The Perils of Manual Deletion:
Jenkins maintains internal pointers and XML configurations that reference these build directories. If you delete a directory manually without updating Jenkins’ internal state, you can end up with orphaned entries, broken links, and a corrupted Jenkins UI. This can lead to:
- Builds appearing in the UI but missing their data.
- Errors when trying to access or delete other builds.
- General instability of your Jenkins server.
How to (Carefully) Perform Manual Deletion:
If you *must* go this route, follow these steps meticulously:
- STOP Jenkins: This is non-negotiable. Shut down your Jenkins service completely. If Jenkins is running, it could be writing to files you’re trying to delete, leading to corruption.
-
BACK UP
$JENKINS_HOME: Before touching anything, create a full backup of your entire$JENKINS_HOMEdirectory. Seriously, don’t skip this. A simpletar -czvf jenkins_home_backup_$(date +%F).tar.gz $JENKINS_HOMEwill do wonders for your peace of mind. -
Navigate to the Job’s Build Directory:
cd $JENKINS_HOME/jobs/Your-Job-Name/builds/(Replace
Your-Job-Namewith the actual name of the job.) -
Identify Builds to Delete: List the contents of the directory. You’ll see numbered folders like
1,2,3, …1000. -
Delete the Old Build Directories: Use a command like
rm -rfor a loop for multiple builds. For example, to delete builds 1 through 99:for i in {1..99}; do rm -rf $i; doneOr, to delete all but the last 100 builds (USE WITH EXTREME CAUTION):
ls -d * | head -n -100 | xargs rm -rf(This command lists directories, takes all but the last 100, and deletes them. Double-check your
lsoutput before piping toxargs rm -rf!) -
Edit
nextBuildNumber: Navigate up one level to the job’s main directory:cd $JENKINS_HOME/jobs/Your-Job-Name/Open the
nextBuildNumberfile:cat nextBuildNumberThis file contains the number of the next build Jenkins will create. If you’ve deleted builds, this number might need adjustment if you deleted the highest numbered build. However, usually you only delete *old* builds, so this file typically doesn’t need to change.
-
Edit
config.xml: In some rare cases, particularly with very old Jenkins versions or specific job types, theconfig.xmlmight have references. It’s usually better *not* to manually edit this unless you know exactly what you’re doing. The built-in “Discard Old Builds” handles these references correctly. - START Jenkins: Once you’ve performed the deletions and are confident, start your Jenkins service. Jenkins should re-index the build history, though this can take some time if you’ve deleted a huge number of builds.
My advice? Avoid this method if at all possible. The risk of corrupting your instance outweighs the convenience for most situations.
Jenkins Plugins for History Management: Extending Capabilities
While the built-in “Discard Old Builds” is robust, the Jenkins ecosystem offers plugins that can enhance history management, particularly for scenarios not perfectly covered by the default options.
-
Build Discarder Plugin: This plugin enhances the “Discard Old Builds” functionality by providing more sophisticated conditions for discarding builds, such as based on build status (success, failure, unstable, aborted), or even regular expressions for console output. It gives you finer-grained control than the default options, especially useful for complex retention policies.
Installation and Usage:
- Go to
Manage Jenkins>Manage Plugins. - In the “Available” tab, search for “Build Discarder”.
- Install the plugin.
- After installation, when you configure a job, you’ll find additional options under “Discard Old Builds” for status-based retention.
- Go to
- Workspace Cleanup Plugin: While not directly for build history, this plugin is invaluable for managing disk space by cleaning the job’s workspace *before* or *after* a build. Often, large workspaces left behind from previous builds consume significant space, separate from build records themselves.
These plugins extend Jenkins’ native capabilities, offering more tailored solutions for complex history and workspace management needs.
Automating with Jenkinsfile (Declarative Pipeline Examples)
For modern Jenkins environments using Pipelines, you can integrate build history management directly into your Jenkinsfile. This ensures that every pipeline automatically adheres to your desired retention policy, providing consistency and making the policy part of your version-controlled codebase.
Example Jenkinsfile with Build History Discard:
You can configure the “Discard Old Builds” property programmatically within your Declarative Pipeline’s options block.
pipeline {
agent any
options {
// Discard old builds: keep at most 30 builds, or builds from the last 7 days, whichever is more restrictive.
// Also discard artifacts based on a separate policy.
buildDiscarder(logRotator(numToKeepStr: '30', daysToKeepStr: '7', artifactNumToKeepStr: '10', artifactDaysToKeepStr: '5'))
// If you need more complex options, you might need the Build Discarder Plugin and its specific syntax.
}
stages {
stage('Build') {
steps {
echo 'Building the application...'
// Your build steps here
}
}
stage('Test') {
steps {
echo 'Running tests...'
// Your test steps here
}
}
}
}
The logRotator syntax in the buildDiscarder option allows you to specify the various retention policies. This is the cleanest way to manage history for Pipeline jobs, as it’s self-documenting and version-controlled.
Best Practices and Critical Considerations for Purging Build History
Purging build history isn’t just a technical task; it’s an administrative responsibility that requires careful planning and consideration. Here are some best practices to ensure you do it effectively and safely:
1. Backup Before You Delete!
I can’t emphasize this enough. Before running any script, especially in the Script Console, or performing any manual file system deletions, make a full backup of your $JENKINS_HOME directory. This is your safety net against accidental data loss or corruption. It’s a lifesaver if you accidentally delete something critical or break your Jenkins instance.
2. Understand Disk Usage Patterns
Don’t just delete blindly. Use tools like du -sh $JENKINS_HOME/jobs/*/builds/ on your Jenkins server to understand which jobs are consuming the most disk space. This helps you target your cleanup efforts effectively. Sometimes, it’s not the number of builds, but the size of artifacts in a few specific jobs that’s the culprit.
3. Impact on Analytics and Auditing
Consider the implications of deleting history on your team’s ability to perform analytics, track long-term trends, or satisfy auditing requirements. For some production deployment pipelines, keeping a longer history (e.g., 6-12 months) of successful builds might be essential for compliance or debugging historical issues. Balance disk space with data utility.
4. Scheduled Maintenance and Automation
Integrate build history purging into your regular Jenkins maintenance schedule. The “Discard Old Builds” feature is perfect for this, as it automates the process. For more complex, global cleanups, consider writing a Jenkins Job that runs a Groovy script periodically (e.g., once a month) using the “Execute system Groovy script” build step. This turns a reactive cleanup into a proactive strategy.
5. Permissions and Security
Only grant access to the Script Console to trusted administrators. Running arbitrary Groovy scripts has the potential to compromise your Jenkins instance or delete critical data. Ensure that anyone with access understands the power and risks involved.
6. Don’t Forget Artifacts!
Often, the biggest offenders for disk space are not the build records themselves, but the artifacts generated by builds (JARs, WARs, Docker images, test reports, etc.). The “Discard Old Builds” feature has specific options for artifact retention. Make sure you configure these aggressively if artifacts are your main storage concern.
7. Consider External Artifact Storage
For very large artifacts or long-term retention, consider offloading them to external storage solutions like Nexus, Artifactory, Amazon S3, Azure Blob Storage, or Google Cloud Storage. Jenkins can then simply store links or pointers to these artifacts, drastically reducing its own disk footprint.
My Personal Experience with Jenkins Disk Crunches:
I’ve been in the trenches when a Jenkins master’s disk filled up overnight. It’s not pretty. Builds would queue indefinitely, the UI became molasses-slow, and trying to even *access* a job’s configuration to enable “Discard Old Builds” felt like wrestling an alligator. In those desperate moments, the Script Console became my scalpel. A carefully crafted Groovy script to prune the oldest 50% of builds across all jobs, executed from the console, often provided enough breathing room to then go back and properly configure individual job policies. It taught me a valuable lesson: Proactive management through “Discard Old Builds” and regular monitoring are far, far better than reactive crisis management with a full disk.
Frequently Asked Questions (FAQs)
Let’s address some common questions that pop up when dealing with Jenkins build history.
1. Will purging build history delete my job configurations or source code?
No, purging build history in Jenkins primarily targets the build records themselves – specifically, the console output, build logs, and any artifacts associated with individual builds. It will not delete your job configurations (which are stored as XML files in $JENKINS_HOME/jobs/) or your source code (which is typically managed in an external Version Control System like Git and only checked out into the workspace for a build).
The job’s structure, parameters, and SCM settings remain intact. The “Discard Old Builds” feature and standard Groovy scripts are designed to only remove the historical data of past executions, not the definition of the job itself. Manual deletion, however, *could* accidentally delete job configuration files if you’re not careful with your rm commands, which is why it’s highly discouraged.
2. Can I recover deleted build history?
Generally, no. Once build history is purged through the “Discard Old Builds” feature, the Script Console, or manual deletion, it is permanently removed from your Jenkins instance and its file system. This is why backups are so critical. If you have a backup of your $JENKINS_HOME directory from before the deletion, you might be able to restore specific build records by extracting them from the backup and carefully placing them back into the correct job’s builds directory while Jenkins is offline.
However, this process is complex and prone to errors. It’s much better to have a clear retention policy from the outset and ensure you’re not deleting data that might be needed in the future. Prevention is definitely better than trying to recover after the fact.
3. How often should I purge build history?
The ideal frequency depends heavily on several factors:
- Build frequency: Jobs that run dozens or hundreds of times a day will accumulate history much faster than those that run weekly.
- Artifact size: Jobs producing large artifacts will consume disk space more rapidly.
- Disk capacity: If you have limited disk space, you’ll need a more aggressive purging strategy.
- Team needs: How far back do your developers and QA engineers typically look for debugging or analysis?
For most environments, setting up the “Discard Old Builds” feature on individual jobs is the best approach, as it automatically purges after each build based on your defined policy (e.g., keep 30 builds or 7 days, whichever is met first). This makes the process continuous and proactive. For larger, less frequent cleanups or global policy enforcement, scheduling a Groovy script via a Jenkins job to run weekly or monthly can be a good supplement.
The key is to monitor your disk usage trends. If you consistently see your disk space nearing its limit, it’s a clear sign you need to review and tighten your retention policies.
4. Does purging build history affect the job’s build number sequence?
No, purging old build history does not reset or alter the job’s build number sequence. Jenkins maintains a nextBuildNumber file within each job’s directory ($JENKINS_HOME/jobs/). This file simply indicates what the number of the *next* build will be. When you discard old builds, you’re only removing historical records; the counter for new builds continues to increment from where it left off.
For example, if you have builds #1 through #100, and you discard builds #1 through #90, your next build will still be #101. The sequence remains consistent, which is crucial for traceability and avoiding confusion. The only way the build number sequence might be affected is if you manually delete the nextBuildNumber file or explicitly modify its content, which is generally not recommended.
By understanding these methods and best practices, you can effectively manage your Jenkins build history, ensuring a lean, fast, and reliable CI/CD platform for your organization. Happy purging!