Jenkins on Ubuntu: Production Install with Java 17, Nginx, SSL & Hard-Won Lessons
Step-by-step guide to install Jenkins on Ubuntu 22.04/24.04 with Java 17, APT repo, UFW, Nginx reverse proxy, Let's Encrypt SSL, and troubleshooting common failures from real incidents..
20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.
- ✓Ubuntu 22.04 or 24.04 LTS, sudo/root access, Java 17 or 11 (OpenJDK), curl, wget, git, basic Linux CLI knowledge, familiarity with systemd services, understanding of Jenkins pipeline concepts
- Use Java 17 (OpenJDK) for Jenkins 2.440+; Java 21 is experimental. Install with: sudo apt install openjdk-17-jdk -y
- Add Jenkins APT repo: curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
- Install Jenkins: sudo apt update && sudo apt install jenkins -y
- Get initial admin password: sudo cat /var/lib/jenkins/secrets/initialAdminPassword
- Configure UFW: sudo ufw allow 8080 && sudo ufw allow 22 && sudo ufw enable
- Set up Nginx reverse proxy with Let's Encrypt SSL using certbot
- Manage Jenkins service: sudo systemctl enable --now jenkins; logs at /var/log/jenkins/jenkins.log
- Common failure: startup fails due to Java version mismatch — check /usr/share/jenkins/jenkins-config with JAVA_HOME
Imagine you're a chef who needs to prepare meals for a big event. Jenkins is like your head chef — it automates tasks like chopping veggies and boiling water. But to work, it needs a proper kitchen (Ubuntu) and a reliable stove (Java). Installing Jenkins on Ubuntu is like setting up that kitchen: you install the stove (Java), connect the gas line (APT repository), then turn on the stove (start Jenkins). The initial admin password is like the key to the kitchen door — you need it to get in. Firewall (UFW) is the security guard that only lets approved people in. Nginx is like a waiter who takes orders from the internet and brings them to the kitchen. SSL is the secret code that keeps orders private. Without these, your kitchen might burn down or get robbed.
I remember the first time I installed Jenkins on Ubuntu for a production CI/CD pipeline. It was a Friday afternoon, and I thought it would be a 30-minute task. Six hours later, I was still debugging a cryptic 'Failed to start Jenkins' error because I had installed Java 11 instead of Java 17. The logs showed nothing useful — just 'WARNING: Could not initialize Java'. That was my hard lesson: Jenkins is picky about Java versions, and the default Ubuntu repos often have the wrong one. Since then, I've installed Jenkins hundreds of times across Ubuntu 20.04, 22.04, and 24.04, and I've seen every failure mode: APT key issues, port conflicts, SSL certificate renewal failures, and plugin installation wizard crashes.
Jenkins has been the backbone of CI/CD for over a decade. It started as a fork of Hudson in 2011 and grew into the most widely used automation server. But its installation on Ubuntu still trips up many engineers. The official documentation is good but assumes ideal conditions. In production, you deal with corporate proxies, locked-down firewalls, and legacy Java versions. This article covers the exact steps I use in production, including the gotchas that aren't in the docs.
I'll walk through Java 17 installation, APT repository setup, initial admin password retrieval, UFW firewall configuration, Nginx reverse proxy with Let's Encrypt SSL, the plugin wizard, creating the first admin user, systemd service management, and troubleshooting common failures. Every command has been tested on Ubuntu 22.04 LTS with Jenkins 2.440.3. No fluff, just production patterns.
1. Prerequisites: Ubuntu 22.04/24.04 LTS and Sudo Access
Before installing Jenkins, ensure you have a clean Ubuntu 22.04 or 24.04 LTS server with sudo privileges. I recommend a minimal server installation — no GUI, no extra packages. The server should have at least 2GB RAM and 20GB disk space for a basic setup. For production, use 4GB RAM and 50GB+.
Update the package index and upgrade existing packages:
``bash sudo apt update sudo apt upgrade -y ``
Gotcha: Ubuntu 24.04 ships with snapd which may interfere with port binding. If you encounter port conflicts, disable snapd: sudo systemctl disable --now snapd. I learned this the hard way when Jenkins couldn't bind to port 8080 because snapd's 'lxd' service was using it.
Verify the OS version: lsb_release -a. You should see 'Ubuntu 22.04.4 LTS' or similar. Also check that systemd is the init system (it should be).
Production insight: In a cloud environment (AWS EC2, DigitalOcean), ensure the security group/firewall allows inbound traffic on ports 22, 80, 443, and optionally 8080 (though we'll use Nginx to proxy). I once spent hours debugging a 'connection refused' because the cloud firewall was blocking port 8080 — UFW was configured correctly, but the cloud firewall was the culprit.
2. Installing Java 17 (OpenJDK) — The Only Supported Version
Jenkins 2.440+ requires Java 17 or 21. Java 11 is deprecated. I recommend Java 17 (OpenJDK) as it's the most tested. Java 21 is experimental and may cause plugin compatibility issues.
Install OpenJDK 17:
``bash sudo apt install openjdk-17-jdk -y ``
Verify the installation:
``bash java -version ``
Expected output: `` openjdk version "17.0.11" 2024-04-16 OpenJDK Runtime Environment (build 17.0.11+9-Ubuntu-122.04.1) OpenJDK 64-Bit Server VM (build 17.0.11+9-Ubuntu-122.04.1, mixed mode, sharing) ``
Gotcha: Ubuntu may have multiple Java versions. Use update-java-alternatives to set the default:
``bash sudo update-java-alternatives -l sudo update-java-alternatives -s java-1.17.0-openjdk-amd64 ``
Set JAVA_HOME environment variable in /etc/environment:
``bash echo 'JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' | sudo tee -a /etc/environment source /etc/environment ``
Production insight: In a Dockerized Jenkins setup, I once saw Jenkins fail with 'OutOfMemoryError' because the container had limited heap. For bare-metal, ensure you set JENKINS_JAVA_OPTIONS in /etc/default/jenkins: JENKINS_JAVA_OPTIONS="-Xmx2048m -Xms512m". This is critical for production.
3. Adding the Jenkins APT Repository
The official Jenkins APT repository provides stable releases. Use the Debian-style repository (not the old RPM one).
Add the GPG key and repository:
``bash curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo gpg --dearmor -o /usr/share/keyrings/jenkins-keyring.asc.gpg echo 'deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc.gpg] https://pkg.jenkins.io/debian-stable binary/' | sudo tee /etc/apt/sources.list.d/jenkins.list ``
Then update and install:
``bash sudo apt update sudo apt install jenkins -y ``
Gotcha: The key file name changed in 2023. If you use an old guide with 'jenkins.io.key', it will fail. Always use the correct key URL from the official docs. If you get 'The following signatures couldn't be verified because the public key is not available', re-add the key as above.
Production insight: In air-gapped environments, you need to manually download the .deb package and dependencies. Use sudo apt install ./jenkins_2.440.3_all.deb after transferring the file. I've done this for government clients where internet access is restricted.
sudo apt-mark hold jenkins.4. Retrieving the Initial Admin Password
After installation, Jenkins starts automatically and generates an initial admin password. This password is stored in a file only readable by root.
Retrieve it:
``bash sudo cat /var/lib/jenkins/secrets/initialAdminPassword ``
Copy this password. You'll need it to unlock Jenkins in the browser.
Gotcha: If the file doesn't exist, Jenkins hasn't started yet. Check the service status: sudo systemctl status jenkins. If it's failed, check logs: sudo journalctl -u jenkins -n 50. Common reason: Java not installed or wrong version.
Production insight: In a CI/CD pipeline, you might want to automate the initial setup. You can set the password via environment variable or by modifying the config file before first start. However, for security, always use the auto-generated password and change it immediately after first login.
5. Configuring UFW Firewall
Ubuntu's Uncomplicated Firewall (UFW) is essential for production. By default, Jenkins listens on port 8080. We'll allow SSH (22), HTTP (80), HTTPS (443), and optionally Jenkins (8080) if you want direct access.
First, check UFW status:
``bash sudo ufw status ``
If inactive, enable it:
``bash sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 8080/tcp # optional, for direct access sudo ufw enable ``
Gotcha: Enabling UFW will disconnect your SSH session if you haven't allowed port 22. Always allow SSH first. I once locked myself out of a server because I forgot to allow 22 before enabling UFW. Had to use the cloud console to disable UFW.
Verify the rules:
``bash sudo ufw status verbose ``
Production insight: For production, only allow 80 and 443 (Nginx will proxy to Jenkins). Disallow 8080 from external access. Use sudo ufw deny 8080/tcp after Nginx is set up.
6. Setting Up Nginx Reverse Proxy
Nginx acts as a reverse proxy, forwarding requests from ports 80/443 to Jenkins on 127.0.0.1:8080. This adds SSL termination, load balancing, and security headers.
Install Nginx:
``bash sudo apt install nginx -y ``
Create a configuration file for Jenkins:
``bash sudo nano /etc/nginx/sites-available/jenkins ``
Paste the following (replace 'jenkins.example.com' with your domain):
```nginx server { listen 80; server_name jenkins.example.com;
location / { proxy_pass http://127.0.0.1:8080; 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_max_temp_file_size 0; proxy_connect_timeout 150; proxy_send_timeout 100; proxy_read_timeout 100; proxy_buffer_size 8k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; proxy_http_version 1.1; proxy_request_buffering off; } } ```
Enable the site and restart Nginx:
``bash sudo ln -s /etc/nginx/sites-available/jenkins /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx ``
Gotcha: Jenkins must listen on 127.0.0.1, not 0.0.0.0. Edit /etc/default/jenkins and set JENKINS_LISTEN_ADDRESS=127.0.0.1. Then restart Jenkins: sudo systemctl restart jenkins. If you skip this, Jenkins will still be accessible directly via port 8080, bypassing Nginx.
Production insight: For high-traffic Jenkins, tune Nginx worker processes and buffer sizes. I once had a Jenkins that served 500+ concurrent users and the default buffer sizes caused frequent 502 errors. Increasing proxy_buffers solved it.
7. Securing with Let's Encrypt SSL
Let's Encrypt provides free SSL certificates. Use certbot to obtain and auto-renew certificates.
Install certbot:
``bash sudo apt install certbot python3-certbot-nginx -y ``
Obtain a certificate:
``bash sudo certbot --nginx -d jenkins.example.com ``
Follow the prompts. Certbot will modify the Nginx configuration to add SSL.
After completion, verify the auto-renewal:
``bash sudo certbot renew --dry-run ``
Gotcha: Certbot requires port 80 to be open for the HTTP-01 challenge. Ensure UFW allows 80/tcp. Also, the domain must point to your server's IP. I once had a renewal fail because the DNS record was pointing to an old IP.
Production insight: Set up a cron job for renewal (certbot adds one by default). Check it with sudo systemctl status certbot.timer. I've seen certificates expire because the timer was disabled during a system update.
sudo systemctl list-timers | grep certbot after setup.8. Completing the Plugin Installation Wizard
After accessing Jenkins via the browser (https://jenkins.example.com), you'll see the 'Unlock Jenkins' page. Enter the initial admin password from step 4.
Next, the plugin installation wizard appears. Choose 'Install suggested plugins' for a standard setup. This installs common plugins like Git, Pipeline, and Blue Ocean.
Gotcha: The plugin installation may fail if the server has no internet access or if plugins are incompatible. I've seen the wizard hang on 'Installing plugins' for hours. In that case, restart Jenkins and try again. You can also skip plugin installation and install manually later.
If the wizard fails, you can install plugins via the CLI or by uploading .hpi files. For production, I recommend installing only necessary plugins to reduce attack surface.
Production insight: In a corporate environment behind a proxy, you need to configure proxy settings in Jenkins before plugins can be downloaded. Go to 'Manage Jenkins' > 'Manage Plugins' > 'Advanced' and set the HTTP proxy.
9. Creating the First Admin User
After plugin installation, Jenkins prompts you to create the first admin user. Fill in the details: username, password, full name, and email.
Gotcha: The password must be strong (min 8 characters, mixed case, numbers). Jenkins will reject weak passwords. If you're automating, use a script to set the password via the REST API.
Once created, you'll be logged in as the admin user. The initial admin password file is now removed.
Production insight: Never use the default 'admin' username. Use a specific name like 'jenkins-admin' to avoid confusion. Also, enable security matrix or role-based strategy later for fine-grained access control.
10. Managing Jenkins as a Systemd Service
Jenkins runs as a systemd service. Common management commands:
``bash sudo systemctl status jenkins sudo systemctl start jenkins sudo systemctl stop jenkins sudo systemctl restart jenkins sudo systemctl enable jenkins ``
Logs are at /var/log/jenkins/jenkins.log. Use journalctl -u jenkins -f for real-time logs.
Gotcha: If you modify /etc/default/jenkins, restart the service. Also, the service user is 'jenkins'. Ensure it has proper permissions on /var/lib/jenkins.
Production insight: For high availability, consider running multiple Jenkins instances behind a load balancer. But that's advanced. For a single instance, monitor the service with a tool like Monit or systemd's watchdog.
11. Troubleshooting Common Installation Failures
Here are the most common failures and their fixes:
Failure 1: APT key error Symptom: The following signatures couldn't be verified because the public key is not available Fix: Re-add the key as shown in section 3.
Failure 2: Jenkins fails to start Symptom: Active: failed in systemctl status Check: Java version, port conflicts, disk space. Run sudo journalctl -u jenkins -n 50.
Failure 3: Can't access Jenkins on port 8080 Check: UFW rules, cloud firewall, Jenkins listening address. Use sudo netstat -tulpn | grep 8080.
Failure 4: Plugin installation hangs Fix: Restart Jenkins and try again. Or skip and install manually.
Failure 5: SSL certificate not trusted Fix: Ensure certbot ran successfully. Check certificate chain: sudo openssl s_client -connect jenkins.example.com:443.
Production insight: Always check /var/log/jenkins/jenkins.log first. 90% of issues are logged there. The other 10% require checking system logs or Nginx error logs.
12. Post-Installation Hardening and Next Steps
After a successful installation, harden Jenkins:
- Enable security: Go to 'Manage Jenkins' > 'Configure Global Security'. Choose 'Jenkins’ own user database' and 'Logged-in users can do anything' (or use matrix-based security).
- Install recommended plugins: Credentials Binding, Pipeline, Git, Blue Ocean.
- Configure backup: Back up /var/lib/jenkins regularly.
- Set up monitoring: Use Prometheus plugin to expose metrics.
- Enable CSRF protection (enabled by default).
- Disable CLI over HTTP if not needed.
Next steps: Create a pipeline job, connect to a Git repository, and set up build agents.
Gotcha: Default Jenkins has no authentication for API access. Use an API token for scripted access.
Production insight: For a production Jenkins, never run it as root. The Jenkins service runs as 'jenkins' user. Also, restrict access to /var/lib/jenkins with chmod 700.
The Java 11 Incident: Jenkins 2.440 Refuses to Start
- Always verify Java version before upgrading Jenkins.
- Use 'java -version' and check Jenkins release notes for minimum Java requirements.
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo gpg --dearmor -o /usr/share/keyrings/jenkins-keyring.asc.gpgecho 'deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc.gpg] https://pkg.jenkins.io/debian-stable binary/' | sudo tee /etc/apt/sources.list.d/jenkins.listKey takeaways
Interview Questions on This Topic
What Java version is required for Jenkins 2.440+?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.
That's Jenkins. Mark it forged?
7 min read · try the examples if you haven't