--- title: "How to Install Jenkins on Ubuntu 24.04 with Nginx and SSL" description: "Install Jenkins LTS on Ubuntu 24.04 from the official repository, secure it behind Nginx with a Let's Encrypt certificate and run your first pipeline job." category: "CI/CD DevOps" language: en source: https://cubepath.com/docs/ci-cd-devops/jenkins-installation-and-configuration-on-linux updated: 2026-09-25 --- # How to Install Jenkins on Ubuntu 24.04 with Nginx and SSL Jenkins is an open-source automation server used to build, test and deploy software through pipelines defined as code. In this tutorial you will install the Jenkins LTS release on Ubuntu 24.04 from the official Jenkins repository, run it behind Nginx with a free Let's Encrypt certificate, complete the setup wizard and run a first pipeline. At the end you will also schedule a simple backup of the Jenkins home directory. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 GB of RAM and 20 GB of free disk space. Jenkins starts with less, but builds quickly use memory and disk. - A non-root user with `sudo` privileges. - A domain name with a DNS A record pointing to the server's public IP. This guide uses `jenkins.your_domain`. - Ports 22, 80 and 443 reachable from the Internet. ## Step 1 - Installing Java Jenkins runs on the Java Virtual Machine and current LTS releases support Java 21. Install the headless OpenJDK 21 runtime from the Ubuntu repositories: ```bash sudo apt update sudo apt install -y fontconfig openjdk-21-jre-headless ``` Check the installed version: ```bash java -version ``` ```output openjdk version "21.0.8" 2025-07-15 OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1) OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing) ``` The exact patch version may differ. You do not need to set `JAVA_HOME`: the Jenkins package finds `java` on the `PATH`. ## Step 2 - Adding the Jenkins repository The Ubuntu archive does not ship Jenkins, so add the official LTS (`debian-stable`) repository. First download its signing key into `/etc/apt/keyrings`: ```bash sudo install -m 0755 -d /etc/apt/keyrings sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key ``` Then add the repository, restricted to that key: ```bash echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null ``` Update the package index. The Jenkins repository should appear without errors: ```bash sudo apt update ``` ```output ... Get:5 https://pkg.jenkins.io/debian-stable binary/ Release [2,044 B] Get:6 https://pkg.jenkins.io/debian-stable binary/ Release.gpg [833 B] ... ``` > Note: The Jenkins project rotates its repository key from time to time. If `apt update` reports `NO_PUBKEY` or an expired signature, download the current key file listed on [pkg.jenkins.io/debian-stable](https://pkg.jenkins.io/debian-stable/) and repeat the `wget` command with it. ## Step 3 - Installing Jenkins Install the package: ```bash sudo apt install -y jenkins ``` The package creates a `jenkins` system user, stores all data in `/var/lib/jenkins` and starts the `jenkins` service. Confirm that it is running: ```bash sudo systemctl status jenkins ``` ```output ● jenkins.service - Jenkins Continuous Integration Server Loaded: loaded (/usr/lib/systemd/system/jenkins.service; enabled; preset: enabled) Active: active (running) since Thu 2026-09-24 10:12:03 UTC; 20s ago ... ``` By default Jenkins listens on port 8080 on every interface. Since Nginx will be the only public entry point, bind Jenkins to localhost with a systemd override: ```bash sudo systemctl edit jenkins ``` Add these lines in the editable area between the comments, then save and close the file: ```ini [Service] Environment="JENKINS_LISTEN_ADDRESS=127.0.0.1" ``` Restart Jenkins and check which address it listens on: ```bash sudo systemctl restart jenkins sudo ss -tlnp | grep 8080 ``` ```output LISTEN 0 50 [::ffff:127.0.0.1]:8080 *:* users:(("java",pid=4211,fd=8)) ``` Jenkins can take up to a minute to start. If the command returns nothing, wait and run it again, or follow the log with `sudo journalctl -u jenkins -f`. ## Step 4 - Configuring Nginx as a reverse proxy Install Nginx: ```bash sudo apt install -y nginx ``` Create a server block for Jenkins: ```bash sudo nano /etc/nginx/sites-available/jenkins ``` Add the following configuration, replacing `jenkins.your_domain` with your domain. The `map` block and the `Upgrade` headers let the Jenkins web interface use WebSockets, and `proxy_request_buffering off` avoids buffering large uploads such as plugin files: ```nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; listen [::]:80; server_name jenkins.your_domain; client_max_body_size 100m; location / { proxy_pass http://127.0.0.1:8080; 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 Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 90s; proxy_request_buffering off; } } ``` Enable the site, test the syntax and reload Nginx: ```bash sudo ln -s /etc/nginx/sites-available/jenkins /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` If UFW is active, allow SSH and web traffic. Port 8080 does not need a rule because Jenkins only listens on localhost: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw status ``` ## Step 5 - Enabling HTTPS with Let's Encrypt Install Certbot and its Nginx plugin: ```bash sudo apt install -y certbot python3-certbot-nginx ``` Request a certificate. Certbot validates the domain, edits the Jenkins server block to use the certificate and adds a redirect from HTTP to HTTPS: ```bash sudo certbot --nginx -d jenkins.your_domain ``` ```output Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/jenkins.your_domain/fullchain.pem ... Successfully deployed certificate for jenkins.your_domain to /etc/nginx/sites-enabled/jenkins Congratulations! You have successfully enabled HTTPS on https://jenkins.your_domain ``` The Ubuntu package installs a systemd timer that renews certificates automatically. Test the renewal process: ```bash sudo certbot renew --dry-run ``` ## Step 6 - Completing the setup wizard Open `https://jenkins.your_domain` in your browser. Jenkins asks for the initial administrator password, which it wrote to a file during the first start: ```bash sudo cat /var/lib/jenkins/secrets/initialAdminPassword ``` ```output 3f1e9c2b7a8d4e5f9a0b1c2d3e4f5a6b ``` Paste it into the **Unlock Jenkins** page and continue: 1. **Customize Jenkins**: choose **Install suggested plugins**. This installs Git, Pipeline, Credentials and the other plugins most setups need. 2. **Create First Admin User**: create your own administrator account with a strong password. After this step the initial password stops working. 3. **Instance Configuration**: confirm that the **Jenkins URL** is `https://jenkins.your_domain/`. Jenkins uses it in links, emails and webhooks. After **Start using Jenkins** you land on the dashboard. Go to **Manage Jenkins**: if the reverse proxy is misconfigured, Jenkins shows a warning saying "It appears that your reverse proxy set up is broken". No warning means the proxy headers are correct. ## Step 7 - Running your first pipeline A pipeline describes the build as code in the Groovy-based Jenkinsfile syntax. Create a test job to confirm that Jenkins can run steps on this server: 1. On the dashboard, click **New Item**. 2. Enter `hello-pipeline` as the name, select **Pipeline** and click **OK**. 3. Scroll to the **Pipeline** section, keep **Pipeline script** as the definition and paste this script: ```groovy pipeline { agent any stages { stage('Info') { steps { sh 'uname -a' sh 'java -version' } } stage('Build') { steps { sh 'echo "Building on $NODE_NAME, build #$BUILD_NUMBER"' } } } post { success { echo 'Pipeline finished successfully' } } } ``` 4. Click **Save**, then **Build Now**. Open the build from the **Builds** list and click **Console Output**. The last lines should look like this: ```output Building on built-in, build #1 ... Pipeline finished successfully [Pipeline] End of Pipeline Finished: SUCCESS ``` For real projects, commit the pipeline to your repository as a file named `Jenkinsfile` and, in the job, set **Definition** to **Pipeline script from SCM** with your repository URL. Store Git tokens and SSH keys under **Manage Jenkins** > **Credentials** and reference them by ID, never inline in the Jenkinsfile. > Tip: Running builds on the built-in node is fine for testing, but in production add at least one agent and set the number of executors on the built-in node to 0 under **Manage Jenkins** > **Nodes**. Builds then cannot read files in the Jenkins home directory. ## Step 8 - Backing up the Jenkins home directory All jobs, credentials, plugins and settings live in `/var/lib/jenkins`. Workspaces and caches can be rebuilt, so the backup below excludes them. Create the script: ```bash sudo nano /usr/local/sbin/jenkins-backup ``` ```bash #!/usr/bin/env bash set -euo pipefail backup_dir="/var/backups/jenkins" jenkins_home="/var/lib/jenkins" keep_days=14 stamp="$(date +%Y%m%d-%H%M%S)" mkdir -p "$backup_dir" chmod 700 "$backup_dir" tar -czf "$backup_dir/jenkins-$stamp.tar.gz" \ --exclude="./workspace" \ --exclude="./caches" \ --exclude="./.cache" \ -C "$jenkins_home" . find "$backup_dir" -name 'jenkins-*.tar.gz' -mtime +"$keep_days" -delete ``` Make it executable and run it once to test it: ```bash sudo chmod 750 /usr/local/sbin/jenkins-backup sudo /usr/local/sbin/jenkins-backup sudo ls -lh /var/backups/jenkins ``` ```output total 92M -rw-r--r-- 1 root root 92M Sep 24 10:48 jenkins-20260924-104812.tar.gz ``` Schedule it every night at 02:30 with a cron file: ```bash echo '30 2 * * * root /usr/local/sbin/jenkins-backup' | sudo tee /etc/cron.d/jenkins-backup > /dev/null ``` The archive contains encrypted credentials together with the keys that decrypt them (`secrets/` and `secret.key`), so treat it as sensitive and copy it to off-server storage. To restore, stop Jenkins, extract the archive into `/var/lib/jenkins`, run `sudo chown -R jenkins:jenkins /var/lib/jenkins` and start the service again. ## Troubleshooting - **`502 Bad Gateway` in the browser**: Jenkins is still starting or has stopped. Check `sudo systemctl status jenkins` and `sudo journalctl -u jenkins -n 50`. - **Jenkins fails to start after an upgrade with a Java version error**: the new release requires a newer Java. Install the version named in the log (for example `openjdk-21-jre-headless`) and restart the service. - **"It appears that your reverse proxy set up is broken"**: the `Host` or `X-Forwarded-Proto` header is missing in the Nginx block, or the Jenkins URL in **Manage Jenkins** > **System** does not match the address you use in the browser. - **`apt update` fails on the Jenkins repository**: the signing key changed. Download the current key as explained in Step 2. - **Builds fail with "No space left on device"**: old builds and workspaces fill the disk. Set **Discard old builds** in each job and check usage with `sudo du -sh /var/lib/jenkins/*`. ## Conclusion Jenkins now runs on Ubuntu 24.04, reachable only through Nginx over HTTPS, with a working pipeline and nightly backups of its configuration. From here, connect a Git repository with a `Jenkinsfile` and a webhook so each push triggers a build, add a dedicated build agent so jobs do not run on the controller, and review **Manage Jenkins** > **Security** to decide who can create and run jobs. Keep Jenkins and its plugins updated from **Manage Jenkins** > **Plugins**, since most Jenkins security advisories affect plugins.