ChatGPT解决这个技术问题 Extra ChatGPT

Spring Boot application as a Service

How to configure nicely Spring Boot application packaged as executable jar as a Service in the Linux system? Is this recommended approach, or should I convert this app to war and install it into Tomcat?

Currently, I can run Spring boot application from the screen session, which is nice but requires manual start after a server reboot.

What I'm looking for is general advice/direction or sample init.d the script, if my approach with executable jar is proper.

To get started, does your distribution use upstart or systemd ?

D
Daniel

The following works for springboot 1.3 and above:

As init.d service

The executable jar has the usual start, stop, restart, and status commands. It will also set up a PID file in the usual /var/run directory and logging in the usual /var/log directory by default.

You just need to symlink your jar into /etc/init.d like so

sudo link -s /var/myapp/myapp.jar /etc/init.d/myapp

OR

sudo ln -s ~/myproject/build/libs/myapp-1.0.jar /etc/init.d/myapp_servicename

After that you can do the usual

/etc/init.d/myapp start

Then setup a link in whichever runlevel you want the app to start/stop in on boot if so desired.

As a systemd service

To run a Spring Boot application installed in var/myapp you can add the following script in /etc/systemd/system/myapp.service:

[Unit]
Description=myapp
After=syslog.target

[Service]
ExecStart=/var/myapp/myapp.jar

[Install]
WantedBy=multi-user.target

NB: in case you are using this method, do not forget to make the jar file itself executable (with chmod +x) otherwise it will fail with error "Permission denied".

Reference

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/deployment-install.html#deployment-service


How does the "fully executable JAR" approach work? I use CentOS 6.6. I added <executable>true</executable> to my pom.xml, but the packaged JAR file does not execute (... ./myapp.jar ... cannot execute binary file.)
This answer only works for the current 1.3 Milestone, which is not released yet. 1.1 and 1.2 branches will need to check the other responses here.
Do you know guys how to pass spring's arguments such as -Dspring.profiles.active=prod to this services? Question - stackoverflow.com/questions/31242291/…
I am not able to stop the spring-boot application. /etc/init.d stop is not stopping the app, its trying to start it again.
If you want to monitor process and restart it if it die without writing system daemons check out patrickgrimard.com/2014/06/06/…
y
yglodt

What follows is the easiest way to install a Java application as system service in Linux.

Let's assume you are using systemd (which any modern distro nowadays does):

Firstly, create a service file in /etc/systemd/system named e.g. javaservice.service with this content:

[Unit]
Description=Java Service

[Service]
User=nobody
# The configuration file application.properties should be here:
WorkingDirectory=/data 
ExecStart=/usr/bin/java -Xmx256m -jar application.jar --server.port=8081
SuccessExitStatus=143
TimeoutStopSec=10
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Secondly, notify systemd of the new service file:

systemctl daemon-reload

and enable it, so it runs on boot:

systemctl enable javaservice.service

Eventually, you can use the following commands to start/stop your new service:

systemctl start javaservice
systemctl stop javaservice
systemctl restart javaservice
systemctl status javaservice

Provided you are using systemd, this is the most non-intrusive and clean way to set up a Java application as system-service.

What I like especially about this solution is the fact that you don't need to install and configure any other software. The shipped systemd does all the work for you, and your service behaves like any other system service. I use it in production for a while now, on various distros, and it just works as you would expect.

Another plus is that, by using /usr/bin/java, you can easily add jvm paramters such as -Xmx256m.

Also read the systemd part in the official Spring Boot documentation: http://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html


Unfortunately systemd is not available for Centos 6
how does it know how to stop it? Records the pid and then kills it?
With Spring Boot 1.3+ you can generate a fully executable war file, so no need for the java -jar ... bit, just use the file's name there.
I prefer using the full java commandline because that way you can add jvm parameters.
For a proper boot sequence you might want to add ordering statements to the [Unit] section, e.g. After=mysql.service, Before=apache2.service.
f
flazzarini

You could also use supervisord which is a very handy daemon, which can be used to easily control services. These services are defined by simple configuration files defining what to execute with which user in which directory and so forth, there are a zillion options. supervisord has a very simple syntax, so it makes a very good alternative to writing SysV init scripts.

Here a simple supervisord configuration file for the program you are trying to run/control. (put this into /etc/supervisor/conf.d/yourapp.conf)

/etc/supervisor/conf.d/yourapp.conf

[program:yourapp]
command=/usr/bin/java -jar /path/to/application.jar
user=usertorun
autostart=true
autorestart=true
startsecs=10
startretries=3
stdout_logfile=/var/log/yourapp-stdout.log
stderr_logfile=/var/log/yourapp-stderr.log

To control the application you would need to execute supervisorctl, which will present you with a prompt where you could start, stop, status yourapp.

CLI

# sudo supervisorctl
yourapp             RUNNING   pid 123123, uptime 1 day, 15:00:00
supervisor> stop yourapp
supervisor> start yourapp

If the supervisord daemon is already running and you've added the configuration for your serivce without restarting the daemon you can simply do a reread and update command in the supervisorctl shell.

This really gives you all the flexibilites you would have using SysV Init scripts, but easy to use and control. Take a look at the documentation.


Finally something worked for me right out of the box. Thanks a lot for the supervisord hint.
This does the same job as systemd, which is built into most current Linux distros.
S
Steve

I just got around to doing this myself, so the following is where I am so far in terms of a CentOS init.d service controller script. It's working quite nicely so far, but I'm no leet Bash hacker, so I'm sure there's room for improvement, so thoughts on improving it are welcome.

First of all, I have a short config script /data/svcmgmt/conf/my-spring-boot-api.sh for each service, which sets up environment variables.

#!/bin/bash
export JAVA_HOME=/opt/jdk1.8.0_05/jre
export APP_HOME=/data/apps/my-spring-boot-api
export APP_NAME=my-spring-boot-api
export APP_PORT=40001

I'm using CentOS, so to ensure that my services are started after a server restart, I have a service control script in /etc/init.d/my-spring-boot-api:

#!/bin/bash
# description: my-spring-boot-api start stop restart
# processname: my-spring-boot-api
# chkconfig: 234 20 80

. /data/svcmgmt/conf/my-spring-boot-api.sh

/data/svcmgmt/bin/spring-boot-service.sh $1

exit 0

As you can see, that calls the initial config script to set up environment variables and then calls a shared script which I use for restarting all of my Spring Boot services. That shared script is where the meat of it all can be found:

#!/bin/bash

echo "Service [$APP_NAME] - [$1]"

echo "    JAVA_HOME=$JAVA_HOME"
echo "    APP_HOME=$APP_HOME"
echo "    APP_NAME=$APP_NAME"
echo "    APP_PORT=$APP_PORT"

function start {
    if pkill -0 -f $APP_NAME.jar > /dev/null 2>&1
    then
        echo "Service [$APP_NAME] is already running. Ignoring startup request."
        exit 1
    fi
    echo "Starting application..."
    nohup $JAVA_HOME/bin/java -jar $APP_HOME/$APP_NAME.jar \
        --spring.config.location=file:$APP_HOME/config/   \
        < /dev/null > $APP_HOME/logs/app.log 2>&1 &
}

function stop {
    if ! pkill -0 -f $APP_NAME.jar > /dev/null 2>&1
    then
        echo "Service [$APP_NAME] is not running. Ignoring shutdown request."
        exit 1
    fi

    # First, we will try to trigger a controlled shutdown using 
    # spring-boot-actuator
    curl -X POST http://localhost:$APP_PORT/shutdown < /dev/null > /dev/null 2>&1

    # Wait until the server process has shut down
    attempts=0
    while pkill -0 -f $APP_NAME.jar > /dev/null 2>&1
    do
        attempts=$[$attempts + 1]
        if [ $attempts -gt 5 ]
        then
            # We have waited too long. Kill it.
            pkill -f $APP_NAME.jar > /dev/null 2>&1
        fi
        sleep 1s
    done
}

case $1 in
start)
    start
;;
stop)
    stop
;;
restart)
    stop
    start
;;
esac
exit 0

When stopping, it will attempt to use Spring Boot Actuator to perform a controlled shutdown. However, in case Actuator is not configured or fails to shut down within a reasonable time frame (I give it 5 seconds, which is a bit short really), the process will be killed.

Also, the script makes the assumption that the java process running the appllication will be the only one with "my-spring-boot-api.jar" in the text of the process details. This is a safe assumption in my environment and means that I don't need to keep track of PIDs.


No need to write your own start/stop script. This is provided as of Spring Boot 1.3 and up. See docs.spring.io/spring-boot/docs/current/reference/htmlsingle/… for more details.
Good to know that's an option, but all it does is remove the need to execute using java -jar. The rest of the script is still needed.
Very useful for when /etc/init.d or systemd is not an option, thanks for sharing.
@Steve: Nope. You are reinventing the wheel. Oh, and we have systemd now.
When you need to pass parameters to JVM (like -javaagent or -D parameters) this is the unique way, tks @Steve !
B
Benjamin M

If you want to use Spring Boot 1.2.5 with Spring Boot Maven Plugin 1.3.0.M2, here's out solution:

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

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>1.3.0.M2</version>
            <configuration>
                <executable>true</executable>
            </configuration>
        </plugin>
    </plugins>
</build>

<pluginRepositories>
    <pluginRepository>
        <id>spring-libs-milestones</id>
        <url>http://repo.spring.io/libs-milestone</url>
    </pluginRepository> 
</pluginRepositories>

Then compile as ususal: mvn clean package, make a symlink ln -s /.../myapp.jar /etc/init.d/myapp, make it executable chmod +x /etc/init.d/myapp and start it service myapp start (with Ubuntu Server)


what about runnable WAR files ? it does not work for me with WAR layout.
Interestingly this works with release 1.3.0.M2, but I got an error when I tried 1.3.0.RC1.
Any idea on how to do this with gradle instead of maven?
When using Gradle, this configuration in done using springBoot { executable = true } block.
@RaduToader : Were you able to execute the WAR file as a service?
v
voor

I know this is an older question, but I wanted to present yet another way which is the appassembler-maven-plugin. Here's the relevant part from my POM that includes a lot of additional option values we found useful:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>appassembler-maven-plugin</artifactId>
    <configuration>
        <generateRepository>true</generateRepository>
        <repositoryLayout>flat</repositoryLayout>
        <useWildcardClassPath>true</useWildcardClassPath>
        <includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath>
        <configurationDirectory>config</configurationDirectory>
        <target>${project.build.directory}</target>
        <daemons>
            <daemon>
                <id>${installer-target}</id>
                <mainClass>${mainClass}</mainClass>
                <commandLineArguments>
                    <commandLineArgument>--spring.profiles.active=dev</commandLineArgument>
                    <commandLineArgument>--logging.config=${rpmInstallLocation}/config/${installer-target}-logback.xml</commandLineArgument>
                </commandLineArguments>
                <platforms>
                    <platform>jsw</platform>
                </platforms>
                <generatorConfigurations>
                    <generatorConfiguration>
                        <generator>jsw</generator>
                        <includes>
                            <include>linux-x86-64</include>
                        </includes>
                        <configuration>
                            <property>
                                <name>wrapper.logfile</name>
                                <value>logs/${installer-target}-wrapper.log</value>
                            </property>
                            <property>
                                <name>wrapper.logfile.maxsize</name>
                                <value>5m</value>
                            </property>
                            <property>
                                <name>run.as.user.envvar</name>
                                <value>${serviceUser}</value>
                            </property>
                            <property>
                                <name>wrapper.on_exit.default</name>
                                <value>RESTART</value>
                            </property>
                        </configuration>
                    </generatorConfiguration>
                </generatorConfigurations>
                <jvmSettings>
                    <initialMemorySize>256M</initialMemorySize>
                    <maxMemorySize>1024M</maxMemorySize>
                    <extraArguments>
                        <extraArgument>-server</extraArgument>
                    </extraArguments>
                </jvmSettings>
            </daemon>
        </daemons>
    </configuration>
    <executions>
        <execution>
            <id>generate-jsw-scripts</id>
            <phase>package</phase>
            <goals>
                <goal>generate-daemons</goal>
            </goals>
        </execution>
    </executions>
</plugin>

A
Arundev

AS A WINDOWS SERVICE

If you want this to run in windows machine download the winsw.exe from

 http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/2.1.2/

After that rename it to jar filename (eg: your-app.jar)

winsw.exe -> your-app.exe

Now create an xml file your-app.xml and copy the following content to that

<?xml version="1.0" encoding="UTF-8"?>
<service>
     <id>your-app</id>
     <name>your-app</name>
     <description>your-app as a Windows Service</description>
     <executable>java</executable>
     <arguments>-jar "your-app.jar"</arguments>
     <logmode>rotate</logmode>
</service>

Make sure that the exe and xml along with jar in a same folder.

After this open command prompt in Administrator previlege and install it to the windows service.

your-app.exe install
eg -> D:\Springboot\your-app.exe install

If it fails with

Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion' has value '1.8', but '1.7' is required.

Then try the following:

Delete java.exe, javaw.exe and javaws.exe from C:\Windows\System32

thats it :) .

To uninstall the service in windows

your-app.exe uninstall

For see/run/stop service: win+r and type Administrative tools then select the service from that. Then right click choose the option - run / stop


I have followed the same steps to run spring boot jar as windows service in company's intranet env, but the service is not getting up. There is a window coming out with error: Error:1067 The process terminated unexpectedly Could you please help or suggest what need to be done?
Do you have all the permission to do that? If you are an administrator this won't cause any issue. Can u please check you have administrator rights.
stackoverflow.com/questions/18205111/… can u please try this may be this will help u to resolve the issue.
thanks for the quick response, I got my service up and running by correcting an issue with the tag in xml file.
U
User0

Here is a script that deploys an executable jar as a systemd service.

It creates a user for the service and the .service file, and place the jar file under /var, and makes some basic lock down of privileges.

#!/bin/bash

# Argument: The jar file to deploy
APPSRCPATH=$1

# Argument: application name, no spaces please, used as folder name under /var
APPNAME=$2

# Argument: the user to use when running the application, may exist, created if not exists
APPUSER=$3

# Help text
USAGE="
Usage: sudo $0 <jar-file> <app-name> <runtime-user>
If an app with the name <app-name> already exist, it is stopped and deleted.
If the <runtime-user> does not already exist, it is created.
"

# Check that we are root
if [ ! "root" = "$(whoami)" ]; then
    echo "Must be root. Please use e.g. sudo"
    echo "$USAGE"
    exit
fi

# Check arguments
if [ "$#" -ne 3 -o ${#APPSRCPATH} = 0 -o ${#APPNAME} = 0 -o ${#APPUSER} = 0 ]; then
    echo "Incorrect number of parameters."
    echo "$USAGE"
    exit
fi

if [ ! -f $APPSRCPATH ]; then
    echo "Can't find jar file $APPSRCPATH"
    echo "$USAGE"
    exit
fi

# Infered values
APPFILENAME=$(basename $APPSRCPATH)
APPFOLDER=/var/javaapps/$APPNAME
APPDESTPATH=$APPFOLDER/$APPFILENAME

# Stop the service if it already exist and is running
systemctl stop $APPNAME >/dev/null 2>&1

# Create the app folder, deleting any previous content
rm -fr $APPFOLDER
mkdir -p $APPFOLDER

# Create the user if it does not exist
if id "$APPUSER" >/dev/null 2>&1; then
    echo "Using existing user $APPUSER"
else
    adduser --disabled-password --gecos "" $APPUSER
    echo "Created user $APPUSER"
fi

# Place app in app folder, setting owner and rights
cp $APPSRCPATH $APPDESTPATH
chown $APPUSER $APPDESTPATH
chmod 500 $APPDESTPATH
echo "Added or updated the $APPDESTPATH file"

# Create the .service file used by systemd
echo "
[Unit]
Description=$APPNAME
After=syslog.target
[Service]
User=$APPUSER
ExecStart=/usr/bin/java -jar $APPDESTPATH
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
" > /etc/systemd/system/$APPNAME.service
echo "Created the /etc/systemd/system/$APPNAME.service file"

# Reload the daemon
systemctl daemon-reload

# Start the deployed app
systemctl start $APPNAME
systemctl status $APPNAME

https://i.stack.imgur.com/N8JXA.png


The issue with deleting the old directory contents of that you could end up deleting configuration files or other important files. But good script nonetheless
M
MariuszS

My SysVInit script for Centos 6 / RHEL (not ideal yet). This script requires ApplicationPidListener.

Source of /etc/init.d/app

#!/bin/sh
#
# app Spring Boot Application 
#
# chkconfig:   345 20 80
# description: App Service
#           

### BEGIN INIT INFO
# Provides: App
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 3 4 5 
# Default-Stop: 0 1 2 6
# Short-Description: Application
# Description:      
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

exec="/usr/bin/java"
prog="app"
app_home=/home/$prog/
user=$prog

[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog

lockfile=/var/lock/subsys/$prog    
pid=$app_home/$prog.pid

start() {

    [ -x $exec ] || exit 5
    [ -f $config ] || exit 6
    # Check that networking is up.
    [ "$NETWORKING" = "no" ] && exit 1
    echo -n $"Starting $prog: "
    cd $app_home
    daemon --check $prog --pidfile $pid --user $user $exec $app_args &
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    killproc -p $pid $prog
    retval=$?
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    stop
    start
}

reload() {
    restart
}

force_reload() {
    restart
}

rh_status() {
    status -p $pid $prog
}

rh_status_q() {
    rh_status >/dev/null 2>&1
}

case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
        restart
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
        exit 2
esac
exit $?

Sample config file /etc/sysconfig/app:

exec=/opt/jdk1.8.0_05/jre/bin/java

user=myuser
app_home=/home/mysuer/

app_args="-jar app.jar"

pid=$app_home/app.pid

V
Vorsprung

I am trying to make springboot applications that are presented as a "init.d" style shell script with a compressed java application tacked on the end

By symlinking these scripts from /etc/init.d/spring-app to /opt/spring-app.jar and chmod'ing the jar to be executable it is possible to make "/etc/init.d/spring-app start" "/etc/init.d/spring-app stop" and other possibilities like status work

Presumably, as the init.d style scripts from springboot look that they have the neccessary magic strings ( like # Default-Start: 2 3 4 5) chkconfig would be able to add it as a "service"

But I wanted to get it to work with systemd

To make this work I tried many of the recipies in the other answers above but none of them worked for me on Centos 7.2 with Springboot 1.3 Mostly they would start the service but not be able to track the pid

In the end I found the following did work for me, when the /etc/init.d link was in place also. A file similar to the one below should be installed as /usr/lib/systemd/system/spring-app.service

[Unit]
Description=My loverly application
After=syslog.target 

[Service]
Type=forking
PIDFile=/var/run/spring-app/spring-app.pid
ExecStart=/etc/init.d/spring-app start
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

R
Radu Toader

I ended up doing systemd service for WAR/JAR layout

I'm calling java -jar because its more flexible. Tried also putting the ExecStart=spring-mvc.war but even though is executable, I got 'Exec format error'

Anyway these days, systemd is present on all distros, and offers a nice solution to redirect logs(syserr is important when you service doesn't even start log4j file location will be empty :) ).

cat /etc/systemd/system/spring-mvc.service 
[Unit]
Description=Spring MVC Java Service

[Service]
User=spring-mvc
# The configuration file application.properties should be here:
WorkingDirectory=/usr/local/spring-mvc


# Run ExecStartPre with root-permissions
PermissionsStartOnly=true

ExecStartPre=-/bin/mkdir -p /var/log/spring-mvc


ExecStartPre=/bin/chown -R spring-mvc:syslog /var/log/spring-mvc
ExecStartPre=/bin/chmod -R 775 /var/log/spring-mvc



#https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
ExecStart=/usr/bin/java \
        -Dlog4j.configurationFile=log4j2-spring.xml \
        -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \
        -Dspring.profiles.active=dev \
        -Denvironment-type=dev \
        -XX:+UseConcMarkSweepGC \
        -XX:CMSInitiatingOccupancyFraction=80 \
        -XX:NewSize=756m \
        -XX:MetaspaceSize=256m \
        -Dsun.net.inetaddr.ttl=5 \
        -Xloggc:/var/log/spring-mvc/gc.log \
        -verbose:gc \
        -verbosegc \
        -XX:+DisableExplicitGC \
        -XX:+PrintGCDetails \
        -XX:+PrintGCDateStamps \
        -XX:+PreserveFramePointer \
        -XX:+StartAttachListener \
        -Xms1024m \
        -Xmx1024m \
        -XX:+HeapDumpOnOutOfMemoryError \
        -jar spring-mvc.war

SuccessExitStatus=143
StandardOutput=journal
StandardError=journal


KillSignal=SIGINT
TimeoutStopSec=20
Restart=always
RestartSec=5
StartLimitInterval=0
StartLimitBurst=10

LimitNOFILE=500000
LimitNPROC=500000

#https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LimitCPU=
#LimitCPU=, LimitFSIZE=, LimitDATA=, LimitSTACK=, LimitCORE=, LimitRSS=, LimitNOFILE=, LimitAS=, LimitNPROC=, LimitMEMLOCK=, LimitLOCKS=, LimitSIGPENDING=, LimitMSGQUEUE=, LimitNICE=, LimitRTPRIO=, LimitRTTIME=¶

SyslogIdentifier=spring-mvc

[Install]
WantedBy=multi-user.target


# https://www.freedesktop.org/software/systemd/man/journalctl.html
#check logs --- journalctl -u spring-mvc -f -o cat

rsyslog - redirect syslog input from app to specific folder/file

cat /etc/rsyslog.d/30-spring-mvc.conf 
if $programname == 'spring-mvc' then /var/log/spring-mvc/spring-mvc.log
& stop

logrotate

cat /etc/logrotate.d/spring-mvc.conf 
/var/log/spring-mvc/spring-mvc.log
{
    daily
    rotate 30
    maxage 30
    copytruncate
    missingok
    notifempty
    compress
    dateext
    dateformat _%Y-%m-%d_%H-%M
    delaycompress
    create 644 spring-mvc syslog
    su spring-mvc syslog
}

logrotate gc

cat /etc/logrotate.d/spring-mvc-gc.conf 
/var/log/spring-mvc/gc.log
{
    daily
    rotate 30
    maxage 30
    copytruncate
    missingok
    notifempty
    compress
    dateext
    dateformat _%Y-%m-%d_%H-%M
    delaycompress
    create 644 spring-mvc syslog
    su spring-mvc syslog
}

Doesn't spring boot manage its own rotation of log files?
If you have application that writes to console, you better do something with it. Also if the application doesn't start, and doesn't write anything to logging is probably because it has an exception before logging framework setup, and that error is present in system.out/err
C
Community

In this question, the answer from @PbxMan should get you started:

Run a Java Application as a Service on Linux

Edit:

There is another, less nice way to start a process on reboot, using cron:

@reboot user-to-run-under /usr/bin/java -jar /path/to/application.jar

This works, but gives you no nice start/stop interface for your application. You can still simply kill it anyway...


Not really, because Spring Boot offers special features to do this.
C
Community

I don't know of a "standard" shrink-wrapped way to do that with a Java app, but it's definitely a good idea (you want to benefit from the keep-alive and monitoring capabilities of the operating system if they are there). It's on the roadmap to provide something from the Spring Boot tool support (maven and gradle), but for now you are probably going to have to roll your own. The best solution I know of right now is Foreman, which has a declarative approach and one line commands for packaging init scripts for various standard OS formats (monit, sys V, upstart etc.). There is also evidence of people having set stuff up with gradle (e.g. here).


C
Community

Are you using Maven? Then you should try the AppAssembler Plugin:

The Application Assembler Plugin is a Maven plugin for generating scripts for starting java applications. ... All artifacts (dependencies + the artifact from the project) are added to the classpath in the generated bin scripts. Supported platforms: Unix-variants Windows NT (Windows 9x is NOT supported) Java Service Wrapper (JSW)

See: http://mojo.codehaus.org/appassembler/appassembler-maven-plugin/index.html


i
ismael

The following configuration is required in build.gradle file in Spring Boot projects.

build.gradle

jar {
    baseName = 'your-app'
    version = version
}

springBoot {
    buildInfo()
    executable = true   
    mainClass = "com.shunya.App"
}

executable = true

This is required to make fully executable jar on unix system (Centos and Ubuntu)

Create a .conf file

If you want to configure custom JVM properties or Spring Boot application run arguments, then you can create a .conf file with the same name as the Spring Boot application name and place it parallel to jar file.

Considering that your-app.jar is the name of your Spring Boot application, then you can create the following file.

JAVA_OPTS="-Xms64m -Xmx64m"
RUN_ARGS=--spring.profiles.active=prod
LOG_FOLDER=/custom/log/folder

This configuration will set 64 MB ram for the Spring Boot application and activate prod profile.

Create a new user in linux

For enhanced security we must create a specific user to run the Spring Boot application as a service.

Create a new user

sudo useradd -s /sbin/nologin springboot

On Ubuntu / Debian, modify the above command as follow:

sudo useradd -s /usr/sbin/nologin springboot

Set password

sudo passwd springboot

Make springboot owner of the executable file

chown springboot:springboot your-app.jar

Prevent the modification of jar file

chmod 500 your-app.jar

This will configure jar’s permissions so that it can not be written and can only be read or executed by its owner springboot.

You can optionally make your jar file as immutable using the change attribute (chattr) command.

sudo chattr +i your-app.jar

Appropriate permissions should be set for the corresponding .conf file as well. .conf requires just read access (Octal 400) instead of read + execute (Octal 500) access

chmod 400 your-app.conf

Create Systemd service

/etc/systemd/system/your-app.service

[Unit]
Description=Your app description
After=syslog.target

[Service]
User=springboot
ExecStart=/var/myapp/your-app.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

Automatically restart process if it gets killed by OS

Append the below two attributes (Restart and RestartSec) to automatically restart the process on failure.

/etc/systemd/system/your-app.service

[Service]
User=springboot
ExecStart=/var/myapp/your-app.jar
SuccessExitStatus=143
Restart=always
RestartSec=30

The change will make Spring Boot application restart in case of failure with a delay of 30 seconds. If you stop the service using systemctl command then restart will not happen.

Schedule service at system startup

To flag the application to start automatically on system boot, use the following command:

Enable Spring Boot application at system startup

sudo systemctl enable your-app.service

Start an Stop the Service

systemctl can be used in Ubuntu 16.04 LTS and 18.04 LTS to start and stop the process.

Start the process

sudo systemctl start your-app

Stop the process

sudo systemctl stop your-app

References

https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html


Nice... The issue now is running the app on a privileged port with that non-sudoer user.
J
JGlass

Following up on Chad's excellent answer, if you get an error of "Error: Could not find or load main class" - and you spend a couple hours trying to troubleshoot it, whether your executing a shell script that starts your java app or starting it from systemd itself - and you know your classpath is 100% correct, e.g. manually running the shell script works as well as running what you have in systemd execstart. Be sure you're running things as the correct user! In my case, I had tried different users, after quite a while of troubleshooting - i finally had a hunch, put root as the user - voila, the app started correctly. After determining it was a wrong user issue, I chown -R user:user the folder and subfolders and the app ran correctly as the specified user and group so no longer needed to run it as root (bad security).


s
slm

In systemd unit files you can set environment variables directory or through an EnvironmentFile. I would propose doing things this way since it seems to be the least amount of friction.

Sample unit file

$ cat /etc/systemd/system/hello-world.service
[Unit]
Description=Hello World Service
After=systend-user-sessions.service

[Service]
EnvironmentFile=/etc/sysconfig/hello-world
Type=simple
ExecStart=/usr/bin/java ... hello-world.jar

Then setup a file under /etc/sysconfig/hello-world which includes uppercase names of your Spring Boot variables. For example, a variable called server.port would follow the form SERVER_PORT as an environment variable:

$ cat /etc/sysconfig/hello-world
SERVER_PORT=8081

The mechanism being exploited here is that Spring Boot applications will take the list of properties and then translate them, making everything uppercase, and replacing dots with underscores. Once the Spring Boot app goes through this process, it then looks for environment variables that match, and uses any found accordingly.

This is highlighted in more detail in this SO Q&A titled: How to set a Spring Boot property with an underscore in its name via Environment Variables?

References

Part IV. Spring Boot features - 24. Externalized Configuration


What i don't personally like is to scatter configuration file around or related files around. Ideally, I like having everything in one place that way you're only looking in one place during maintenance or when someone else has to take over your stuff.
M
MariuszS

It can be done using Systemd service in Ubuntu

[Unit]
Description=A Spring Boot application
After=syslog.target

[Service]
User=baeldung
ExecStart=/path/to/your-app.jar SuccessExitStatus=143

[Install] 
WantedBy=multi-user.target

You can follow this link for more elaborated description and different ways to do so. http://www.baeldung.com/spring-boot-app-as-a-service


i
ismael

Create a script with name your-app.service (rest-app.service). We should place this script in /etc/systemd/system directory. Here is the sample content of the script

[Unit]
Description=Spring Boot REST Application
After=syslog.target

[Service]
User=javadevjournal
ExecStart=/var/rest-app/restdemo.jar
SuccessExitStatus=200

[Install]
WantedBy=multi-user.target

Next:

 service rest-app start

References

enter link description here


Looks the same like this -> stackoverflow.com/a/30497095/516167
a
aCiD

For SpringBoot 2.4.4, in addition to instructions by @ismael

I had the following in my maven pom.xml to make it an executable jar

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>               
            <configuration>
                <executable>true</executable>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>