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.
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
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
[Unit]
section, e.g. After=mysql.service
, Before=apache2.service
.
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.
systemd
, which is built into most current Linux distros.
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.
java -jar
. The rest of the script is still needed.
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)
1.3.0.M2
, but I got an error when I tried 1.3.0.RC1
.
springBoot { executable = true }
block.
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>
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
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
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
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
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
}
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...
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).
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
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
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).
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
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
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
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>
Success story sharing
<executable>true</executable>
to mypom.xml
, but the packaged JAR file does not execute (..../myapp.jar ... cannot execute binary file
.)-Dspring.profiles.active=prod
to this services? Question - stackoverflow.com/questions/31242291/…/etc/init.d stop
is not stopping the app, its trying to start it again.