Article

How to Deploy a Spring Boot JAR as a Linux Service on Ubuntu
Learn how to deploy a Spring Boot JAR on Ubuntu as a systemd service, configure automatic startup and restarts, and monitor application logs with journalctl.
Stop running java -jar by hand. Let systemd start, restart, and monitor your app for you.
Building a Spring Boot app is the easy part. Keeping it running on a server is where people get stuck. If you start your app with java -jar myapp.jar over SSH, it stops as soon as your session closes, and it won't come back after a reboot or a crash.
The fix is to run it as a systemd service. systemd is the service manager built into Ubuntu, and with it your app can:
Start automatically when the server boots
Restart itself if it crashes
Be managed with simple
systemctlcommandsSend its logs to
journalctl
In this guide, you'll go from a built JAR to a running, auto-starting service.
Prerequisites
An Ubuntu server with
sudoaccessA Spring Boot project that builds with Maven
Basic comfort with the terminal
Step 1: Build the JAR
From your project root, run:
bash
./mvnw clean packageIf Maven is installed globally, use mvn clean package instead. The JAR will appear in the target/ folder:
target/myapp-0.0.1-SNAPSHOT.jarTest it before creating the service:
bash
java -jar target/myapp-0.0.1-SNAPSHOT.jarIf it starts cleanly, stop it with Ctrl + C.
Step 2: Copy the JAR to the server
Create a directory for your app under /opt, which is the standard place for manually installed software:
bash
sudo mkdir -p /opt/myapp
sudo cp target/myapp-0.0.1-SNAPSHOT.jar /opt/myapp/myapp.jarYour app now lives at /opt/myapp/myapp.jar. If you built the JAR on your own machine, use scp to upload it to the server first.
Step 3: Make sure Java is installed
bash
java -versionIf Java is missing, install a JDK:
bash
sudo apt update
sudo apt install openjdk-17-jdkUse a Java version that matches what your Spring Boot app was built for.
Step 4: Create a dedicated user
Running your app as root means a bug or vulnerability in it could give an attacker full control of the server. A dedicated user with limited permissions is safer:
bash
sudo useradd -r -s /usr/sbin/nologin myapp
sudo chown -R myapp:myapp /opt/myappStep 5: Create the systemd service file
Create the file:
bash
sudo nano /etc/systemd/system/myapp.servicePaste in:
ini
[Unit]
Description=My Spring Boot Application
After=network.target
[Service]
User=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -jar /opt/myapp/myapp.jar
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetWhat each line does:
Descriptionis a human-readable name for the service.After=network.targetstarts the app only after networking is up.Useris the account the app runs as.WorkingDirectoryis the folder the app treats as its current directory.ExecStartis the command that launches your app. Runwhich javato confirm the path on your server, and use that path if it differs.Restart=alwaysrestarts the app whenever the process stops.RestartSec=10waits 10 seconds before each restart attempt.WantedBy=multi-user.targetlets the service start on a normal system boot.
Step 6: Reload systemd
Whenever you create or edit a service file, reload systemd's configuration:
bash
sudo systemctl daemon-reloadStep 7: Start the service
bash
sudo systemctl start myapp
sudo systemctl status myappIf everything is set up correctly, the status output shows the service as active (running).
Step 8: Enable it at boot
Starting the service doesn't make it survive a reboot. To do that, enable it:
bash
sudo systemctl enable myappOr start and enable it in one command:
bash
sudo systemctl enable --now myappStep 9: Check the logs
systemd collects your app's output, so you can read it with journalctl:
bash
sudo journalctl -u myapp # all logs
sudo journalctl -u myapp -f # follow in real time
sudo journalctl -u myapp -n 100 # last 100 linesPress Ctrl + C to stop following.
Deploying a new version
Once the service is set up, each new release takes three commands:
bash
sudo cp myapp.jar /opt/myapp/myapp.jar
sudo systemctl restart myapp
sudo systemctl status myappIf something goes wrong, check the last 100 log lines:
bash
sudo journalctl -u myapp -n 100Commands cheat sheet
bash
sudo systemctl start myapp
sudo systemctl stop myapp
sudo systemctl restart myapp
sudo systemctl status myapp
sudo systemctl enable myapp
sudo systemctl disable myapp
sudo systemctl daemon-reload
sudo journalctl -u myappTroubleshooting
Service fails immediately: run
sudo journalctl -u myapp -n 100and read the error. A wrong Java path or an unsupported Java version are common causes.Permission denied: make sure the
myappuser owns/opt/myapp(sudo chown -R myapp:myapp /opt/myapp).Port already in use: another process is using your app's port. Check with
sudo ss -tulpn | grep 8080.Changes to the service file have no effect: run
sudo systemctl daemon-reload, then restart the service.
Conclusion
Running your Spring Boot app as a systemd service turns it from a command you babysit into a proper Linux service. It starts on boot, recovers from crashes, and gives you clean logs through journalctl, all with commands you'll use every day.
From here, you could put Nginx in front of the app as a reverse proxy, add HTTPS with Let's Encrypt, or automate these deployment steps with a CI/CD pipeline.