How to Create and Manage Jenkins Plugins

How to Create and Manage Jenkins Plugins

The moment I realized Jenkins plugins were “just Java” was the moment the whole ecosystem stopped feeling like a black box. I had spent years installing plugins from the marketplace without ever wondering how they worked, until my team needed a custom build step that didn’t exist anywhere – a small internal tool that posted deployment metadata to our own compliance system. Building that plugin taught me more about Jenkins’ internals than years of just using it ever did. This guide covers both sides: managing the plugins you install, and building your own from scratch.

Understanding the Jenkins Plugin Architecture

Jenkins itself is a relatively small core; almost everything you interact with day to day – Git integration, pipeline syntax, Slack notifications, Kubernetes clouds – is a plugin. Plugins are packaged as .hpi files (a specialized JAR), and they hook into Jenkins through well-defined extension points: Builder (build steps), Publisher (post-build actions), SCM (source control systems), Cloud (dynamic agent providers), and many more. Jenkins uses a dependency-injection-like model where plugins declare which extension point they implement, and Jenkins discovers and loads them at startup.

Each plugin has:

  • A pom.xml (plugins are Maven projects, built against the jenkins-core and plugin-pom parent artifacts).
  • Java classes extending Jenkins extension points, annotated with @Extension.
  • Jelly/Groovy view files (.jelly) for rendering configuration UI, since Jenkins predates most modern frontend frameworks and still relies heavily on Jelly/Stapler for its web UI.
  • A src/main/resources directory holding help text, icons, and localization files.

Part 1: Managing Plugins (Installing, Updating, Removing)

Installing Plugins via the UI

Go to Manage JenkinsPluginsAvailable plugins, search, select, and click Install. Jenkins downloads the .hpi from the configured Update Center and either installs immediately or after a restart, depending on the plugin.

Installing Plugins via CLI

java -jar jenkins-cli.jar -s http://localhost:8080/ install-plugin git slack kubernetes -restart

Installing Plugins via Configuration as Code (Recommended for Production)

Rather than clicking through the UI, I manage plugins declaratively using a plugins.txt file, especially inside a custom Jenkins Docker image:

FROM jenkins/jenkins:lts-jdk17
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
# plugins.txt
git:5.2.1
workflow-aggregator:596.v8c21c963d92d
slack:715.v4b_1c5c88f76d
kubernetes:4232.v20250115
credentials-binding:657.v2b_19db_2b_c8b_4

Pinning exact versions here means every rebuild of your Jenkins image produces an identical plugin set – critical for reproducibility.

Updating Plugins Safely

  1. Check Manage JenkinsPluginsUpdates for available updates.
  2. Read release notes for breaking changes before bulk-updating.
  3. Snapshot your Jenkins home directory (or take a volume snapshot in Kubernetes) before major version jumps.
  4. Update in a staging Jenkins instance first if you run anything business-critical through it.

Removing Plugins

Uninstall from the Installed plugins tab, but be aware Jenkins won’t automatically remove plugins that others depend on – check the dependency tree first, visible on each plugin’s detail page.

Part 2: Building Your Own Jenkins Plugin

Step 1: Generate the Plugin Skeleton

Jenkins provides a Maven archetype to scaffold a new plugin:

mvn -U org.jenkins-ci.tools:maven-hpi-plugin:create \
  -DgroupId=com.mycompany.jenkins \
  -DartifactId=compliance-notifier

This generates a working Maven project with the correct parent POM (org.jenkins-ci.plugins:plugin) already wired up.

Step 2: Understand the Generated Structure

compliance-notifier/
├── pom.xml
├── src/main/java/com/mycompany/jenkins/
│   └── ComplianceNotifierBuilder.java
├── src/main/resources/com/mycompany/jenkins/ComplianceNotifierBuilder/
│   ├── config.jelly
│   └── help-message.html
└── src/test/java/...

Step 3: Write a Simple Build Step

Here’s a minimal custom build step (“Builder”) that posts build metadata to an internal endpoint:

package com.mycompany.jenkins;

import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.tasks.Builder;
import hudson.tasks.BuildStepDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
import java.net.HttpURLConnection;
import java.net.URL;

public class ComplianceNotifierBuilder extends Builder {

    private final String endpointUrl;

    @DataBoundConstructor
    public ComplianceNotifierBuilder(String endpointUrl) {
        this.endpointUrl = endpointUrl;
    }

    public String getEndpointUrl() {
        return endpointUrl;
    }

    @Override
    public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) {
        try {
            listener.getLogger().println("Notifying compliance system at " + endpointUrl);
            URL url = new URL(endpointUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            String payload = "{\"job\":\"" + build.getProject().getName()
                    + "\",\"build\":" + build.getNumber() + "}";
            conn.getOutputStream().write(payload.getBytes());
            int code = conn.getResponseCode();
            listener.getLogger().println("Compliance system responded with: " + code);
            return code == 200;
        } catch (Exception e) {
            listener.getLogger().println("Failed to notify compliance system: " + e.getMessage());
            return false;
        }
    }

    @Extension
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
        @Override
        public boolean isApplicable(Class<? extends hudson.model.AbstractProject> jobType) {
            return true;
        }

        @Override
        public String getDisplayName() {
            return "Notify Compliance System";
        }
    }
}

Step 4: Add the Jelly Configuration View

<!-- config.jelly -->
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
  <f:entry title="Endpoint URL" field="endpointUrl">
    <f:textbox />
  </f:entry>
</j:jelly>

This renders the input field for the build step in the job configuration UI, automatically bound to the constructor parameter of the same name.

Step 5: Build and Test Locally

mvn hpi:run

This spins up a local Jenkins instance with your plugin pre-installed at http://localhost:8080/jenkins, so you can test it in a real UI before packaging.

Step 6: Package and Install

mvn clean package

This produces target/compliance-notifier.hpi, which you upload via Manage JenkinsPluginsAdvanced settingsDeploy Plugin.

Step 7: Write Unit Tests

Jenkins provides JenkinsRule for integration-style plugin testing:

@Rule
public JenkinsRule jenkins = new JenkinsRule();

@Test
public void testBuildStep() throws Exception {
    FreeStyleProject project = jenkins.createFreeStyleProject();
    project.getBuildersList().add(new ComplianceNotifierBuilder("http://localhost:9999/mock"));
    FreeStyleBuild build = jenkins.buildAndAssertSuccess(project);
    jenkins.assertLogContains("Notifying compliance system", build);
}

Building a Pipeline Step Instead of a Freestyle Builder

Modern Jenkins usage is pipeline-first, so most custom plugins today implement a Step (via org.jenkinsci.plugins.workflow.steps.Step) so it’s callable directly from a Jenkinsfile, like complianceNotify url: 'https://...'. This is more involved (requires a StepExecution class) but is the right approach if your plugin needs to work with declarative or scripted pipelines.

Publishing to the Jenkins Update Center

If you want to share your plugin publicly:

  1. Host the source on GitHub under the jenkinsci organization (requires a hosting request via their JIRA/GitHub process).
  2. Set up CD via the Jenkins Infrastructure’s shared CI, which builds and publishes releases automatically.
  3. Follow the Plugin Tutorial for the exact release process, including signing and versioning conventions.

Integrating with the Wider Toolchain

  • Git – plugin source lives in Git, typically hosted on GitHub with the jenkinsci org’s shared release pipeline.
  • Maven – the entire plugin build system is Maven-based, using the org.jenkins-ci.plugins:plugin parent POM for dependency management.
  • Docker – test your plugin inside a container matching your production Jenkins version to catch compatibility issues early.

Monitoring and Troubleshooting

  • Plugin fails to load at startup – check $JENKINS_HOME/jenkins.log for ClassNotFoundException or version mismatch errors, usually caused by a dependency conflict with another installed plugin.
  • UI fields not showing – verify your Jelly file path exactly matches the fully qualified class name of your Descriptor/Builder.
  • mvn hpi:run fails to start – often a Java version mismatch; check the required JDK version in your plugin’s parent POM.
  • Plugin works locally but not in production – check for hard-coded localhost URLs or missing environment-specific configuration.

Security Best Practices

  • Never log secrets (API keys, tokens) in listener.getLogger() output, since Jenkins console logs are often broadly readable.
  • Use Jenkins’ Secret type for any credential-like field in your plugin’s configuration, not plain String.
  • Validate all user input in your DescriptorImpl.doCheckX() methods to avoid injection issues if you shell out to external commands.
  • Follow the Jenkins security advisories process if you discover or need to report a vulnerability in a plugin you maintain.

Best Practices

  • Prefer contributing a pipeline Step over a legacy Builder if you want the plugin usable in modern declarative pipelines.
  • Keep plugins narrowly scoped – one plugin, one responsibility – rather than building a monolith with a dozen unrelated features.
  • Write integration tests using JenkinsRule from day one; UI regressions in plugins are notoriously easy to introduce silently.
  • Pin exact plugin versions in production via plugins.txt/JCasC rather than letting auto-update run unattended.

FAQs

Do I need to know Jelly to build a plugin? For UI-configurable plugins, yes, though newer Jenkins versions also support a Jenkins-specific Groovy DSL as an alternative in some cases.

Can I write Jenkins plugins in a language other than Java? Groovy is fully supported and commonly used since it compiles to the same JVM bytecode and integrates cleanly with the plugin POM.

How do I know which extension point to implement? Check the Jenkins Extension Points list – it catalogs every pluggable interface in Jenkins core and popular plugins.

Is there a faster way to prototype without building a full plugin? Yes, for simple reusable logic, a Shared Library (Groovy scripts loaded into pipelines via @Library) is often enough and requires no Java/Maven packaging at all.

How often should I update installed plugins? Monthly is a reasonable cadence for most teams, with immediate updates for anything flagged in a Jenkins security advisory.

Summary

Managing Jenkins plugins well means pinning versions, testing updates in staging, and using Configuration as Code so your plugin set is reproducible rather than a mystery accumulated over years of manual installs. Building your own plugin is more approachable than it looks – scaffold with the Maven HPI archetype, implement the right extension point, test locally with mvn hpi:run, and package it as an .hpi. Whether you’re just keeping your instance healthy or extending Jenkins with custom internal tooling, understanding the plugin architecture is what turns Jenkins from a fixed tool into a platform you can shape around your team’s actual workflow.

References

  • Jenkins Plugin Tutorial: https://www.jenkins.io/doc/developer/tutorial/
  • Jenkins Extension Points: https://www.jenkins.io/doc/developer/extensions/
  • Jenkins Plugin Development documentation: https://www.jenkins.io/doc/developer/
  • Jenkins Configuration as Code plugin: https://plugins.jenkins.io/configuration-as-code/
  • Jenkins Security Advisories: https://www.jenkins.io/security/
Total
1
Shares

Leave a Reply

Previous Post
How to Run Jenkins in Docker Containers

How to Run Jenkins in Docker Containers

Next Post
How to Set Up Jenkins for Selenium Testing

How to Set Up Jenkins for Selenium Testing

Related Posts