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/andcredentials.xmlplugins/(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/jenkinsbefore 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.



































































