111
votes

Spring boot's preferred deployment method is via a executable jar file which contains tomcat inside.

It is started with a simple java -jar myapp.jar.

Now, I want to deploy that jar to my linux server on EC2, am I missing something or do I really need to create a init script to properly start the application as a daemon?

If I simply call java -jar the application dies when I log out.

I could start it in screen or nohup but that is not very elegant and a restart in my server would force me to log in and start the process manually.

So, is there something already for the task in spring boot?

9
nohup/screen (dirty method), init/systemd/upstart (proper method)user180100
@RC Yep, that much I know, like I mentioned, /sbin/init with a custom script in /etc/init.d, would do the job, but is it really the idea that everyone should build his own script to manage the daemon (start, stop, restart, status)? Feels like something is missing on that solution.Cleber Goncalves
I you feel that something is missing in spring-boot (it's a really "fresh" project btw) just contact the team in charge and make an evolution proposal.user180100
Well, if you generate a war archive you can use your distribution's version on Tomcat which will have a init script ready to use. On the other hand, if you use the executable jar approach, you have to come up with your own custom init script. Not sure if that is on boot's realm, but it is clearly missing, which is kind of weird, hence me asking in case I overlooked something. Will ping them.Cleber Goncalves

9 Answers

102
votes

Please note that since Spring Boot 1.3.0.M1, you are able to build fully executable jars using Maven and Gradle.

For Maven, just include the following in your pom.xml:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <executable>true</executable>
    </configuration>
</plugin>

For Gradle add the following snippet to your build.gradle:

springBoot {
    executable = true
}

The fully executable jar contains an extra script at the front of the file, which allows you to just symlink your Spring Boot jar to init.d or use a systemd script.

init.d example:

$ln -s /var/yourapp/yourapp.jar /etc/init.d/yourapp

This allows you to start, stop and restart your application like:

$/etc/init.d/yourapp start|stop|restart

Or use a systemd script:

[Unit]
Description=yourapp
After=syslog.target

[Service]
ExecStart=/var/yourapp/yourapp.jar
User=yourapp
WorkingDirectory=/var/yourapp
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

More information at the following links:

11
votes

By far the most easiest and reliable way to run Spring Boot applications in production is with Docker. Use Docker Compose, Docker Swarm or Kubernetes if you need to use multiple connected services.

Here's a simple Dockerfile from the official Spring Boot Docker guide to get you started:

FROM frolvlad/alpine-oraclejdk8:slim
VOLUME /tmp
ADD YOUR-APP-NAME.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]

An even better approach for building Docker images is to use Jib, an open-source Java tool maintained by Google for building Docker images of Java applications. Jib does not need a Dockerfile, you just invoke it with Maven (official quickstart here) or Gradle (official quickstart here).

Here's a sample command line to run the container as a daemon:

docker run \
  -d --restart=always \
  -e "SPRING_PROFILES_ACTIVE=prod" \
  -p 8080:8080 \
  prefix/imagename
3
votes

My Spring boot application has two initializers. One for development and another for production. For development, I use the main method like this:

@SpringBootApplication
public class MyAppInitializer {

    public static void main(String[] args) {
        SpringApplication.run(MyAppInitializer .class, args);
    }

}

My Initializer for production environment extends the SpringBootServletInitializer and looks like this:

@SpringBootApplication
public class MyAppInitializerServlet extends SpringBootServletInitializer{
    private static final Logger log = Logger
            .getLogger(SpringBootServletInitializer.class);
    @Override
    protected SpringApplicationBuilder configure(
            SpringApplicationBuilder builder) {
        log.trace("Initializing the application");
        return builder.sources(MyAppInitializerServlet .class);
    }

}

I use gradle and my build.gradle file applies 'WAR' plugin. When I run it in the development environment, I use bootrun task. Where as when I want to deploy it to production, I use assemble task to generate the WAR and deploy.

I can run like a normal spring application in production without discounting the advantages provided by the inbuilt tomcat while developing. Hope this helps.

2
votes

In a production environment you want your app to be started again on a machine restart etc, creating a /etc/init.d/ script and linking to the appropriate runlevel to start and stop it is the correct approach. Spring Boot will not extend to covering this as it is a operating system specific setup and the are tonnes of other options, do you want it running in a chroot jail, does it need to stop / start before some other software etc.

2
votes

You can use the application called Supervisor. In supervisor config you can define multiple services and ways to execute the same.

For Java and Spring boot applications the command would be java -jar springbootapp.jar.

Options can be provided to keep the application running always.So if the EC2 restart then Supervisor will restart you application

I found Supervisor easy to use compared to putting startup scripts in /etc/init.d/.The startup scripts would hang or go into waiting state in case of errors .

2
votes

If you are using gradle you can just add this to your build.gradle

springBoot {
    executable = true
}

You can then run your application by typing ./your-app.jar

Also, you can find a complete guide here to set up your app as a service

56.1.1 Installation as an init.d service (System V)

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

cheers

2
votes

On Windows OS without Service.

start.bat

@ECHO OFF
call run.bat start

stop.bat:

@ECHO OFF
call run.bat stop

run.bat

@ECHO OFF
IF "%1"=="start" (
    ECHO start myapp
    start "myapp" java -jar -Dspring.profiles.active=staging myapp.jar
) ELSE IF "%1"=="stop" (
    ECHO stop myapp
    TASKKILL /FI "WINDOWTITLE eq myapp"
) ELSE (
    ECHO please, use "run.bat start" or "run.bat stop"
)
pause
1
votes

I start applications that I want to run persistently or at least semi-permanently via screen -dmS NAME /path/to/script. As far as I am informed this is the most elegant solution.

0
votes

This is a simple, you can use spring boot maven plugin to finish your code deploy.

the plugin config like:

<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <jvmArguments>-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debug.port}
                    </jvmArguments>
                    <profiles>
                        <profile>test</profile>
                    </profiles>
                    <executable>true</executable>
                </configuration>
            </plugin>

And, the jvmArtuments is add for you jvm. profiles will choose a profile to start your app. executable can make your app driectly run.

and if you add mvnw to your project, or you have a maven enveriment. You can just call./mvnw spring-boot:run for mvnw or mvn spring-boot:run for maven.