Spring Boot application as a Service

158,118

Solution 1

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

Solution 2

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

Solution 3

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.

Solution 4

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.

Solution 5

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)

Share:
158,118

Related videos on Youtube

MariuszS
Author by

MariuszS

Devskiller - Powerful tool to test developers’ skills

Updated on November 01, 2021

Comments

  • MariuszS
    MariuszS over 2 years

    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.

  • MariuszS
    MariuszS almost 10 years
    Unfortunately systemd is not available for Centos 6
  • mist
    mist about 9 years
    how does it know how to stop it? Records the pid and then kills it?
  • MariuszS
    MariuszS almost 9 years
    Thanks, it is also possible to install as a systemd service
  • lrkwz
    lrkwz almost 9 years
    Is it for the 1.2.5/1.3.x only? I'm triyng with a 1.2.4 jar build for jdk8 and it throws a java.lang.UnsupportedClassVersionError: ... : Unsupported major.minor version 52.0
  • Abdull
    Abdull almost 9 years
    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.)
  • voor
    voor almost 9 years
    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.
  • nKognito
    nKognito almost 9 years
    Do you know guys how to pass spring's arguments such as -Dspring.profiles.active=prod to this services? Question - stackoverflow.com/questions/31242291/…
  • Radu Toader
    Radu Toader over 8 years
    what about runnable WAR files ? it does not work for me with WAR layout.
  • JBCP
    JBCP over 8 years
    Interestingly this works with release 1.3.0.M2, but I got an error when I tried 1.3.0.RC1.
  • Geir
    Geir over 8 years
    Any idea on how to do this with gradle instead of maven?
  • igracia
    igracia over 8 years
    @chad Is there a way to manage this with service myapp {start|stop}? I couldn't get it to work that way, and I get a Failed to stop spring.service: Unit spring.service not loaded error.
  • tintin
    tintin over 8 years
    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.
  • Pierre Henry
    Pierre Henry over 8 years
    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.
  • ruX
    ruX about 8 years
    If you want to monitor process and restart it if it die without writing system daemons check out patrickgrimard.com/2014/06/06/…
  • wired00
    wired00 almost 8 years
    for me that was sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp in ubuntu
  • Sajib Ghosh
    Sajib Ghosh almost 8 years
    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.
  • Steve
    Steve almost 8 years
    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.
  • yglodt
    yglodt almost 8 years
    I prefer using the full java commandline because that way you can add jvm parameters.
  • ashirman
    ashirman over 7 years
    @tintin i need to mention about spring-boot application stop command issues under some versions of linux. the latest available version of spring boot (1.4.0.RELEASE) use following piece of code to check is process running isRunning() { ps -p "$1" &> /dev/null } so even ps -p command can't find process by id it still returns 0 (means success). as result spring-boot generated service doesn't remove appropriate .pid file and it is impossible to start the service again after that. detected on ps version "procps version 3.2.8"
  • Vorsprung
    Vorsprung over 7 years
    The answer I've given works for me with springboot 1.3, Centos 7.2 and systemd
  • bernardn
    bernardn over 7 years
    Very useful for when /etc/init.d or systemd is not an option, thanks for sharing.
  • Derek Mahar
    Derek Mahar over 7 years
    @yglodt, can a Spring Boot executable jarfile not accept JVM parameters? (See my question at stackoverflow.com/q/40893530/107158.)
  • Martin Schröder
    Martin Schröder over 7 years
    @DerekMahar: Of course it can. Configurable via /etc/default/$service.conf
  • Martin Schröder
    Martin Schröder over 7 years
    @Steve: Nope. You are reinventing the wheel. Oh, and we have systemd now.
  • Natix
    Natix over 7 years
    When using Gradle, this configuration in done using springBoot { executable = true } block.
  • Dyorgio
    Dyorgio over 7 years
    When you need to pass parameters to JVM (like -javaagent or -D parameters) this is the unique way, tks @Steve !
  • humkins
    humkins about 7 years
    Hello chad, you wrote "The executable jar has the usual start, stop, restart, and status commands". Can you please explain what does this mean? Should I process "start"|"stop"|"restart"|"status" arguments in main() method of main jar class or is it internal jar function?
  • Admin
    Admin over 6 years
    @gumkins no, these are passed on the command line e.g. /etc/init.d/yourApp status you don't parse them yourself
  • Marged
    Marged almost 6 years
    I had some hard to find error because Java wrote to stderr which systemd did not grab. You might want to add some lines that include stderrs output
  • Tristan
    Tristan over 5 years
    Not really, because Spring Boot offers special features to do this.
  • spinlok
    spinlok over 5 years
    For others wondering, SuccessExitStatus=143 means "Treat exit status 143 (which corresponds to SIGTERM) as a normal exit status for this service"
  • naveenkumarbv
    naveenkumarbv over 5 years
    @RaduToader : Were you able to execute the WAR file as a service?
  • Radu Toader
    Radu Toader over 5 years
    I ended up configuring systemd with war/jar file as is easier to manage start/restart enable at boot
  • MariuszS
    MariuszS over 4 years
    Looks the same like this -> stackoverflow.com/a/30497095/516167
  • Vitaly Sazanovich
    Vitaly Sazanovich over 4 years
    Finally something worked for me right out of the box. Thanks a lot for the supervisord hint.
  • rustyx
    rustyx over 4 years
    This does the same job as systemd, which is built into most current Linux distros.
  • rustyx
    rustyx over 4 years
    For a proper boot sequence you might want to add ordering statements to the [Unit] section, e.g. After=mysql.service, Before=apache2.service.
  • Nikhil Singh Bhadoriya
    Nikhil Singh Bhadoriya about 4 years
    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?
  • Arundev
    Arundev about 4 years
    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.
  • Arundev
    Arundev about 4 years
    stackoverflow.com/questions/18205111/… can u please try this may be this will help u to resolve the issue.
  • Nikhil Singh Bhadoriya
    Nikhil Singh Bhadoriya about 4 years
    thanks for the quick response, I got my service up and running by correcting an issue with the tag in xml file.
  • TheRealChx101
    TheRealChx101 almost 3 years
    Nice... The issue now is running the app on a privileged port with that non-sudoer user.
  • TheRealChx101
    TheRealChx101 almost 3 years
    Doesn't spring boot manage its own rotation of log files?
  • TheRealChx101
    TheRealChx101 almost 3 years
    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.
  • TheRealChx101
    TheRealChx101 almost 3 years
    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
  • Radu Toader
    Radu Toader over 2 years
    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