Skip to main content

Deploy Spring Boot on Ubuntu (production guide)

TL;DR

Complete guide to deploying a Spring Boot JAR on Ubuntu with systemd, nginx, and JVM tuning for production.

Key facts

Topology
Spring Boot on Ubuntu

TL;DR

This guide covers deploying a Spring Boot application as a standalone JAR on Ubuntu with systemd, nginx, and basic JVM tuning for production.

Prerequisites

  • Ubuntu 24.04 LTS with root or sudo access
  • Java 21 (LTS) or Java 17 (LTS)
  • Your Spring Boot application packaged as an executable JAR

Step 1 — Install Java

sudo apt update && sudo apt upgrade -y
sudo apt install -y openjdk-21-jre-headless
java -version

Step 2 — Deploy the JAR

Create a dedicated service user and directory:

sudo useradd -r -s /bin/false springapp
sudo mkdir -p /opt/springapp
sudo chown springapp:springapp /opt/springapp

Copy your JAR and configuration:

sudo cp myapp-1.0.0.jar /opt/springapp/app.jar
sudo cp application-production.yml /opt/springapp/
sudo chown springapp:springapp /opt/springapp/*

Step 3 — Systemd service

Create /etc/systemd/system/springapp.service:

[Unit]
Description=Spring Boot Application
After=network.target

[Service]
User=springapp
Group=springapp
WorkingDirectory=/opt/springapp
ExecStart=/usr/bin/java \
    -Xms512m -Xmx1024m \
    -XX:+UseG1GC \
    -XX:MaxGCPauseMillis=200 \
    -Dspring.profiles.active=production \
    -jar app.jar
Restart=always
RestartSec=10
SuccessExitStatus=143
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start springapp
sudo systemctl enable springapp

Verify the application started:

sudo journalctl -u springapp -f
curl http://localhost:8080/actuator/health

Step 4 — nginx reverse proxy

server {
    listen 80;
    server_name 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_read_timeout 60s;
    }
}
sudo ln -s /etc/nginx/sites-available/springapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Step 5 — SSL and firewall

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
sudo ufw enable

JVM tuning notes

  • -Xms / -Xmx: Set initial and max heap to the same value in production to avoid resize pauses
  • -XX:+UseG1GC: G1 is the default in Java 21 and handles most workloads well
  • Spring Boot Actuator: Enable /actuator/health and /actuator/metrics for production monitoring

With Reflex

Reflex monitors your Spring Boot process via Actuator endpoints and systemd status. It tracks JVM heap usage, garbage collection pauses, HTTP error rates, and system resources. When issues arise, Reflex can restart the service, verify health through the Actuator endpoint, and alert your team with correlated diagnostics. See How it works.