135

In the Spring Boot Document, they said that 'Each SpringApplication will register a shutdown hook with the JVM to ensure that the ApplicationContext is closed gracefully on exit.'

When I click ctrl+c on the shell command, the application can be shutdown gracefully. If I run the application in a production machine, I have to use the command java -jar ProApplicaton.jar. But I can't close the shell terminal, otherwise it will close the process.

If I run command like nohup java -jar ProApplicaton.jar &, I can't use ctrl+c to shutdown it gracefully.

What is the correct way to start and stop a Spring Boot Application in the production environment?

Chris
  • 5,751
  • 9
  • 39
  • 56
  • Depending on your configuration the PID of the application is written to file. You can send a kill signal to that PID. See also the comments in [this issue](https://github.com/spring-projects/spring-boot/issues/644). – M. Deinum Oct 24 '14 at 12:08
  • Which signal should I use, I don't think kill -9 is a good idea, right? – Chris Oct 24 '14 at 12:19
  • 5
    That is why I pointed you to that thread... But something like `kill -SIGTERM ` should do the trick. – M. Deinum Oct 24 '14 at 13:08
  • kill with pid, no -9 – Dapeng Sep 27 '16 at 17:12
  • 1
    kill $(lsof -ti tcp:) -- in case you don't want to use actuator and need a quick kill – Alex Nolasco Mar 20 '17 at 23:13
  • I found another question + answer about this same problem: https://stackoverflow.com/questions/22944144/programmatically-shut-down-spring-boot-application Searching a little bit more I found this post describing a more sophisticated scenario where a entire stack is shutdown, including some Spring Boot applications: https://williamsbdev.com/posts/graceful-shutdowns/ – Jackson Cassimiro Sep 01 '17 at 13:30

16 Answers16

68

If you are using the actuator module, you can shutdown the application via JMX or HTTP if the endpoint is enabled.

add to application.properties:

endpoints.shutdown.enabled=true

Following URL will be available:

/actuator/shutdown - Allows the application to be gracefully shutdown (not enabled by default).

Depending on how an endpoint is exposed, the sensitive parameter may be used as a security hint.

For example, sensitive endpoints will require a username/password when they are accessed over HTTP (or simply disabled if web security is not enabled).

From the Spring boot documentation

catch23
  • 13,661
  • 38
  • 120
  • 194
Jean-Philippe Bond
  • 8,829
  • 2
  • 32
  • 56
  • 1
    I don't want to include actuator module. But I found that, in my console log, Spring Boot print the PID in the first line. Is there any way to let Spring Boot print the PID in other file without adding actuator module (ApplicationPidListener)? – Chris Nov 05 '14 at 09:59
  • 2
    Note this has to be a http post to this endpoint. You can enable it with endpoints.shutdown.enabled=true – sparkyspider Mar 02 '15 at 13:46
66

Here is another option that does not require you to change the code or exposing a shut-down endpoint. Create the following scripts and use them to start and stop your app.

start.sh

#!/bin/bash
java -jar myapp.jar & echo $! > ./pid.file &

Starts your app and saves the process id in a file

stop.sh

#!/bin/bash
kill $(cat ./pid.file)

Stops your app using the saved process id

start_silent.sh

#!/bin/bash
nohup ./start.sh > foo.out 2> foo.err < /dev/null &

If you need to start the app using ssh from a remote machine or a CI pipeline then use this script instead to start your app. Using start.sh directly can leave the shell to hang.

After eg. re/deploying your app you can restart it using:

sshpass -p password ssh -oStrictHostKeyChecking=no userName@www.domain.com 'cd /home/user/pathToApp; ./stop.sh; ./start_silent.sh'
Jens
  • 1,379
  • 12
  • 30
  • 1
    This should be the answer. I just confirmed that a signal 15 shutdown tells spring to shitdown gracefully. – Chad May 31 '18 at 14:52
  • 1
    Why don't you call java - jar execution with nohup inside start.sh, rather than calling java - jar execution inside start.sh which is called with nohup inside another outer shell script?? – Anand Varkey Philips Jun 14 '18 at 19:25
  • 2
    @AnandVarkeyPhilips The only reason is that I sometimes call start.sh from the command line for testing purposes, but if you always need `nohup` then you can just merge the commands – Jens Jun 15 '18 at 09:56
  • @Jens, Thanks for the info. Can you tell me you do this : foo.out 2> foo.err < /dev/null – Anand Varkey Philips Jun 15 '18 at 18:53
  • @AnandVarkeyPhilips You can find some good answers to nohup here: https://stackoverflow.com/questions/10408816/how-do-i-use-the-nohup-command-without-getting-nohup-out but to copy the one you are looking for - < /dev/null redirects the standard input for nohup. Linux does not require this but POSIX allows behavior where nohup cannot run on the background if it's standard input is connected to terminal. Examples of such systems are BSD and OS X. The foo files are used to save any output that may be generated by the command so you can read them later. – Jens Jun 15 '18 at 20:57
  • Sometimes, don't you have to wait till stopping is completed and then start? Do you handle that waiting?? – Anand Varkey Philips Jun 25 '18 at 14:36
  • @AnandVarkeyPhilips I have not had any issues but you are welcome to add extra commands to fit your needs. I this case you could look at `sleep 5s` – Jens Jul 11 '18 at 14:52
  • 1
    @jens, thanks I have done it succesfully and I have posted my start and stop script here below. ( https://stackoverflow.com/questions/26547532/how-to-shutdown-a-spring-boot-application-in-a-correct-way/51426095#51426095 ) – Anand Varkey Philips Jul 19 '18 at 15:13
  • This will couse the connections keep open from db, file etc. Be careful when you apply this solution. – C.T Jan 14 '20 at 12:06
  • @C.T have you tried cleaning up your connection etc. in a shutdown hook? Like its described here: https://www.baeldung.com/spring-boot-shutdown. – Jens Jan 14 '20 at 14:21
  • @Jens your answer saved me thank you. Is there any way that we can rotate the logs foo.out and foo.err by one way or the other since the .out file can become very large at some point can we do that by some way ??? – Vikram Jun 22 '20 at 05:21
55

As to @Jean-Philippe Bond 's answer ,

here is a maven quick example for maven user to configure HTTP endpoint to shutdown a spring boot web app using spring-boot-starter-actuator so that you can copy and paste:

1.Maven pom.xml:

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

2.application.properties:

#No auth  protected 
endpoints.shutdown.sensitive=false

#Enable shutdown endpoint
endpoints.shutdown.enabled=true

All endpoints are listed here:

3.Send a post method to shutdown the app:

curl -X POST localhost:port/shutdown

Security Note:

if you need the shutdown method auth protected, you may also need

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

configure details:

Community
  • 1
  • 1
JaskeyLam
  • 13,279
  • 17
  • 103
  • 134
  • After second step, when try to deploy, met this error message: Description: Parameter 0 of method setAuthenticationConfiguration in org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter required a bean of type 'org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration' in your configuration. – Roberto Dec 07 '17 at 15:55
  • 2
    Please note: If I have included something like `server.contextPath=/appName` in my application.properties So now command to shutdown would become: `curl -X POST localhost:8080/appName/shutdown` Hope it may help someone. I had to struggle a lot because of this mistake. – Naveen Kumar Jan 06 '18 at 18:47
  • With Spring Boot 1.5.8 it is suggested, if without security, having in application.properties `endpoints.shutdown.enabled=true management.security.enabled=false`. – Stefano Scarpanti Jan 09 '18 at 14:06
  • If you dont want to expose end point and use PID file to stop and start via shell script, try this: https://stackoverflow.com/questions/26547532/how-to-shutdown-a-spring-boot-application-in-a-correct-way/51426095#51426095 – Anand Varkey Philips Jul 19 '18 at 15:15
27

You can make the springboot application to write the PID into file and you can use the pid file to stop or restart or get the status using a bash script. To write the PID to a file, register a listener to SpringApplication using ApplicationPidFileWriter as shown below :

SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./bin/app.pid"));
application.run();

Then write a bash script to run the spring boot application . Reference.

Now you can use the script to start,stop or restart.

nav3916872
  • 900
  • 11
  • 17
  • Generic and Jenkins compatible complete start and stop shell script could be found here (https://stackoverflow.com/questions/26547532/how-to-shutdown-a-spring-boot-application-in-a-correct-way/51426095#51426095) – Anand Varkey Philips Jul 19 '18 at 15:16
  • @DwB No? https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/ApplicationPidFileWriter.html – Christian Nilsson Aug 12 '19 at 15:46
21

All of the answers seem to be missing the fact that you may need to complete some portion of work in coordinated fashion during graceful shutdown (for example, in an enterprise application).

@PreDestroy allows you to execute shutdown code in the individual beans. Something more sophisticated would look like this:

@Component
public class ApplicationShutdown implements ApplicationListener<ContextClosedEvent> {
     @Autowired ... //various components and services

     @Override
     public void onApplicationEvent(ContextClosedEvent event) {
         service1.changeHeartBeatMessage(); // allows loadbalancers & clusters to prepare for the impending shutdown
         service2.deregisterQueueListeners();
         service3.finishProcessingTasksAtHand();
         service2.reportFailedTasks();
         service4.gracefullyShutdownNativeSystemProcessesThatMayHaveBeenLaunched(); 
         service1.eventLogGracefulShutdownComplete();
     }
}
Michal
  • 1,640
  • 18
  • 33
8

As of Spring Boot 2.3 and later, there's a built-in graceful shutdown mechanism.

Pre-Spring Boot 2.3, there is no out-of-the box graceful shutdown mechanism. Some spring-boot starters provide this functionality:

  1. https://github.com/jihor/hiatus-spring-boot
  2. https://github.com/gesellix/graceful-shutdown-spring-boot
  3. https://github.com/corentin59/spring-boot-graceful-shutdown

I am the author of nr. 1. The starter is named "Hiatus for Spring Boot". It works on the load balancer level, i.e. simply marks the service as OUT_OF_SERVICE, not interfering with application context in any way. This allows to do a graceful shutdown and means that, if required, the service can be taken out of service for some time and then brought back to life. The downside is that it doesn't stop the JVM, you will have to do it with kill command. As I run everything in containers, this was no big deal for me, because I will have to stop and remove the container anyway.

Nos. 2 and 3 are more or less based on this post by Andy Wilkinson. They work one-way - once triggered, they eventually close the context.

jihor
  • 2,138
  • 11
  • 24
8

I don't expose any endpoints and start (with nohup in background and without out files created through nohup) and stop with shell script(with KILL PID gracefully and force kill if app is still running after 3 mins). I just create executable jar and use PID file writer to write PID file and store Jar and Pid in folder with same name as of application name and shell scripts also have same name with start and stop in the end. I call these stop script and start script via jenkins pipeline also. No issues so far. Perfectly working for 8 applications(Very generic scripts and easy to apply for any app).

Main Class

@SpringBootApplication
public class MyApplication {

    public static final void main(String[] args) {
        SpringApplicationBuilder app = new SpringApplicationBuilder(MyApplication.class);
        app.build().addListeners(new ApplicationPidFileWriter());
        app.run();
    }
}

YML FILE

spring.pid.fail-on-write-error: true
spring.pid.file: /server-path-with-folder-as-app-name-for-ID/appName/appName.pid

Here is the start script(start-appname.sh):

#Active Profile(YAML)
ACTIVE_PROFILE="preprod"
# JVM Parameters and Spring boot initialization parameters
JVM_PARAM="-Xms512m -Xmx1024m -Dspring.profiles.active=${ACTIVE_PROFILE} -Dcom.webmethods.jms.clientIDSharing=true"
# Base Folder Path like "/folder/packages"
CURRENT_DIR=$(readlink -f "$0")
BASE_PACKAGE="${CURRENT_DIR%/bin/*}"
# Shell Script file name after removing path like "start-yaml-validator.sh"
SHELL_SCRIPT_FILE_NAME=$(basename -- "$0")
# Shell Script file name after removing extension like "start-yaml-validator"
SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT="${SHELL_SCRIPT_FILE_NAME%.sh}"
# App name after removing start/stop strings like "yaml-validator"
APP_NAME=${SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT#start-}

PIDS=`ps aux |grep [j]ava.*-Dspring.profiles.active=$ACTIVE_PROFILE.*$APP_NAME.*jar | awk {'print $2'}`
if [ -z "$PIDS" ]; then
  echo "No instances of $APP_NAME with profile:$ACTIVE_PROFILE is running..." 1>&2
else
  for PROCESS_ID in $PIDS; do
        echo "Please stop the process($PROCESS_ID) using the shell script: stop-$APP_NAME.sh"
  done
  exit 1
fi

# Preparing the java home path for execution
JAVA_EXEC='/usr/bin/java'
# Java Executable - Jar Path Obtained from latest file in directory
JAVA_APP=$(ls -t $BASE_PACKAGE/apps/$APP_NAME/$APP_NAME*.jar | head -n1)
# To execute the application.
FINAL_EXEC="$JAVA_EXEC $JVM_PARAM -jar $JAVA_APP"
# Making executable command using tilde symbol and running completely detached from terminal
`nohup $FINAL_EXEC  </dev/null >/dev/null 2>&1 &`
echo "$APP_NAME start script is  completed."

Here is the stop script(stop-appname.sh):

#Active Profile(YAML)
ACTIVE_PROFILE="preprod"
#Base Folder Path like "/folder/packages"
CURRENT_DIR=$(readlink -f "$0")
BASE_PACKAGE="${CURRENT_DIR%/bin/*}"
# Shell Script file name after removing path like "start-yaml-validator.sh"
SHELL_SCRIPT_FILE_NAME=$(basename -- "$0")
# Shell Script file name after removing extension like "start-yaml-validator"
SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT="${SHELL_SCRIPT_FILE_NAME%.*}"
# App name after removing start/stop strings like "yaml-validator"
APP_NAME=${SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT:5}

# Script to stop the application
PID_PATH="$BASE_PACKAGE/config/$APP_NAME/$APP_NAME.pid"

if [ ! -f "$PID_PATH" ]; then
   echo "Process Id FilePath($PID_PATH) Not found"
else
    PROCESS_ID=`cat $PID_PATH`
    if [ ! -e /proc/$PROCESS_ID -a /proc/$PROCESS_ID/exe ]; then
        echo "$APP_NAME was not running with PROCESS_ID:$PROCESS_ID.";
    else
        kill $PROCESS_ID;
        echo "Gracefully stopping $APP_NAME with PROCESS_ID:$PROCESS_ID..."
        sleep 5s
    fi
fi
PIDS=`/bin/ps aux |/bin/grep [j]ava.*-Dspring.profiles.active=$ACTIVE_PROFILE.*$APP_NAME.*jar | /bin/awk {'print $2'}`
if [ -z "$PIDS" ]; then
  echo "All instances of $APP_NAME with profile:$ACTIVE_PROFILE has has been successfully stopped now..." 1>&2
else
  for PROCESS_ID in $PIDS; do
    counter=1
    until [ $counter -gt 150 ]
        do
            if ps -p $PROCESS_ID > /dev/null; then
                echo "Waiting for the process($PROCESS_ID) to finish on it's own for $(( 300 - $(( $counter*5)) ))seconds..."
                sleep 2s
                ((counter++))
            else
                echo "$APP_NAME with PROCESS_ID:$PROCESS_ID is stopped now.."
                exit 0;
            fi
    done
    echo "Forcefully Killing $APP_NAME with PROCESS_ID:$PROCESS_ID."
    kill -9 $PROCESS_ID
  done
fi
Anand Varkey Philips
  • 1,271
  • 17
  • 34
7

Spring Boot provided several application listener while try to create application context one of them is ApplicationFailedEvent. We can use to know weather the application context initialized or not.

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.event.ApplicationFailedEvent; 
    import org.springframework.context.ApplicationListener;

    public class ApplicationErrorListener implements 
                    ApplicationListener<ApplicationFailedEvent> {

        private static final Logger LOGGER = 
        LoggerFactory.getLogger(ApplicationErrorListener.class);

        @Override
        public void onApplicationEvent(ApplicationFailedEvent event) {
           if (event.getException() != null) {
                LOGGER.info("!!!!!!Looks like something not working as 
                                expected so stoping application.!!!!!!");
                         event.getApplicationContext().close();
                  System.exit(-1);
           } 
        }
    }

Add to above listener class to SpringApplication.

    new SpringApplicationBuilder(Application.class)
            .listeners(new ApplicationErrorListener())
            .run(args);  
user3137438
  • 71
  • 1
  • 1
5

SpringApplication implicitly registers a shutdown hook with the JVM to ensure that ApplicationContext is closed gracefully on exit. That will also call all bean methods annotated with @PreDestroy. That means we don't have to explicitly use the registerShutdownHook() method of a ConfigurableApplicationContext in a boot application, like we have to do in spring core application.

@SpringBootConfiguration
public class ExampleMain {
    @Bean
    MyBean myBean() {
        return new MyBean();
    }

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(ExampleMain.class, args);
        MyBean myBean = context.getBean(MyBean.class);
        myBean.doSomething();

        //no need to call context.registerShutdownHook();
    }

    private static class MyBean {

        @PostConstruct
        public void init() {
            System.out.println("init");
        }

        public void doSomething() {
            System.out.println("in doSomething()");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("destroy");
        }
    }
}
mnille
  • 1,332
  • 4
  • 13
  • 17
Shruti Gupta
  • 180
  • 2
  • 9
  • As an alternative to `@PostConstruct` and `@PreDestroy` I used the `initMethod` and `destroyMethod` attributes within the `@Bean` annotation. So for this example: `@Bean(initMethod="init", destroyMethod="destroy")`. – acker9 Sep 10 '18 at 23:02
  • One catch about `@PreDestroy` few developers might not know is, such methods are only called for beans with Singleton scope. The developers have to manage the cleanup part of the bean life cycle for other scopes – asgs Feb 15 '20 at 18:28
3

They are many ways to shutdown a spring application. One is to call close() on the ApplicationContext:

ApplicationContext ctx =
    SpringApplication.run(HelloWorldApplication.class, args);
// ...
ctx.close()

Your question suggest you want to close your application by doing Ctrl+C, that is frequently used to terminate a command. In this case...

Use endpoints.shutdown.enabled=true is not the best recipe. It means you expose an end-point to terminate your application. So, depending on your use case and your environment, you will have to secure it...

A Spring Application Context may have register a shutdown hook with the JVM runtime. See ApplicationContext documentation.

Spring Boot configure this shutdown hook automatically since version 2.3 (see jihor's answer). You may need to register some @PreDestroy methods that will be executed during the graceful shutdown (see Michal's answer).

Ctrl+C should work very well in your case. I assume your issue is caused by the ampersand (&) More explanation:

On Ctrl+C, your shell sends an INT signal to the foreground application. It means "please interrupt your execution". The application can trap this signal and do cleanup before its termination (the hook registered by Spring), or simply ignore it (bad).

nohup is command that execute the following program with a trap to ignore the HUP signal. HUP is used to terminate program when you hang up (close your ssh connexion for example). Moreover it redirects outputs to avoid that your program blocks on a vanished TTY. nohupdoes NOT ignore INT signal. So it does NOT prevent Ctrl+C to work.

I assume your issue is caused by the ampersand (&), not by nohup. Ctrl+C sends a signal to the foreground processes. The ampersand causes your application to be run in background. One solution: do

kill -INT pid

Use kill -9 or kill -KILL is bad because the application (here the JVM) cannot trap it to terminate gracefully.

Another solution is to bring back your application in foreground. Then Ctrl+C will work. Have a look on Bash Job control, more precisely on fg.

mcoolive
  • 2,615
  • 19
  • 28
3

Use the static exit() method in the SpringApplication class for closing your spring boot application gracefully.

public class SomeClass {
    @Autowired
    private ApplicationContext context

    public void close() {
        SpringApplication.exit(context);
    }
}
rw026
  • 825
  • 1
  • 4
  • 13
2

Spring Boot now supports graceful shut down (currently in pre-release versions, 2.3.0.BUILD-SNAPSHOT)

When enabled, shutdown of the application will include a grace period of configurable duration. During this grace period, existing requests will be allowed to complete but no new requests will be permitted

You can enable it with:

server.shutdown.grace-period=30s

https://docs.spring.io/spring-boot/docs/2.3.0.BUILD-SNAPSHOT/reference/html/spring-boot-features.html#boot-features-graceful-shutdown

cmlonder
  • 1,915
  • 17
  • 29
0

If you are using maven you could use the Maven App assembler plugin.

The daemon mojo (which embed JSW) will output a shell script with start/stop argument. The stop will shutdown/kill gracefully your Spring application.

The same script can be used to use your maven application as a linux service.

Community
  • 1
  • 1
Nicolas Labrot
  • 3,758
  • 21
  • 35
0

If you are in a linux environment all you have to do is to create a symlink to your .jar file from inside /etc/init.d/

sudo ln -s /path/to/your/myboot-app.jar /etc/init.d/myboot-app

Then you can start the application like any other service

sudo /etc/init.d/myboot-app start

To close the application

sudo /etc/init.d/myboot-app stop

This way, application will not terminate when you exit the terminal. And application will shutdown gracefully with stop command.

Johna
  • 1,416
  • 2
  • 12
  • 24
0

For Spring boot web apps, Spring boot provides the out-of-box solution for graceful shutdown from version 2.3.0.RELEASE.

An excerpt from Spring doc

Refer this answer for the Code Snippet

Prasanth Rajendran
  • 1,596
  • 1
  • 20
  • 26
0

If you are using spring boot version 2.3 n up , There is an in-build way to shutdown app gracefully.Add below in application.properties

server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=20s

If you are using lower spring boot version, You can write a custom shutdown hook and handle different beans, how they should shutdown or in which order they should shutdown. Example code below.

@Component
public class AppShutdownHook implements ApplicationListener<ContextClosedEvent> {

    private static final Logger logger = LoggerFactory.getLogger(AppShutdownHook.class);


    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        logger.info("shutdown requested !!!");
        try {
            //TODO Add logic to shutdown, diff elements of your application
            } catch (Exception e) {
            logger.error("Exception occcured while shutting down Application:", e);
        }

    }


}
Varun Maurya
  • 327
  • 2
  • 14