How to deploy a Spring Boot - Angular application on Ubuntu server

When I first tried to deploy an application, I had no clue how it works. I had to do it on my own and read lots of blog posts, tutorials and Stack Overflow answers to get it. I collected information all along the way to be able to create my own step-by-step tutorial.

Since then, I reused my own tutorial to deploy a Spring Boot + Angular and Ionic application and it worked like a charm.

What we will do in this tutorial

This tutorial aims at deploying a web application with a Java / Spring Boot REST API and an Angular frontend on an Ubuntu server version 16.04 (Xenial Xerus) or 18.04 (Bionic Beaver). I’m using a PostgreSQL database and also explain how I installed it. I did not use containerization.

I used Java 14, Postgres 11.

Deploy a Spring Boot – Angular application on Ubuntu server

I’m not going through the creation of Spring Boot, Java, Angular… projects or PostgreSQL database, if you’re here I guess you have it all ready to deploy.

I developed on a Windows machine but I’ll deploy on Ubuntu server. There was a little issue a had to solve to deploy on Ubuntu 16.04, which didn’t appear on version 18.04, I included that step in the tutorial.

If you’re working on a Windows machine, make sure you have a SSH client installed (PuTTy). To remote connect through SSH, you’ll use the Windows command line

putty.exe user@IP

where “user” is the user name and IP is the host IP address.

You might use PuTTy’s graphical interface, but using command line allows you to create a little script (.bat) so you just click on the script and it opens the connection window where you just have to input the password. I find it very handy.

When connected to the remote Ubuntu machine, you want to ensure the user that will deploy has the sudo privileges. To be honest, when I deploy a private project and I’m working alone, I do everything with the root account, but that’s not recommended. For production grade applications, you should of course have a technical user dedicated to this kind of actions.

From admin (root) account (type su):

visudo

It opens a file, just add following line at bottom:

username ALL=(ALL) NOPASSWD:ALL

where username is the name of the user that needs sudo access.

Now, when you connect as “username”, you can type sudo -i or su to act with full privilege (beware of security issues!) or prefix commands with sudo.

Install Java (Oracle Java JDK Installer) on the Ubuntu server

Here started trouble, because I wasn’t able to install one of the licensed Java versions (8 or 11), neither from Oracle or OpenJDK sources. Here are the commands to install any unlicensed Java version from the web. I chose Java 14.

sudo apt update && sudo apt upgrade
sudo add-apt-repository ppa:linuxuprising/java
sudo apt update
sudo apt install oracle-java14-installer

Check that it worked with:

java --version
javac -version

Install PostgreSQL on Ubuntu server

Get dependency for PostgreSQL:

sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main" >> /etc/apt/sources.list.d/pgdg.list'

where “bionic” is the Ubuntu version. Check yours with lsb_release -c if needed and adapt above line.

Import the repository signing key:

wget --verbose -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add – 

Install (here installing PostgreSQL version 11):

apt-get update
apt-get install postgresql-11

Alternately, this shell script will automate the repository setup. The script is included in the postgresql-common package in Debian and Ubuntu, so you can also run it straight from there:

sudo apt install postgresql-common
sudo sh /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh

Now, try to connect to PostgresSQL. Default user’s name is “postgres”:

su - postgres

Exit Postgres and set the environment variables (change version if necessary):

export PATH=$PATH:/usr/lib/postgresql/11/bin
export PATH=$PATH:/usr/lib/postgresql/11/main
export PATH=$PATH:/var/lib/postgresql/11/main
export PATH=$PATH:/var/run/postgresql

Check your installation and variables:

postgres --version

You might want to make PostgreSQL start at server boot:

update-rc.d postgresql enable
systemctl enable postgresql

I’m not going into details on how to allow remote connections from other services here because I installed the database and the application on the same server, which was good enough for my use case.

You can always check the status of your database with the following commands. The first one gives the pg server status (connections, ports…), the second gives information on the service (if you created a service, as shown few lines above).

pg_isready                 
/etc/init.d/postgresql status

You probably want to make an automatic back-up of your database!

su – postgres
(postgres@server:~$)
mkdir -p ~/postgres/backups
find -name “backups”
crontab -e

The structure of the instructions is explained in the file that opens. Here’s an example of how to make a back-up of your database every Sunday at midnight. Beware, the server has to be up and running at that time, otherwise the back-up will be skipped!

0 0 * * 0 pg_dump -U postgres my_db_name > ~/postgres/backups/my_db_name.bak

Deploy Spring Boot back-end on Ubuntu

You can get your repository from a public GitHub repo like so:

wget --no-check-certificate https://github.com/User/Repository/archive/master(branchname).tar.gz

Or download the tar from your GitLab repo and copy it to the remote Ubuntu server. I tend to like GitLab a lot more because it gives you that choice to download a .tar or .tar.gz directly! Beware that destination file must have write permission from “other” group (chmod o+w remoteFolder).

scp full/path/myLocalFile remoteuser@remoteserver:/remoteFolder/

where “myLocalFile” is the tar.gz downloaded from GitLab and “remoteFolder” is the destination folder on the server.

Extract source code from the archive:

tar -xf myRepo.tar.gz

Installing and configuring maven for deployment

I worked with maven as a dependency manager.

Install maven:

apt-get install maven

You’ll need to make some changes in your pom.xml file, the one that you unzipped on your server (you might make the changes before copying to the Ubuntu server, but what’s more fun than using vim honestly?). This allows to make an executable jar.

<groupId> ... </groupId>
<artifactId> ... </artifactId>
<version>0.0.1</version>
<name> ... </name>
<packaging>jar</packaging>
 
<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
            <execution>
                <goals>
                    <goal>repackage</goal>
                </goals>
                <configuration>
                    <executable>true</executable>
                    <classifier>spring-boot</classifier>
                    <mainClass>
                       
[groupId.artifactId].Application
                    </mainClass>
                </configuration>
            </execution>
        </executions>

    </plugin>
</plugins>

Place yourself in the file where the pom.xml is and build the jar:

mvn clean package

Then try running the application:

mvn spring-boot:run

Make a service from the jar

Now that you have an executable jar, you want to make a service so it can run, start at boot, stopped and restarted…

Check that you file is executable, else change it with chmod 755 yourApp.jar

I created a script myapp.service in file /etc/systemd/system with following content:

[Unit]
Description=My Spring Boot application
After=syslog.target
 
[Service]
ExecStart=/path/to/your-app.jar 
SuccessExitStatus=143 
 
[Install] 
WantedBy=multi-user.target

When saved, try:

service myapp start 
service myapp status

If everything ok, you’d see the Spring Boot start logs, or an error log.

If you want the app to start at server boot, type:

systemctl enable myapp

You can now check your endpoints with the curl command, you’ll see an html/JSON representation or a JSON message saying that you have to be connected if you put some secured access on your application.

Deploy Angular front-end project on Ubuntu

You’ll have to get your repository just as you did for Spring Boot. Be sure you changed your URIs from http://localhost:8080/blabla to /blabla, because your Angular front-end won’t be communicating directly with your Spring Boot back-end, we will use a web server with a proxy.

When I first tried to deploy, I was convinced I just had to install everything just as it is on my dev machine and it would work, but spoiler alert, it didn’t.

You need a web server to render the frontend. I used Apache2 to serve my application.

Installations to be able to run Angular:

apt install nodejs
apt install npm
curl -sL https://deb.nodesource.com/setup_12.x | bash -
apt-get install -y nodejs
node --version
npm install -g @angular/cli

Move to the folder you previously transferred and unzipped, where [src] is, and build:

ng build --prod

If it works, a [dist] folder is created. If you’re working with Ionic, please note the folder will be called [www] instead of [dist].

You might encounter problem “@angular-devkit/build-angular”, which I solved with following lines:

npm install --save-dev @angular-devkit/build-angular
npm update

Install and use Apache2 as web server for your Angular application

Install Apache2 and allow access from anywhere (if your application is meant to be public). We then configure the Ubuntu firewall (ufw). If the app list doesn’t show Apache as allowed application (from outside), type line 3. Type line 4 to check that firewall is up and running. Type line 5 if it wasn’t, and check again.

sudo apt install apache2
sudo ufw app list
(sudo ufw allow 'Apache')
(sudo ufw status)
(sudo ufw enable)

Check that Apache server is “active running”:

sudo systemctl status apache2

If everything seems ok, check from another computer that you can see the default Apache2 page by just typing the IP address in a browser.

Set up a virtual host:

sudo chmod -R 755 /var/www/domain_name

And configure the proxy from front to back:

sudo nano /etc/apache2/sites-available/domain_name.conf

Use 80 for http or 443 for https:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName your_domain
    ServerAlias your_domain
    Alias /mypath /var/www/your_domain
    DocumentRoot /var/www/your_domain
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
	ProxyPass /api/	http://localhost:8080/api/
	ProxyPassReverse /api/ http://localhost:8080/api/
	ProxyRequests	Off
</VirtualHost>

Enable your new domain and disable the default one:

a2ensite your_domain.conf
a2dissite 000-default.conf

Check config:

sudo apache2ctl configtest

You might encounter error “Invalid command ProxyPass”. If so, enable proxy with:

a2enmod proxy_http
systemctl restart apache2

Now, copy the Angular [dist] folder CONTENT to the newly created domain file. The index file must be in /domain_name.

cp -a ./angular/dist/[folder]/. /var/www/domain_name

When I build, I get a folder with my app name in the dist folder. That’s the content of that folder I need to copy into my new “domain_name” folder.

Finally, configure routing:

sudo nano /var/www/domain_name/.htaccess

Just copy/paste:

RewriteEngine on
#Don’t rewrite files or directories
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule ^- [L]
#Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ /index.html [L]

Well, at this point, you should be able to access your application from any computer by typing its IP in a browser, and your frontend should be able to communicate with your bac-end, which is connected to your PostgreSQL database.

2 thoughts on “How to deploy a Spring Boot – Angular application on Ubuntu server

  1. I tried this – it didn’t work, I also don’t understand how it is even supposed to work since Angular index.html is download on my browser on my local machine, following which it sends a request to backend with a url /blabla but without prepending the ip of EC2 instance such call can never work. I don’t undestand how setting up proxies can help if the call is made from my machine that has no idea about Apache2 installed on ec2

    1. You access to the website served via Apache server from your machine, so it’s aware of the server address. You’re not making a direct call from your browser, your browser contacts the web server, then the web server calls to back-end service. This, of course, if both are on the same server machine. I’ve used this deployment template many times. Only thing I can think of if it doesn’t work is with the URIs on Angular or Java side.

Leave a Reply

Your email address will not be published. Required fields are marked *

Skip to content