Stop Using nohup java -jar: Three Production-Grade Ways to Daemonize Spring Boot
In the development environment, we are used to casually typing a line:
nohup java -jar my-app.jar &
This indeed runs. But in a production environment, this approach is like walking a tightrope over a high-voltage line.
Why nohup & Doesn't Work?
nohup plus & only puts the process in the background and ignores the hangup signal; it is fundamentally not a process management solution. Using this set in production, you will frequently step on these landmines:
- Chaotic Process Management: Want to restart? First
psto find the PID, thenkill. A slip of the hand might accidentally kill other services. - No Auto-Start on Boot: The server restarts due to a failure, the service won't recover automatically. Being woken up by alarm calls in the middle of the night is the norm.
- Out-of-Control Permissions: Running directly as
rootfor convenience carries an extremely high security risk. - Chaotic Log Management: Log files have no rotation, and filling up the disk is only a matter of time.
Behind the eight characters "Deploy SpringBoot project on Linux" is actually a practical chain spanning four dimensions: development, operations, security, and performance. You need to use systemd for service supervision, not rely on
nohup &to tough it out.
Three production-grade solutions are introduced below, sorted by recommendation level.
Solution 1: Systemd (Most Recommended)
systemd is the standard service manager for modern Linux distributions (CentOS 7+, Ubuntu 16.04+). Using it to manage Spring Boot applications requires no additional software installation, yet its capabilities are far more powerful than nohup.
Step 1: Create a Dedicated Account (Security First)
Absolutely do not run business code as the root user:
# Create a user without login permissions
sudo useradd -r -s /bin/false app_user
# Ensure this user has read/write permissions for the JAR package
sudo chown app_user:app_user /opt/myapp/app.jar
Step 2: Write the Service File
Create a service configuration file at /etc/systemd/system/myapp.service:
[Unit]
Description=My Spring Boot Application
After=syslog.target network.target
[Service]
# Specify the running user to achieve permission isolation
User=app_user
Group=app_user
# Core startup command (recommend specifying the full Java path and JVM parameters)
ExecStart=/usr/bin/java -Xms512m -Xmx512m -XX:+UseG1GC \
-jar /opt/myapp/app.jar \
--spring.profiles.active=prod
# Spring Boot graceful shutdown: 143 represents a normal exit via SIGTERM signal
SuccessExitStatus=143
# Auto-restart on crash
Restart=always
RestartSec=10
# Logs are managed by systemd, view with journalctl
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myapp
[Install]
WantedBy=multi-user.target
Step 3: Activation and Management
# Reload configuration
sudo systemctl daemon-reload
# Set to auto-start on boot (say goodbye to restart anxiety from now on)
sudo systemctl enable myapp
# Start the service
sudo systemctl start myapp
# Check status
sudo systemctl status myapp
# View logs
sudo journalctl -u myapp -f
What If You Don't Have Root Privileges?
Many internal network environments in banks and state-owned enterprises only have regular accounts without sudo privileges. In this case, you can use systemd's User Mode:
# Create a user-level service file
mkdir -p ~/.config/systemd/user/
# Place the .service file in this directory, with the same content but remove the User/Group fields
# Enable and start
systemctl --user enable myapp
systemctl --user start myapp
# Key: Enable linger so the service continues running after the user logs out
# This requires the administrator to execute once:
sudo loginctl enable-linger <username>
Solution 2: Supervisor
If your system is still using an older version like CentOS 6, or you need a lightweight, cross-language process management tool, Supervisor is a very good choice.
Installation and Configuration
# Ubuntu/Debian
sudo apt install supervisor
# CentOS
sudo yum install supervisor
Create the configuration at /etc/supervisor/conf.d/myapp.conf:
[program:myapp]
command=/usr/bin/java -Xms512m -Xmx512m -jar /opt/myapp/app.jar --spring.profiles.active=prod
directory=/opt/myapp
user=app_user
autostart=true
autorestart=true
startsecs=10
stopasgroup=true
killasgroup=true
stopwaitsecs=30
redirect_stderr=true
stdout_logfile=/var/log/myapp/out.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
environment=JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
Several key configurations must be noted:
commandmust use an absolute path for the Java command; Supervisor does not inherit the system's$PATH.autorestart=true+startsecs=10: Tells Supervisor this is a long-running service, don't kill it just because startup is slow.stopasgroup=true+killasgroup=true: Ensures the entire process group can be killed, otherwise child threads from the thread pool or Netty might remain.
Common Management Commands
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start myapp
sudo supervisorctl status
Note: On CentOS 7+, systemd has become the standard, and Supervisor is gradually being deprecated in some distributions. For new projects, prioritize systemd.
Solution 3: Docker + Kubernetes (The Cloud-Native Era Choice)
If your team has already embarked on the path of containerization, Docker + K8s is a more modern solution.
Dockerfile Example
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/myapp.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Xms512m", "-Xmx512m", "-jar", "app.jar"]
Spring Boot 3.x provides good support for containerization, making containerized deployment very simple.
Advantages of Kubernetes
K8s itself is a production-grade process supervision system:
- Auto-Restart: Pods are automatically recreated if they die.
- Rolling Updates: Zero-downtime releases.
- Health Checks: liveness probe + readiness probe.
- Horizontal Scaling: Automatically scales based on load.
Docker solves environment consistency, K8s handles automated operations.
Production Environment Checklist
No matter which solution you choose, you must do the following:
| Check Item | Description |
|---|---|
| Dedicated Run User | Absolutely do not run applications as root |
| JVM Parameter Tuning | Specify -Xms, -Xmx, GC strategy, don't use defaults |
| Specify Config File | Use --spring.config.location to externalize configuration, don't package it into the JAR |
| Graceful Shutdown | SuccessExitStatus=143 allows Spring Boot to properly execute @PreDestroy |
| Log Management | Configure log rotation (Logback's RollingPolicy) to prevent disk from filling up |
| Auto-Start on Boot | systemctl enable or autostart=true |
| Firewall | Confirm the security group and system firewall have opened the application port |
| Java Version Match | Spring Boot 2.7.x uses JDK 8/17, 3.x mandates JDK 17+ |
Summary
| Solution | Applicable Scenario | Advantages | Disadvantages |
|---|---|---|---|
| Systemd | Most Linux production environments | Native integration, powerful, no install needed | Linux only |
| Supervisor | Older systems, cross-language scenarios | Lightweight, flexible config | Gradually deprecated on CentOS 7+ |
| Docker + K8s | Cloud-native, microservice architecture | Consistent environment, automated ops | High learning curve, high infrastructure requirements |
One-sentence suggestion: If your server is CentOS 7+ or Ubuntu 16.04+, use systemd directly; if the company has already adopted containers, go with Docker + K8s; only consider Supervisor for special circumstances.
Stop letting nohup java -jar, a development-phase toy, run in the production environment. What production needs is not "it can run," but "running rock-solid for 365 days."
Top 1 from juejin.cn, machine-translated. The original thread is authoritative.
1