New for Ubuntu 16.04 (Xenial Xerus) is systemd which replaces Upstart as the default init system.
systemd basics
The basic object that systemd manages is the unit which can be of different types but when it comes to running our Play app we will need to create a service.
The systemctl command is used to manage systemd services, similar to the old service command. E.g. to start Nginx you now type:
sudo systemctl start nginx.service
Custom unit files should go in the /etc/systemd/system directory and you should always run the following command after creating new unit files or modifying existing ones:
sudo systemctl daemon-reload
Run levels have been replaced with systemd units called targets. Target unit files end with the .target file extension and they are used to group other units.
A Play Framework unit file
Here is a basic Play Framework systemd unit file. It makes use of the EnvironmentFile directive which can be used to set environment variables. It is possible to set environment variables in the unit file directly but using a separate file takes some of the clutter out.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ADDRESS=127.0.0.1 | |
PORT=9000 | |
APPLY_EVOLUTIONS=true | |
APPLICATION_SECRET=my_secret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=My play app | |
After=network.target | |
[Service] | |
EnvironmentFile=/path/to/app/conf/env | |
PIDFile=/path/to/app/RUNNING_PID | |
WorkingDirectory=/path/to/app | |
ExecStart=/path/to/app/bin/play -Dhttp.address=${ADDRESS} -Dhttp.port=${PORT} -Dplay.evolutions.db.default.autoApply=${APPLY_EVOLUTIONS} -J-server | |
Restart=on-failure | |
User=play_user | |
Group=play_user | |
# See http://serverfault.com/a/695863 | |
SuccessExitStatus=143 | |
[Install] | |
WantedBy=multi-user.target |
Deploying the unit file
- Create the env file in your app’s conf directory and unit file in /etc/systemd/system
- Set the file permissions
sudo chmod 664 /etc/systemd/system/play.service
- Reload systemd
sudo systemctl daemon-reload
You can now start and stop your play app using systemctl
sudo systemctl start play.service sudo systemctl stop play.service
Thanks for posting this David. It was very useful.
Question: When I do a “system enable my-app” I get the following error msg:
Please advise.
Hmm, not a very helpful error message, is it? Does journalctl -xn show anything useful? Are there any problems with environment variables being used?
Both options ExecStop and ExecStopPost are superfluous as this is the default behavior of systemd.
From the documentation of ExecStop: “If this option is not specified, the process is terminated by sending the signal specified in
KillSignal=
when service stop is requested.”From PIDFile’s documentation: “systemd will not write to the file configured here, although it will remove the file after the service has shut down if it still exists.”
Furthermore, you should add option SuccessExitStatus=143. Details: http://serverfault.com/a/695863/140809
Thanks, Martin! I’ve updated the code.
Is
WorkingDirectory actually required?
Hi
Do you know, if there is a way to
Type=forking
the play systemd unit file?Without any type
Type=simple
is assumed; with this I don’t get an error message, if the startup wasn’t successful. I have to do astatus
after startup to see, if the play app started.Do you have an idea, how I could get an error message when the startup fails?
My systemd unit file looks like that:
# Systemd unit file for playapp (Netty)
[Unit]
Description=Run Server for playapp (Netty)
Wants=syslog.target network.target
After=network.target
[Service]
Type=simple
PIDFile=/opt/playapp/play.pid
ExecStart=/opt/playapp/bin/playapp
ExecStop=/bin/kill $MAINPID
Restart=always
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
Like this, if e.g. the launcher.jar (started by the bin/playapp script) doesn’t exist, the
Type=simple
shows no error. But theType=forking
returns an error. But if everything works fine, thesystemctl start playapp.service
remains in the foreground.Do you have a tip to improve my systemd unit file?
Thanks
Daniel
Yeah this does look like a problem. Here’s a blog post that seems relevant. I didn’t have much luck redirecting output to the terminal or trying to run with Type=forking and getting the play app to run in the background. What does appear to work is if you set Type=forking and then CTRL+C to get back to a terminal after launch. This doesn’t seem like a great idea though. 🙁
Hi David
Thanks for your answer and the link. Seems like here the playframework could get some improvements. I asked a question at https://stackoverflow.com/q/46173248/7311363. But I think there’s no solution yet.
Have a nice day
Daniel