# How Jenkins Handles Job Configuration Persistence with XStream

> Discover how Jenkins handles job configuration persistence with XStream. Learn about XML files, XStream2, XmlFile, and the Saveable interface for saving Jenkins jobs.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: internals
- Published: 2026-06-19

---

**Jenkins persists job configurations to XML files using a hardened XStream wrapper (`XStream2`) and a file abstraction layer (`XmlFile`), with model objects implementing the `Saveable` interface to trigger serialization to `<job-dir>/config.xml`.**

Jenkins stores every job configuration in a [`config.xml`](https://github.com/jenkinsci/jenkins/blob/main/config.xml) file within the job's directory on the master's file system. The serialization and deserialization of these XML documents are handled by the XStream library, wrapped in Jenkins-specific helper classes to ensure security and consistency. Understanding how the jenkinsci/jenkins core implements this persistence mechanism is essential for plugin developers and administrators troubleshooting configuration issues.

## The XStream2 Wrapper: Secure Serialization Core

At the heart of Jenkins persistence lies `hudson.util.XStream2`, a thin wrapper around the XStream library located in [`core/src/main/java/hudson/util/XStream2.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/util/XStream2.java). This class configures a secure XStream instance by registering the allowed class hierarchy, type aliases, and custom converters.

The framework exposes this configured instance as a singleton through `Jenkins.XSTREAM2`, ensuring that all operations throughout the application use the same security settings and converters. When serializing objects, `XStream2` applies whitelisting filters that prevent arbitrary object injection attacks.

## XmlFile: The XML Persistence Layer

While `XStream2` handles object-to-XML conversion, the actual file operations are managed by `hudson.XmlFile` ([`core/src/main/java/hudson/XmlFile.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/XmlFile.java)). An `XmlFile` object is constructed with a reference to the target file **and** the specific `XStream` instance that should perform the serialization and deserialization.

This separation allows the persistence layer to remain agnostic of the specific serialization strategy while ensuring that the same security-hardened `XStream2` instance is used consistently across all read and write operations.

## The Saveable Interface and Job Persistence

Most Jenkins model objects implement the `hudson.model.Saveable` interface, which defines the contract for objects that can persist themselves. The default implementation of `Saveable.save()` delegates to `XmlFile`, creating a standardized persistence pattern across the codebase.

### Saving Job Configuration

The `hudson.model.Job` class (and subclasses like `FreeStyleProject`) inherits from `hudson.model.AbstractItem`, which implements `Saveable`. When a job's configuration changes, Jenkins calls `Job.save()`, which creates an `XmlFile` pointing at `<job-dir>/config.xml` and writes the job object using the shared `XStream2` instance.

The persistence flow follows this path:

```

Job.save()
 └─> new XmlFile(configFile, Jenkins.XSTREAM2)
       └─> XmlFile.write(this)  → XStream2.toXML(this) → writes <job>/config.xml

```

### Loading Jobs at Startup

During Jenkins startup, the system scans each job directory and reconstructs objects using the `Job.load()` method or constructors. This process reads [`config.xml`](https://github.com/jenkinsci/jenkins/blob/main/config.xml) via `XmlFile.read()`, which internally calls `XStream2.fromXML()`. The XStream instance safely reconstructs the job object, applying the same security filters used during writing.

The loading flow mirrors the save operation:

```

Job.load()
 └─> new XmlFile(configFile, Jenkins.XSTREAM2)
       └─> XmlFile.read() → XStream2.fromXML() → re-creates the Job instance

```

## Security Hardening for XStream Deserialization

Because XStream can be vulnerable to arbitrary object injection, Jenkins hardens the serialization pipeline through multiple layers of defense.

**Class Filtering:** A custom `ClassFilter` (located in [`jenkins/util/xstream/ClassFilter.java`](https://github.com/jenkinsci/jenkins/blob/main/jenkins/util/xstream/ClassFilter.java)) whitelists only Jenkins-specific classes. The `XStream2` class registers this filter automatically via `XStream2.setClassLoaderFilter`, ensuring that deserialization attempts with unauthorized classes are blocked before they can execute.

**Exception Handling:** The `XmlFile` helper catches `XStreamException` during read operations and re-wraps it as an `IOException`. This prevents internal XStream details from leaking through error messages while maintaining clear failure indicators for administrators.

## Practical Code Examples

The following examples demonstrate how Jenkins core and plugins interact with the persistence framework.

**Saving a job configuration (simplified from `hudson.model.Job`):**

```java
public void save() throws IOException {
    XmlFile config = new XmlFile(getConfigFile(), Jenkins.XSTREAM2);
    config.write(this);               // uses Jenkins.XSTREAM2 to serialize
}

```

**Loading a job configuration at startup:**

```java
public static Job<?,?> load(File jobDir) throws IOException {
    File cfg = new File(jobDir, "config.xml");
    XmlFile xml = new XmlFile(cfg, Jenkins.XSTREAM2);
    return (Job<?,?>) xml.read();     // deserialises using the secured XStream instance
}

```

**Custom XStream usage in a plugin:**

```java
XStream2 xs = new XStream2();                // gets the same security filter as the core
xs.alias("my-plugin-config", MyConfig.class);
String xml = xs.toXML(myConfig);             // serialize plugin-specific data
MyConfig cfg = (MyConfig) xs.fromXML(xml);   // deserialize safely

```

## Summary

- **Jenkins persists jobs to `<job>/config.xml`** using a combination of `XStream2` for serialization and `XmlFile` for file operations.
- **`hudson.util.XStream2`** ([`core/src/main/java/hudson/util/XStream2.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/util/XStream2.java)) provides a secure, singleton XStream instance available via `Jenkins.XSTREAM2`.
- **The `Saveable` interface** standardizes persistence across model objects, with `hudson.model.Job` delegating to `XmlFile` for actual XML read/write operations.
- **Security is enforced** through a `ClassFilter` that whitelists allowed classes, protecting against deserialization attacks.
- **Loading at startup** uses the same secure pipeline as saving, ensuring configuration integrity across Jenkins restarts.

## Frequently Asked Questions

### Where does Jenkins store job configuration files?

Jenkins stores each job's configuration in a [`config.xml`](https://github.com/jenkinsci/jenkins/blob/main/config.xml) file located inside the job's directory on the master's file system (typically under `$JENKINS_HOME/jobs/<job-name>/`). This XML file contains the serialized state of the job object, produced by the `XStream2` instance when `Saveable.save()` is invoked.

### How does Jenkins secure XStream deserialization?

Jenkins secures XStream through a custom `ClassFilter` located in [`jenkins/util/xstream/ClassFilter.java`](https://github.com/jenkinsci/jenkins/blob/main/jenkins/util/xstream/ClassFilter.java) that whitelists only approved classes. The `XStream2` wrapper automatically registers this filter, and the `XmlFile` class wraps exceptions to prevent information leakage. This prevents arbitrary object injection attacks while allowing legitimate Jenkins model objects to deserialize normally.

### Can plugins use XStream2 for custom configuration persistence?

Yes. Plugins can instantiate `XStream2` directly to obtain a security-hardened XStream instance that inherits the same class filtering as the core Jenkins installation. Plugins should use `xs.alias()` to register custom type aliases and follow the same pattern of wrapping file operations with `XmlFile` to ensure consistent error handling and security enforcement.

### What happens if a config.xml file is corrupted during Jenkins startup?

If `XmlFile.read()` encounters an invalid XML structure or a class that fails the security filter, it throws an `IOException` (wrapping the underlying `XStreamException`). Depending on the severity and location, Jenkins may skip loading that specific job, log the error, or in critical cases, fail to start. Administrators should check the Jenkins system log for specific XStream unmarshalling errors when troubleshooting configuration load failures.