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 the way to be able to create my own step-by-step tutorial. Because guess what, none of the ones I found on the Internet worked as is and I had to mix a lot of resources all together to make it work.
Since then, I reused my own tutorial to deploy a Spring Boot + Angular and Ionic application and it worked like a charm. So maybe if you have the same config as I have, this might work for you too!
This tutorial aims at deploying a web application with a Java / Spring Boot REST API, an Angular and/or Ionic front-end user interface (I’m not going into Ionic details here), 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 as I have very little knowledge, even though I’d like to give Docker (and maybe Jenkins) a try for my next deployment. I also chose the “old school”, server way, to deploy, first to understand it well, second because it seemed the easiest, somehow… and finally because I don’t get to “do” much Linux while I enjoy it, so it was an opportunity. I’ll also try new ways to deploy apps shortly (Cloud solutions namely). But this tutorial is not about it.
Also, I didn’t go into all details about Linux commands (for writing in files or checking folders contents for example) because if you know nothing about Linux it would be good to take a little course on the basics (file systems, basic commands…) before trying this, or anything else on a Linux machine. Linux is more permissive and lends more power and responsibilities (thank you!) than Windows, but that’s also more “dangerous” so better know the beast before doing anything with it.
I cross my fingers for this tutorial to work for you without having to compile a ton of other information!
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 a full, independent (REST API model) back-end API with latest LTS Spring Boot version and Java14, and front-end interface consuming that API with the latest Angular version. 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 hadn’t appeared on version 18.04 (always use latest LTS when possible!), I included that step in the tutorial.
If you’re working on a Windows machine, make sure you have a SSH client installed (PuTTy is quite a reference…). Also, gather the access information to connect to the remote Ubuntu server (if you’ve rented a remote server like I did).
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. I guess if there’s no security issues you might even create a script that inputs the pwd as well…
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.
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 (which also prevented me from working with Jenkins, therefore the old school deployment tactic…). So this method works to install any unlicensed Java version from the web. I chose Java 14 but you might just change the version number.
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
This might need adjustments depending on your Postgres version!
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, which you can of course change):
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 (for a Power BI or tier application connection or to manage your database remotely e.g.) here because I installed everything on the same server and didn’t intend to give remote accesses. You will find plenty of answers about this on the Internet and that’s quite simple to do.
You can always check the status of your database with those commands, first giving you pg server status (connections, ports…), the second giving you information on the service (if you made 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 work with maven as a dependency manager, so you might do a little research is you use gradle. Also, I use a very simple configuration because this is about personal projects, you might want to configure production settings more cleanly for a professional project or if heavy maintenance is foreseen. This is probably the minimum acceptable config.
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 have done it before, but what’s more fun than using vim or nano 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 (in the Linux way) 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 (the ones you’d see in the console of your IDE), 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 as on my dev machine and it would do the trick, but spoiler alert, that won’t. You need a web server to render the front end, so your Angular front-end itself won’t be working on the “localhost” itself. I chose Apache2 to serve my application which seems a pretty common choice (common = well documented, well tested… don’t make it too exotic at first!).
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
Place yourself in the file you previously transferred and unzipped, where [src] file is, and build:
ng build --prod
If it works, a [dist] file is created. If you’re working with Ionic, please note the file 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, otherwise you’ll have to make some config). Here we will 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 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”, enable proxy with:
a2enmod proxy_http
systemctl restart apache2
Now, copy the Angular [dist] file 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. Don’t folder the /. that copies all the content, because copying the folder itself doesn’t work.
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 front-end should be able to communicate with your back-end which works with your PostgreSQL database.
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
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.