Jenkins remains the most widely adopted open-source automation server for continuous integration and continuous delivery (CI/CD). Written in Java, it provides hundreds of plugins that support building, deploying, and automating virtually any project. This guide walks you through a complete Jenkins installation on Ubuntu 24.04 or Debian 13, including Nginx reverse proxy with SSL, essential plugins, pipeline creation, agent configuration, and ongoing maintenance.

Whether you are setting up a single build server or the foundation for a large-scale CI/CD platform, the steps below will get Jenkins production-ready in under an hour.

Prerequisites

Before you begin, confirm the following requirements are in place:

  • A server running Ubuntu 24.04 LTS or Debian 13 (Trixie) with root or sudo access.
  • At least 2 GB of RAM (4 GB recommended for moderate workloads).
  • A registered domain name pointing to your server’s public IP (required for SSL).
  • Ports 80 and 443 open in your firewall for HTTP/HTTPS traffic.
  • Java 21 (OpenJDK). Jenkins LTS requires Java 17 or 21; this guide uses 21 for long-term support.

If you are running your server behind UFW, allow the necessary ports now:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Step 1: Install Java 21 (OpenJDK)

Jenkins runs on the JVM, so Java must be installed first. OpenJDK 21 is available in the default repositories for both Ubuntu 24.04 and Debian 13.

Update your package index and install the JDK:

sudo apt update
sudo apt install -y openjdk-21-jdk

Verify the installation:

java -version

Expected output:

openjdk version "21.0.6" 2025-01-21
OpenJDK Runtime Environment (build 21.0.6+7-Ubuntu-124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.6+7-Ubuntu-124.04.1, mixed mode, sharing)

If you need to manage multiple Java versions on the same host, use update-alternatives:

sudo update-alternatives --config java

Select the entry that points to /usr/lib/jvm/java-21-openjdk-amd64/bin/java and confirm.

Step 2: Add the Jenkins LTS Repository and Install

The Jenkins project maintains its own Debian/Ubuntu repository that always ships the latest LTS release. Adding it ensures you receive security patches and feature updates through apt.

Import the Jenkins GPG key:

curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null

Add the Jenkins stable repository:

echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null

Now install Jenkins:

sudo apt update
sudo apt install -y jenkins

The package automatically creates a jenkins system user, sets /var/lib/jenkins as the home directory, and drops a systemd unit file into place.

Step 3: Enable and Start Jenkins

Enable the service so it starts on boot, then bring it up:

sudo systemctl enable jenkins
sudo systemctl start jenkins

Check the service status:

sudo systemctl status jenkins

You should see active (running) in the output. Jenkins listens on port 8080 by default. A quick test with curl confirms it is responding:

curl -sI http://localhost:8080 | head -1

If the output shows HTTP/1.1 403 or HTTP/1.1 200, Jenkins is up and waiting for initial configuration. A connection refusal usually means the JVM has not finished starting; give it 15 to 30 seconds and try again.

Step 4: Complete the Initial Setup Wizard

Open your browser and navigate to http://your_server_ip:8080. Jenkins displays the Unlock Jenkins page and asks for an initial admin password.

Retrieve the password from the filesystem:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Paste the string into the browser and click Continue.

On the next screen you have two choices:

  • Install suggested plugins installs the most commonly used plugins. This is the recommended option for most users.
  • Select plugins to install lets you hand-pick exactly what you need.

For a general-purpose CI/CD server, go with Install suggested plugins. The process takes a few minutes depending on your network speed.

Once plugins are installed, Jenkins prompts you to Create First Admin User. Fill in a username, password, full name, and email address. Do not skip this step; relying on the initial admin password in production is a security risk.

Finally, confirm the Jenkins URL. If you plan to put Jenkins behind a reverse proxy (covered in the next section), you can set the public URL now or change it later under Manage Jenkins > System > Jenkins Location.

Step 5: Configure Nginx Reverse Proxy with SSL (Let’s Encrypt)

Running Jenkins directly on port 8080 over plain HTTP is acceptable for isolated lab environments, but any internet-facing instance needs TLS termination. Nginx handles this efficiently while also letting you serve Jenkins on standard HTTPS port 443.

Install Nginx and Certbot

sudo apt install -y nginx certbot python3-certbot-nginx

Create a new server block for Jenkins. Replace jenkins.example.com with your actual domain throughout:

sudo tee /etc/nginx/sites-available/jenkins.conf <<'EOF'
upstream jenkins {
    keepalive 32;
    server 127.0.0.1:8080;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    server_name jenkins.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name jenkins.example.com;

    # SSL certificates managed by Certbot (placeholders until certbot runs)
    ssl_certificate     /etc/letsencrypt/live/jenkins.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jenkins.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    client_max_body_size 100m;

    location / {
        proxy_pass         http://jenkins;
        proxy_http_version 1.1;

        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Forwarded-Port  443;

        proxy_set_header   Upgrade           $http_upgrade;
        proxy_set_header   Connection        $connection_upgrade;

        proxy_read_timeout 90;
        proxy_redirect     http://127.0.0.1:8080 https://jenkins.example.com;
    }
}
EOF

Enable the site and remove the default placeholder:

sudo ln -sf /etc/nginx/sites-available/jenkins.conf /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default

Obtain an SSL Certificate

Before requesting the certificate, temporarily comment out the listen 443 server block (or the ssl_certificate lines) so Nginx can start on port 80 for the HTTP-01 challenge. Alternatively, request the certificate before creating the 443 block. The simplest approach is to let Certbot handle everything:

sudo certbot --nginx -d jenkins.example.com --non-interactive --agree-tos -m [email protected]

Certbot will obtain the certificate, patch the Nginx configuration with the correct paths, and set up automatic renewal via a systemd timer. Confirm the timer is active:

sudo systemctl list-timers | grep certbot

Test the Nginx configuration and reload:

sudo nginx -t && sudo systemctl reload nginx

Update Jenkins URL

In the Jenkins dashboard, go to Manage Jenkins > System. Under Jenkins Location, set the Jenkins URL to https://jenkins.example.com/. Save and apply. From this point on, access Jenkins exclusively through the Nginx proxy on port 443.

For more details on securing web applications with Nginx and Let’s Encrypt, see our guide on how to install and configure Nginx on Ubuntu.

Step 6: Install Essential Plugins

The suggested plugin bundle covers the basics, but a serious CI/CD setup needs a few more. Navigate to Manage Jenkins > Plugins > Available plugins and install the following:

  • Pipeline (and Pipeline: Stage View) for declarative and scripted pipeline support.
  • Git and GitHub Branch Source for repository integration.
  • Docker Pipeline and Docker for building inside containers.
  • Blue Ocean for a modern pipeline visualization UI.
  • Credentials Binding for injecting secrets into builds.
  • SSH Agent for forwarding SSH keys to build steps.
  • Timestamper for adding timestamps to console output.
  • Workspace Cleanup for clearing workspace before/after builds.

You can also install plugins from the CLI, which is useful for automation:

sudo java -jar /var/lib/jenkins/jenkins-cli.jar -s http://localhost:8080/ -auth admin:YOUR_API_TOKEN install-plugin docker-workflow blueocean pipeline-stage-view ssh-agent timestamper ws-cleanup -restart

After the restart, verify all plugins are active under Manage Jenkins > Plugins > Installed plugins.

Step 7: Create Your First Pipeline Job

Pipelines defined in a Jenkinsfile stored alongside your source code are the standard approach. This keeps your build definition version-controlled and reviewable in pull requests.

From the Jenkins dashboard, click New Item, enter a name (for example, my-app-pipeline), select Pipeline, and click OK.

Under the Pipeline section, set Definition to Pipeline script from SCM. Choose Git as the SCM, enter your repository URL, and specify the branch (typically */main). The Script Path defaults to Jenkinsfile, which works if the file sits at the repository root.

Here is a minimal Jenkinsfile to get started:

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        stage('Build') {
            steps {
                sh 'echo "Running build..."'
                sh 'make build || echo "No Makefile found, skipping"'
            }
        }
        stage('Test') {
            steps {
                sh 'echo "Running tests..."'
                sh 'make test || echo "No tests defined, skipping"'
            }
        }
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sh 'echo "Deploying application..."'
            }
        }
    }

    post {
        always {
            cleanWs()
        }
        failure {
            echo 'Build failed. Check the console output for details.'
        }
    }
}

Save the job and click Build Now. Jenkins will clone the repository, detect the Jenkinsfile, and execute each stage sequentially. Click the build number and then Console Output to follow the run in real time.

If you work with Docker-based builds, see our article on installing Docker on Ubuntu to prepare your build host.

Step 8: Configure Agents and Nodes

Running all builds on the controller is fine during initial testing, but production workloads should be offloaded to dedicated agents. Jenkins supports several agent types; the two most common are SSH agents and Docker agents.

SSH Agent

An SSH agent is a separate Linux machine (or VM) that Jenkins connects to over SSH to run builds.

On the agent machine, create a jenkins user and install Java 21:

sudo useradd -m -s /bin/bash jenkins
sudo apt install -y openjdk-21-jdk

On the Jenkins controller, generate an SSH key pair (or use an existing one) and add the public key to /home/jenkins/.ssh/authorized_keys on the agent machine.

In the Jenkins UI, go to Manage Jenkins > Nodes > New Node. Set the following:

  • Name: build-agent-01
  • Type: Permanent Agent
  • Remote root directory: /home/jenkins
  • Labels: linux docker (labels let you target specific agents in pipelines)
  • Launch method: Launch agents via SSH
  • Host: the agent’s IP or hostname
  • Credentials: select the SSH key you stored (covered in the credentials section below)

Save and Jenkins will connect to the agent. Check the agent’s log if the connection fails; the most common issues are incorrect SSH key permissions and missing Java on the remote host.

Docker Agent

Docker agents spin up a fresh container for each build, providing perfect isolation. Install Docker on the Jenkins controller (or on a Docker host the controller can reach), then install the Docker and Docker Pipeline plugins.

In your Jenkinsfile, replace agent any with a Docker directive:

pipeline {
    agent {
        docker {
            image 'maven:3.9-eclipse-temurin-21'
            args '-v $HOME/.m2:/root/.m2'
        }
    }
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }
}

Jenkins pulls the specified image, runs the build inside the container, and destroys it when the pipeline finishes. This approach eliminates “works on my machine” problems and keeps the controller clean.

For a deeper look at container-based CI workflows, check out our guide on running Jenkins in a Docker container.

Step 9: Credentials Management

Jenkins stores credentials in an encrypted form and injects them into builds at runtime. Proper credential management prevents secrets from leaking into console logs and source control.

Navigate to Manage Jenkins > Credentials > System > Global credentials and click Add Credentials. Jenkins supports several credential types:

  • Username with password for basic auth to APIs, registries, and repositories.
  • SSH Username with private key for Git over SSH and remote agent connections.
  • Secret text for API tokens and single-value secrets.
  • Secret file for certificates, kubeconfig files, and similar.

In a pipeline, use the credentials() helper or the withCredentials block:

pipeline {
    agent any
    environment {
        DOCKER_CREDS = credentials('docker-hub-creds')
    }
    stages {
        stage('Push Image') {
            steps {
                sh '''
                    echo "$DOCKER_CREDS_PSW" | docker login -u "$DOCKER_CREDS_USR" --password-stdin
                    docker push myapp:latest
                '''
            }
        }
    }
}

Jenkins automatically masks the password in console output. For tighter control, scope credentials to specific folders or jobs rather than making everything global.

Step 10: Backup Jenkins Home Directory

The /var/lib/jenkins directory contains all job configurations, build history, plugin data, credentials, and secrets. Losing it means rebuilding everything from scratch. A reliable backup strategy is not optional.

Simple Backup with tar

Stop Jenkins briefly to get a consistent snapshot:

sudo systemctl stop jenkins
sudo tar czf /backup/jenkins-home-$(date +%Y%m%d).tar.gz -C /var/lib jenkins
sudo systemctl start jenkins

For zero-downtime backups, install the ThinBackup plugin. It handles incremental backups while Jenkins is running and can be scheduled from the UI under Manage Jenkins > ThinBackup.

What to Include and Exclude

Always back up:

  • config.xml (global configuration)
  • jobs/ (all job definitions)
  • users/ (user accounts)
  • secrets/ and credentials.xml
  • plugins/ (or at least a list of installed plugins and versions)

You can safely exclude:

  • workspace/ (rebuilt from source on the next build)
  • caches/
  • logs/ (unless you need historical build logs)

Store backups off-host. Sending them to an S3 bucket, NFS share, or a separate backup server protects you against disk failure on the Jenkins machine itself.

Step 11: Upgrade Jenkins

Because Jenkins was installed from the official APT repository, upgrading is straightforward:

sudo apt update
sudo apt install --only-upgrade jenkins

After the package is updated, restart the service:

sudo systemctl restart jenkins

A few upgrade best practices worth following:

  • Back up /var/lib/jenkins before every upgrade. If a new version introduces a breaking change, you can roll back quickly.
  • Read the LTS changelog before upgrading. Pay attention to deprecated features and required plugin updates.
  • After upgrading Jenkins, go to Manage Jenkins > Plugins > Updates and update all plugins to their latest compatible versions.
  • Test the upgrade in a staging environment first if your Jenkins instance serves production deployments.

For guidance on managing system packages and repositories, see our post on managing APT packages on Ubuntu and Debian.

Step 12: Troubleshooting Common Issues

Even a clean installation can run into problems. Below are the issues that come up most often and how to resolve them.

Java Version Mismatch

Symptom: Jenkins fails to start. The systemd journal shows an error like Unsupported Java version or class file version 65.0.

Cause: Jenkins LTS requires Java 17 or 21. If an older JDK (such as Java 11) is the system default, Jenkins will refuse to launch.

Fix: Confirm the active Java version and switch if necessary:

java -version
sudo update-alternatives --config java

You can also set the Java path explicitly in the Jenkins systemd override:

sudo systemctl edit jenkins

Add the following content in the editor:

[Service]
Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64"

Reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart jenkins

Port 8080 Already in Use

Symptom: Jenkins fails to bind to port 8080 because another process is already listening there.

Fix: Identify the conflicting process:

sudo ss -tlnp | grep 8080

Either stop the conflicting service or change the Jenkins port. Edit the systemd override:

sudo systemctl edit jenkins

Add:

[Service]
Environment="JENKINS_PORT=9090"

Reload and restart Jenkins, then update the Nginx upstream to point to the new port.

Plugin Compatibility Problems

Symptom: After an upgrade, the Jenkins UI shows warnings about incompatible or failed plugins. Some jobs may break.

Fix: Go to Manage Jenkins > Plugins > Updates and apply all available plugin updates. If a specific plugin is causing failures and no update is available, disable it temporarily:

sudo mv /var/lib/jenkins/plugins/broken-plugin.jpi /var/lib/jenkins/plugins/broken-plugin.jpi.disabled
sudo systemctl restart jenkins

Check the Jenkins issue tracker for the plugin in question and pin a known-good version until the maintainer releases a fix.

Out of Memory (OOM) Errors

Symptom: Jenkins becomes sluggish or crashes. The system journal shows java.lang.OutOfMemoryError or the OOM killer terminates the process.

Cause: The default JVM heap size may be too small for your workload, especially with many concurrent builds and heavy plugin usage.

Fix: Increase the JVM heap. Create or edit the systemd override:

sudo systemctl edit jenkins

Add the following, adjusting values to match your available RAM:

[Service]
Environment="JAVA_OPTS=-Xms1g -Xmx2g -XX:+UseG1GC"

Reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart jenkins

As a rule of thumb, allocate roughly half of the server’s total RAM to the Jenkins JVM and leave the rest for the operating system and build processes. On a 4 GB machine, -Xmx2g is a reasonable starting point. For hosts with 8 GB or more, you can go higher, but monitor heap usage through Manage Jenkins > System Information or a monitoring stack like Prometheus and Grafana before committing to a fixed value.

Nginx 502 Bad Gateway

Symptom: The browser shows a 502 error when accessing Jenkins through the Nginx proxy.

Cause: Jenkins is either not running or Nginx cannot reach port 8080.

Fix: Verify Jenkins is running and confirm the upstream port:

sudo systemctl status jenkins
curl -sI http://127.0.0.1:8080 | head -1

If Jenkins is up but Nginx still returns 502, check the Nginx error log for details:

sudo tail -20 /var/log/nginx/error.log

Common causes include SELinux or AppArmor policies blocking the proxy connection and mismatched upstream addresses.

Security Hardening Tips

A few additional steps to lock down your Jenkins instance:

  • Disable the built-in Jenkins user database if you integrate with LDAP, Active Directory, or SAML.
  • Enable CSRF protection (on by default in recent versions). Do not disable it.
  • Restrict agent-to-controller access under Manage Jenkins > Security > Agent/Controller Access Control.
  • Use role-based access control via the Role-based Authorization Strategy plugin instead of the default “Logged-in users can do anything” setting.
  • Keep Jenkins and all plugins up to date. Security advisories are published regularly on the Jenkins security page.
  • Audit builds and credentials access with the Audit Trail plugin.

Conclusion

You now have a fully functional Jenkins CI/CD server running on Ubuntu 24.04 or Debian 13, fronted by Nginx with a valid Let’s Encrypt certificate. The setup covers everything from the initial installation through pipeline creation, agent configuration, credential management, backups, upgrades, and troubleshooting.

From here, you can expand by integrating Jenkins with your Git hosting platform through webhooks, adding notification channels (Slack, email, Microsoft Teams), and building out multi-branch pipelines that automatically test every pull request. For more CI/CD and DevOps guides, browse our DevOps category.

LEAVE A REPLY

Please enter your comment!
Please enter your name here