How to Deploy a Spring Boot Application on RHEL 7

Spring Boot has become the de facto standard for building production-ready Java microservices and web applications. Its embedded server model — packaging the entire application, including Tomcat or Jetty, into a single executable JAR — greatly simplifies deployment on Linux servers. Instead of managing a standalone application server, you run a self-contained JAR file as a system service. This guide walks through building a Spring Boot application with Maven, deploying it on RHEL 7, managing it as a systemd service, reverse-proxying it through Nginx, and monitoring its health endpoint.

Prerequisites

  • RHEL 7 with root or sudo access
  • Java 8 or Java 11 installed and JAVA_HOME set
  • Apache Maven installed (see the Maven installation guide)
  • Nginx available or installable via yum
  • A Spring Boot application project (or the sample shown below)

Step 1: Create and Build the Spring Boot Application

If you do not already have a Spring Boot project, generate a minimal one. The simplest approach is to use the Spring Initializr web interface at https://start.spring.io and download a ZIP, or create the structure manually.

For this guide, assume your project directory is /opt/myapp/myapp-src and contains a standard Maven pom.xml with the Spring Boot parent and the Web starter dependency:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.7.18</version>
</parent>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

Build the executable JAR:

cd /opt/myapp/myapp-src
mvn clean package -DskipTests

This produces target/myapp-0.0.1-SNAPSHOT.jar. The spring-boot-maven-plugin repackages the artifact into a fat JAR containing all dependencies and the embedded Tomcat server.

Step 2: Prepare the Deployment Directory and User

Running a service as root is a security risk. Create a dedicated system user that owns only the application files and cannot log in interactively:

sudo useradd -r -s /sbin/nologin springapp
sudo mkdir -p /opt/myapp/current
sudo cp target/myapp-0.0.1-SNAPSHOT.jar /opt/myapp/current/myapp.jar
sudo chown -R springapp:springapp /opt/myapp
sudo chmod 750 /opt/myapp/current
sudo chmod 640 /opt/myapp/current/myapp.jar

Step 3: Configure Application Environment Variables

Spring Boot reads configuration from environment variables and from application.properties. Sensitive values such as database passwords should be supplied as environment variables rather than stored in the JAR. Create a dedicated environment file owned by root with restricted permissions:

sudo tee /opt/myapp/myapp.env <<'EOF'
SPRING_PROFILES_ACTIVE=production
SERVER_PORT=8080
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/mydb
SPRING_DATASOURCE_USERNAME=mydbuser
SPRING_DATASOURCE_PASSWORD=changeme
LOGGING_FILE_NAME=/var/log/myapp/myapp.log
EOF

sudo chown root:springapp /opt/myapp/myapp.env
sudo chmod 640 /opt/myapp/myapp.env

Create the log directory:

sudo mkdir -p /var/log/myapp
sudo chown springapp:springapp /var/log/myapp

Step 4: Create the systemd Service Unit

A systemd unit file manages the application lifecycle — starting on boot, restarting on failure, and stopping cleanly. Spring Boot applications exit with code 143 when they receive SIGTERM (the default signal systemd sends on stop), so the unit must declare that exit code as successful to prevent systemd from marking the stop as a failure.

sudo tee /etc/systemd/system/myapp.service <<'EOF'
[Unit]
Description=My Spring Boot Application
After=network.target
After=syslog.target

[Service]
Type=simple
User=springapp
Group=springapp

EnvironmentFile=/opt/myapp/myapp.env

WorkingDirectory=/opt/myapp/current
ExecStart=/usr/bin/java 
  -Xms256m 
  -Xmx512m 
  -Djava.security.egd=file:/dev/./urandom 
  -jar /opt/myapp/current/myapp.jar

SuccessExitStatus=143

Restart=on-failure
RestartSec=10s

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target
EOF

Key points about this unit file:

  • SuccessExitStatus=143 — tells systemd that exit code 143 (128 + SIGTERM signal 15) is a clean shutdown, not a crash
  • EnvironmentFile — loads environment variables from the file, keeping secrets out of the unit file itself
  • -Djava.security.egd=file:/dev/./urandom — prevents the JVM from blocking on /dev/random entropy during startup, a common issue on virtual machines
  • Restart=on-failure — automatically restarts if the process crashes but not on clean stops

Step 5: Enable and Start the Service

sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp

Expected output from status:

● myapp.service - My Spring Boot Application
   Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2026-05-17 10:00:00 UTC; 5s ago
 Main PID: 12345 (java)
   CGroup: /system.slice/myapp.service
           └─12345 /usr/bin/java -Xms256m -Xmx512m ...

May 17 10:00:02 host myapp[12345]: Started MyApplication in 4.231 seconds

View live logs:

sudo journalctl -u myapp -f

Step 6: Configure Nginx as a Reverse Proxy

It is best practice not to expose Spring Boot’s embedded Tomcat directly on port 80 or 443. Instead, run Nginx in front as a reverse proxy. This allows Nginx to handle SSL termination, serve static files efficiently, and rate-limit requests.

Install Nginx:

sudo yum install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Create a server block configuration for your application:

sudo tee /etc/nginx/conf.d/myapp.conf <<'EOF'
upstream springboot_backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

server {
    listen 80;
    server_name myapp.example.com;

    access_log  /var/log/nginx/myapp.access.log;
    error_log   /var/log/nginx/myapp.error.log;

    location / {
        proxy_pass         http://springboot_backend;
        proxy_http_version 1.1;
        proxy_set_header   Connection        "";
        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_connect_timeout 10s;
        proxy_read_timeout    60s;
    }

    # Block external access to Actuator management endpoints
    location /actuator {
        deny all;
    }
}
EOF

Test the configuration and reload:

sudo nginx -t
sudo systemctl reload nginx

Step 7: Verify the Health Endpoint

Spring Boot Actuator exposes a /actuator/health endpoint that reports the application status. Check it directly on the Spring Boot port (not through Nginx, since Actuator is blocked externally):

curl -s http://localhost:8080/actuator/health | python -m json.tool

A healthy application returns:

{
    "status": "UP"
}

If the application connects to a database or other services, their statuses appear under the components key. A DOWN status indicates a dependency is unreachable.

Step 8: Log Configuration

Spring Boot uses Logback by default. To write logs to a rotating file, add these properties to src/main/resources/application.properties:

logging.file.name=/var/log/myapp/myapp.log
logging.file.max-size=50MB
logging.file.max-history=14
logging.level.root=INFO
logging.level.com.example=DEBUG
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n

Rebuild and redeploy the JAR after changing application properties:

mvn clean package -DskipTests
sudo cp target/myapp-0.0.1-SNAPSHOT.jar /opt/myapp/current/myapp.jar
sudo systemctl restart myapp

Conclusion

Deploying a Spring Boot application on RHEL 7 involves packaging the application as a self-contained JAR, creating a non-privileged system user to run it, writing a robust systemd service unit with the correct SuccessExitStatus=143 setting, and placing Nginx in front as a reverse proxy. This architecture is production-ready: the service starts automatically on reboot, restarts on unexpected crashes, and its management endpoints are shielded from public access. By externalising configuration through an environment file, you keep secrets out of your application binaries and version-controlled configuration.