Full guide to deploy your Play Framework app on Ubuntu 20.042020-10-27 12:36:24.66707
So, you've developed your Play Framework application and you now want to host it. In this blog post we will guide you through the general steps to safely host your application on an Ubuntu 20.02 server.
If you want to follow along with this guide, but don't have a web app, don't worry, we have a sample project on GitHub that you can use.
The web app we work with in this guide is a simple Play Framework 2.8 application in Java. It uses a PostgreSQL database and serves a sample page. In order to deploy such application, we will go through the following steps:
- Optional: running Ubuntu 20.02 on your local machine
- Setting up SSH authentication
- Installing and setting up Git
- Installing Java
- Installing sbt
- Setting up PostgreSQL
- Running your application
- Setting up a web server
- Installing Let's Encrypt for SSL connections (HTTPS)
- Notes and further improvements
1. Optional: running Ubuntu 20.02 on your local machine
If you don't already have a server on which you want to host your application, or if you just want to follow along without having a remote server, you can set up a local Ubuntu server. One of the easiest ways to do so is by downloading a VirtualBox or VMware image and spin it up. OSBoxes.org provides images that you can download (the "Info" contains the user credentials).
For this guide, we'll be using an Ubuntu 20.02 VirtualBox image and pretend as if it's our server. If you set up your local server correctly, you can sign in with the root account:
However, in a real-life situation, you would be SSH'ing to your server. So, let's also do that. In order to SSH to a machine, you need its IP-address. But since we are using VirtualBox, we need to forward a port. First, sign in as your root user via VirtualBox itself and enter the following commands:
sudo apt-get update sudo apt-get upgrade sudo apt-get install openssh-server
If that has succeeded, you can shut down your machine and go to your VirtualBox machine settings. Go to Network > Adapter 1 > Advanced > Port forwarding and add a new record with Host IP: 127.0.0.1, Host Port: 2222 and Guest Port: 22.
Save it and start your machine. You can now minimize your VirtualBox windows and pretend as if its running in some data center. Open up a new terminal window or some other SSH client program that you use. In this guide we use a bash terminal. You can now SSH to your machine:
ssh email@example.com -p 2222 The authenticity of host '[127.0.0.1]:2222 ([127.0.0.1]:2222)' can't be established. ECDSA key fingerprint is SHA256:gTCDYqbsUNGHwrFRV1XuiFa+PpQ+nE2uoD0QqMGm5WE. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[127.0.0.1]:2222' (ECDSA) to the list of known hosts. firstname.lastname@example.org's password:
In real life, if you have an VPS for example, you would be using the IP-address of the VPS instead of
root and default port
2. Setting up SSH authentication
Usually, when you get a VPS for example, your hosting provider gives you the IP-address and root user credentials. This allows you to SSH to your server, like so (with default port 22):
ssh email@example.com firstname.lastname@example.org's password:
Or, when you already pointed your domain name to the IP-address:
ssh email@example.com firstname.lastname@example.org's password:
However, it is not recommended to SSH to your server like this. For three reasons: you don't want to sign in as the root user, you don't want to sign in with a username and password and you don't want to use the default port 22. Why? In short: for security purposes. You don't want bots to keep trying to sign in with (root) user credentials (brute force attacks). And if a bot or hacker succeeds, you definitely don't want them to have root access on your server. That's why we have to do the following:
- Set up SSH certificate authentication
- Disable password authentication
- Change the authentication port
- Create a new non-root user and do the same
If you aren't familiar with SSH certificate authentication, it works as follows: On your computer you generate a public- and private key. You then transfer the public key over to your server. Then, after adjusting some settings, when you SSH to your server, you send your private key along, instead of a password. The server validates if your private key matches with its public key. If it's correct, you get signed in. In this guide, we'll also be changing the default port that gets used. We will change it from 22 to 900 (you can also pick another available port). Quick side note: if you followed along with chapter 1, your current port is 2222 instead of 22.
Let's start by signing into your server, if you haven't already:
ssh email@example.com firstname.lastname@example.org's password:
Make sure your software is up to date by executing the following commands (you can skip this if you already followed along with the first chapter on setting up your virtual machine). The first command,
sudo apt-get update checks what packages need to be updated, and
sudo apt-get upgrade actually updates them.
sudo apt-get update sudo apt-get upgrade
We also need directory to place our public key in. We create a .ssh folder in our home directory:
Now switch back to your local computer. We need to generate the public and private key. Make sure you have a folder in which you want to place your private key. On MacOS/Unix machines this can be your
cd over to that folder and execute the
ssh-keygen command. Choose a name for your certificates, in this example we entered "root_myserver". If you want, you can also enter a password.
cd ~/.ssh/ ssh-keygen -t rsa -b 4096 Generating public/private rsa key pair. Enter file in which to save the key (/Users/johnny/.ssh/id_rsa): root_myserver Enter passphrase (empty for no passphrase): Enter same passphrase again: $ chmod 600 root_myserver $ chmod 600 root_myserver.pub
If done correctly, it generates two files:
root_myserver.pub. The first one is the private key, the latter the public key. What we need to do now, is to transfer the public key (
root_myserver.pub) over to the server in the already created
.ssh folder. To accomplish this, we use the bash
scp command (if you use a virtual machine on port 2222, add
scp root_myserver.pub email@example.com:~/.ssh/
Sign back into your server and verify that the public key has been transferred:
cd .ssh/ ls root_myserver.pub
We now need to make sure that it can actually be used for SSH certificate authentication. Let's add the public key to the authorized keys (make sure your working directory is still the
cat root_myserver.pub >> authorized_keys
Edit the ssh configuration file (we use Vim) and uncomment the line with
AuthorizedKeysFile. Restart the ssh service after saving:
sudo vim /etc/ssh/sshd_config ... AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 ... sudo service ssh restart
You can now sign out from the server, and sign back in with your private key (add
-p 2222 if you use port 2222 instead of 22):
ssh -i ~/.ssh/root_myserver firstname.lastname@example.org
Only problem is, you can still also sign in with just a username and password. Let's change that.Disable password authentication
To disable password authentication, ssh to your server and edit the ssh configuration file. Here you want to uncomment the line with
PasswordAuthentication and change the value to
no. Restart the ssh service after saving changes"
sudo vim /etc/ssh/sshd_config ... PasswordAuthentication yes ... sudo service ssh restart
Whenever you now try to ssh to your server with username and password authentication you'll get a
Permission denied error.
Next thing we want to do is change our port. By default, it is set to 22 (using your virtual machine it's set to 2222 in chapter 1). You can use any available port that you want (make sure is actually is available and not reserved for something else), in this guide we'll change it to 900. Ssh to your server and once again, edit the ssh configuration file. Uncomment the line with
Port, change the value to 900 and restart the ssh service.
sudo vim /etc/ssh/sshd_config ... Port 900 ... sudo service ssh restart
So now, when you ssh to your server, you need to provide the port and private key. Notice how to don't need the root user its password anymore. But do be careful with your private key and don't just share it!
ssh -p 900 -i ~/.ssh/root_myserver email@example.com
Note: If you followed along with chapter 1, and set up VirtualBox, you'll notice that it doesn't work. That's because you need to add another port forwarding, for example from host 9999 to guest 900. If you do this, you need to ssh to your server with
Because we don't want to run our application as the root user, we are going to create a new user:
my_user. Creating a new user goes as follows in Ubuntu:
sudo adduser myuser Adding user `myuser' ... Adding new group `myuser' (1001) ... Adding new user `myuser' (1001) with group `myuser' ... Creating home directory `/home/myuser' ... Copying files from `/etc/skel' ... New password: Retype new password: passwd: password updated successfully Changing the user information for myuser Enter the new value, or press ENTER for the default Full Name : My User Room Number : Work Phone : Home Phone : Other : Is the information correct? [Y/n] Y sudo usermod -aG sudo myuser
When to try to ssh to your server with myuser, you'll see that you get a "Permission denied" error. That's because myuser doesn't have the ssh certificate authentication set up. So, we need to generate another public and private key:
scp it to the
~/.ssh/ folder of the root user. Then ssh to you server as the root user and move the public key to the home directory of myuser and add it to its authorized keys:
cd ~/.ssh/ sudo mkdir /home/myuser/.ssh sudo mv myuser_myserver.pub /home/myuser/.ssh/ su myuser
cd ~/.ssh/ sudo cat myuser_myserver.pub >> authorized_keys
You can now directly ssh to your server from your newly created user
ssh -p 900 -i ~/.ssh/myuser_myserver firstname.lastname@example.org
Thus, concludes the server authentication part of this guide. We can finally start with the setup of our actual application!
3. Installing and setting up Git
In a professional environment you use a VCS (version control) to host your code. For example, using Git and Github to host your code. You probably also do this for the application that you want to host. In this guide we will use this Play Framework application: sample-play-framework-project. Feel free to clone this repository if you want to follow along.
Sign back into your server and install git:
sudo apt-get install git
You should now set up your authentication method. We recommend you use SSH authentication to pull your code. If you use Bitbucket, check out this link: Set up and SSH key. If you use GitHub, you can use this link: Connecting to GitHub with SSH. In this guide we'll use GitHub:
cd ~/.ssh/ ssh-keygen -t rsa -b 4096 -C "email@example.com" Generating public/private rsa key pair. Enter file in which to save the key (/home/myuser/.ssh/id_rsa): key_github Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in key_github Your public key has been saved in key_github.pub The key fingerprint is: ... eval "$(ssh-agent -s)" Agent pid 1150 ssh-add ~/.ssh/key_github Identity added: /home/myuser/.ssh/key_github (firstname.lastname@example.org)
Copy the contents of
key_github.pub. Go you the Github website and visit your settings page. There you'll see a tab named "SSH and GPG keys", there you add your copied key. Now you can clone your repository. We clone our sample application in the home directory:
cd ~ git clone email@example.com:Peggir/sample-play-framework-app.git
Now we have our code from the server, and we can pull it whenever there are some changes (
4. Installing Java
In this chapter, and the next two, we need to install a couple of things that we need in order to actually run the application on our server. Since our sample web app is a java application, we need to install java. We need to install the jre and the jkd. After installing we can verify it by checking the version:
sudo apt install default-jre sudo apt install default-jdk javac -version javac 11.0.8
5. Installing sbt
Play Framework applications use sbt. They use sbt to build projects and thus we need to install it. When you install it, always make sure you install the correct version. In this guide we install it as follows (version 1.3.5):
curl -L -o sbt.deb http://dl.bintray.com/sbt/debian/sbt-1.3.5.deb sudo dpkg -i sbt.deb sudo apt-get update sudo apt-get install sbt
6. Setting up PostgreSQL
Our sample application uses a PostgreSQL database, so we need to install it, add the database and a user (with password). If your application uses a different database, then install that one. In this guide we won't dive in securing your remote database connection, but it is important to know that you can disable remote access or set up certificate authentication. Installing and setting up our database (remember the database name, user and password for the next step):
sudo apt-get install postgresql postgresql-contrib sudo -u postgres psql
CREATE DATABASE "sample-app-db"; CREATE USER "sample-app-db-user" WITH ENCRYPTED PASSWORD 'my_server_password'; GRANT ALL PRIVILEGES ON DATABASE "sample-app-db" TO "sample-app-db-user"; \q
7. Running your application
In order to run the application on our server we need do three things:
- Make a log directory
- Add a production configuration file
- Create a deploy script
We need to create the directory that we are going to output our logs to. From our
logback.xml configuration we can see that it logs to
/var/log/sample-play-framework-app/. Let's create that directory:
Add a production configuration file
sudo mkdir /var/log/sample-play-framework-app
In our sample application we have an
application.conf file. Here we specify our application variables. The current one contains values for our development environment, but these don't all work on our production environment. So, we are going to create a new configuration file in which we are going to specify different values for some properties:
cd ~ sudo vim prod.conf include "application" application.env = "PROD" play.http.secret.key = "c9147165-e782-46f8-8dfa-a7156b4c8082" db.default.url = "jdbc:postgresql://localhost:5432/sample-app-db" db.default.username = "sample-app-db-user" db.default.password = "my_server_password" sudo chmod 600 prod.conf
On the first line,
include "application", we make sure that we keep our default values. On the other lines we specify our production environment specific properties and values. Our secret is a random GUID that we generated.
We are now going to create a script that will pull all changes and start the application for us. You can repeatedly execute this script whenever you have new changes that you want to deploy. Okay, lets create it:
cd ~ sudo vim deploy-script.sh
Add the following contents to the script (contents will be explained below):
#!/bin/bash cd ~/sample-play-framework-app/ git pull sudo rm -rf target/universal sudo sbt dist cd target/universal sudo unzip $(ls -t sample-play-framework-app-*.zip | head -1) sudo rm $(ls -t sample-play-framework-app-*.zip | head -1) cd $(ls -td sample-play-framework-app-* | head -1) sudo kill $(cat ~/application-instance-1.pid) sleep 20 sudo bin/sample-play-framework-app -Dconfig.file=/home/myuser/prod.conf -Dhttp.port=9998 -Dpidfile.path=/home/myuser/application-instance-1.pid > /dev/null 2>&1 & sleep 60 sudo kill $(cat ~/application-instance-2.pid) sleep 20 sudo bin/sample-play-framework-app -Dconfig.file=/home/myuser/prod.conf -Dhttp.port=9999 -Dpidfile.path=/home/myuser/application-instance-2.pid > /dev/null 2>&1 &
Explanation of the script: In short, it pulls the latest version of the application from git. It then uses
sbt dist to build a binary version of the application. This creates a .zip file, that we then unzip and delete. In the unzipped folder there is a script in the bin directory. We run that script with our arguments. In our arguments list we include our prod.conf file, a http port, and a pid file. The pid file will be generated when the application starts, it contains the process ID, that can be used to stop the application. As you can see, we actually run two instances of the application. One on port 9998 and one on port 9999. In chapter 8 you'll see why we do this (spoiler alert: for redeploying the application without down-time).
After saving the file, we need to make it executable. But we also need to install unzip:
sudo chmod +x deploy-script.sh sudo apt-get install zip unzip
If we are going to run our script the first time, we will get an error. That's because the script kills the running instances of the application, but there are none the first time. So, you can ignore the error messages about the
kill command. You can run your script as follows (may take some minutes):
The applications are running in the background after the script is done, but how do we verify that? We can't access it in our browser yet, since we don't expose it (we'll do that in the next chapter). So, for now you can use
curl to check if they are running:
curl localhost:9998 curl localhost:9999
Curl outputs the html of the index page if they are running. If you don't get the html output, something might have gone wrong in one of the previous steps. Try to solve that first before continuing.
8. Setting up a web server
At this point we have two running application instances on our server. One on port 9999 and the other on port 9998. What we need to do now is exposing our application on port 80 and let it point to one of our running instances. Port 80 is the default http port: http://example.com/ automatically goes through port 80. That way our web application becomes accessible to the outer world, in our case, by going to http://203.0.113.0/. Don't worry about https, we will handle that in the next chapter.
In this guide we'll use an apache server, but you can change this as you wish. If you want, you can use nginx. Let's install and set up our apache server to expose our application:
sudo apt-get install apache2
If the installation succeeded, an apache server is running. Verify this by accessing your IP-address or domain in a browser (side note: if you use VirtualBox, make sure to add another port forwarding from 8080 to guest port 80, then you can access your server in your browser via: http://127.0.0.1:8080/):
We now need to change the apache configuration to show our web application. We are going to use a load balancer so apache can pick which app instance to serve to the user (remember: we have two running instances, on port 9999 and 9998). Let's change our configuration:
cd /etc/apache2/sites-available/ sudo vim 000-default.conf
Change the apache configuration to:
<VirtualHost *:80> ServerName www.example.com <Location /balancer-manager> SetHandler balancer-manager Order Deny,Allow Deny from all </Location> <Proxy balancer://mycluster> BalancerMember http://localhost:9999 BalancerMember http://localhost:9998 status=+H </Proxy> <Proxy *> Order Allow,Deny Allow From All </Proxy> ProxyPreserveHost On ProxyPass /balancer-manager ! ProxyPass / balancer://mycluster/ ProxyPassReverse / balancer://mycluster/ </VirtualHost>
After saving, install the following dependencies and restart apache:
sudo a2enmod proxy sudo a2enmod proxy_balancer sudo a2enmod proxy_http sudo a2enmod lbmethod_byrequests sudo service apache2 restart
If you now refresh your browser, you should see your application. If you used our sample application, test it out and check if everything works! When you submit a form, it should be inserted into the database, if you go to the "Overview" page, you see all forms from the database.
9. Installing Let's Encrypt for SSL connections (HTTPS)
Note: In this chapter we will be setting up a HTTPS connection with Let's Encrypt. If you follow along this chapter using VirtualBox or some other virtual machine, you won't be able to actually generate the certificate and install the HTTPS connection, because you won't be authorized.
We finally got our application up and running. Only problem is, it's using HTTP, while we really want to use HTTPS. We need a certificate and have all HTTP pages automatically redirect to HTTPS. For this, we are going to use Let's Encrypt. Depending on your web server, the installation might differ. Check out their documentation. For this guide we'll install it as follows (taken from the documentation of Let's Encrypt/Certbot):
sudo apt install snapd sudo snap install core; sudo snap refresh core sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot sudo certbot --apache
If you followed all steps along successfully, you'll now have a Let's Encrypt certificate and your apache configuration will automatically be updated to actually use it. You can now visit your web application using HTTPS instead of HTTP.
10. Notes and further improvements
You've reached the finish! At least, for now that is. We've successfully installed a server and ran a Play Framework application on it, with a database, logging and (re)deploy script (that redeploys without down-time!). What else can you do now? There are multiple things you can do to further improve your server and hosting. These are:
- Visualizing and analyzing your application logs: It's important to always analyze your logs. Thanks to your logs you are able to detect errors that users encounter. One way to do that, is using the Elastic Stack.
- Securing your database: You should always work actively on securing your database. For this guide is was out of its scope, but you should really look into it.
- Create automated back-ups of logs and the database: In addition to previous two points, you should make regular back-ups of your logs and database. Again, be careful how/where you store your backed-up data. You don't want to lose all data when something happens to your server.
- Set up automated continuous deployment: Whenever you add changes to your application and push it to your version control, you don't really want to manually sign into your server and execute the deployment script. That's where CI-tools (continuous integration) chime in. With a CI-tool you can automate the process of building, testing and deploying your application. Two widely used CI tools are Travis-CI and Jenkins.