Linux: Spring Boot as a service on port 80

Well, that’s a mouth full. This blog shows how to run your Spring Boot application on port 80 when your linux server starts. We are going to use Ubuntu or Linux Mint for this, and we’re going to assume that Java is installed.

Setup Spring Boot

The first thing we need to do is tell Maven to make our Spring Boot application runnable. We’ll configure the spring-boot-maven-plugin to make the jar that’s going to be built executable.

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

By default Spring Boot runs on port 8080. There’s no need to change that here, we’ll deal with that later. But if you want, you could start the application on a different port by configuring it in application.properties

server.port=8079

Run Application as a Service

Set permissions

The first thing we need to do is to copy the application to its final destination, and create a user to run the app. By creating a separate user for this application, we limit the damage that can be done in case there is a security breach. I’m going to call the application “website” and I’m going to place it under /var/website, to make things easy. Once the application is in its place, change directory to /var/website, create a new user and transfer ownership to the new user. We’re going to allow the newly created user to read and execute the file, but not to write to the file. This, again, limits the damage that can be done when there’s a security breach.

sudo useradd website
sudo passwd website
sudo chown website:website website.jar
sudo chmod 500 website.jar

Setup systemd service

To manage starting and stopping our application, we’re going to turn it into a service maintained by systemd. We need to tell systemd how to run our service by creating a configuration file.

create /etc/systemd/system/website.service

[Unit]
Description=My spring-boot application

Wants=network.target
After=syslog.target

[Service]
User=website
Type=simple
ExecStart=/var/website/website.jar
Restart=on-failure
RestartSec=60
KillMode=mixed

[Install]
WantedBy=multi-user.target

This is what the options mean:
Description: A text description of what the service is for.
Wants: The services that should, but are not required to, be started before this service.
After: The services that should be started (if they are not already running) after this service has been started.
User: The user that should be used to start this service.
Type: This indicates the way the service is started. We’ll set it to simple.
ExecStart: The command with parameters that needs to be executed to start this service.
Restart: When the service should be restarted.
RestartSec: If the service needs to be restarted, how many seconds systemd should wait.
KillMode: How the process will be stopped by systemd. We’ll set it to mixed, which sends a SIGTERM signal to our service which allows the service to shut down itself.
WantedBy: We want our service to be started when the system is up and running, and accepting connections from users.

For a list of options, look here.
KillMode is described here.
For more info on WantedBy, see this.

For the security reasons mentioned above, we’ll change the permissions of this file:

sudo chmod 640 /etc/systemd/system/website.service

Enable service to start on boot

The next thing we want to do is to indicate that this service should be started when the server is starting.

sudo systemctl enable website

Setup nginx

Now we have our application installed as a service, and it’s started when the server starts up. But the application is running on port 8079, which is not what we want. Linux only allows the root user to open TCP ports below 1000. To fix this, we’re going to use nginx to forward port 80 to our application.

create /etc/nginx/sites-enabled/website.conf with the following content:

server {
        listen  80;
        server_name     _;
        location /  {
            proxy_pass          http://localhost:8079/;
            proxy_redirect      off;

            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
}

This tells nginx to listen to port 80, for incoming connections on any hostname, and forward it to our application.

Downloading an entire podcast archive

These days I spend way too much time in my car. And I’d like to make that time at least a bit more useful. So I listen to podcasts. However, downloading all these podcasts can also take a lot of time, especially when you want to download an entire archive. Using Linux, it only takes one simple little script to do it.

for i in $( curl -s <a href="http://www.nature.com/nature/podcast/archive.html">http://www.nature.com/nature/podcast/archive.html</a> |
grep -oie "http.*\.mp3" |
sort |
uniq );
do
echo "${i}:" ;
curl -o ${i##*/} -# ${i};
done

Download

Ok, so it isn’t that simple. Here’s a breakdown of what it does:

for i in $( <list> ); do <commands> ; done

This is the basic for-each loop. For each item in <list>, do <command>.

curl -s <a href="http://www.nature.com/nature/podcast/archive.html\">http://www.nature.com/nature/podcast/archive.html</a>

Retrieve the page (in this case http://www.nature.com/nature/podcast/archive.html) silently. However, it will still output the page to the standard output stream, which is what we want.

grep -oie "http.*\.mp3"

Match the pattern starting with “http”, and ending with “.mp3”. The -o switch will only show the part matching the pattern, the -i will ignore the case, and the -e indicates the pattern will follow.

sort | uniq

These two are combined, because they are basically self explanatory: “sort” sorts the list, “uniq” will remove duplicates from the list.

echo "${i}:"

This just prints the URL of the file to retrieve.

curl -o ${i##*/} -# ${i}

This will retrieve the URL, specified by ‘${i}’. The ‘-#’ means it will display a progress bar. And lastly, it will write the retrieved file to disk. The filename, indicated by ‘-o’, will be the same as the last part of the URL. This means ‘http://www.nature.com/multimedia/podcast/nature/v503/n7477/nature-2013-11-28.mp3′ will be translated to ‘nature-201podcasts3-11-28.mp3

And the end result will be about 400 podcast episodes to listen to. That will keep you busy for a while…