Surfing in Digital Ocean with Angular, .Net Core Web-API, MySql on Ubuntu + Nginx.

Nipun Kularathne
13 min readMar 9, 2021

--

This weekend I tried out something different. I had a long-overdue task to finish in my private backlog. I wanted my simple Angular CRUD application to be accessed virtually from anywhere in the world and shut down my always-running IIS server on my PC. This simple Angular application was talking to a .Net Core WebAPI backend that connects to another MySQL server instance using Entity Framework. In simple terms I wanted all these to be migrated to the cloud.

System architecture

Choosing Digital Ocean

I had several choices moving to the cloud. Microsoft Azure and AWS were the most reliable choices and then there is this cheap but highly popular choice among developers: “Digital Ocean” (www.digitalocean.com). Since my application is on .Net Core and MySql, it can be run on any platform including Linux. Therefore a good service provider who can offer a cheap and reliable Linux Virtual Machine would be a great choice for me. Even though this is a simple system I am trying to migrate, it would be always good to know what I am getting into. Therefore, before I choose Digital Ocean I did some background checks on the reliability and the readiness of this cloud service provider, and all those came out positive. In fact, I found out it is very reliable, easy to set up, and has a very fair charging policy as well.

Now that Digital Ocean is my choice (maybe yours too), let’s see what I had to do to perform this migration. As I am going with Linux which comes free, first I had to provision a Linux virtual machine (Virtual Private Server) on Digital Ocean (DO). In DO it’s called a droplet. You have various choices for droplets there. I choose Ubuntu droplet with 1GB of RAM and 25Gb of space as it's just 5$ per month. In fact, when you first sign up in DO you get 100$ free credit for 60 days and you can do quite a lot with it. Here are the steps for creating the droplet I mentioned earlier.

  1. Sign up in Digital Ocean (You can use Google sign in even)
  2. Create a new Project
  3. Create a New Droplet by choosing the menu option and selecting Ubuntu in Distributions.
Menu
Distibution Selection

I selected the 5$/mo plan (The cheapest of all)

Plan selection

Give a name and select password authentication(for now let's not go with creating ssh keys), finally click Create Droplet button. The droplet will be created in a minute.

Our droplet is now up and running — It runs Ubuntu by choice. But how are we going to access it? That is done through a firewall. Let's create one by clicking on the Create button on the site again and selecting Cloud firewalls. When you get the firewall rules window add the below inbound rules. The SSH rule will be already there as a default addition.

Inbound firewall rules

We now have allowed ssh, HTML, and MySql access to our droplet hence we can use any ssh client and access the droplet. In windows, I used Putty utility to ssh into the droplet. You can download it from https://www.putty.org/

After downloading and installing Putty launch the client and give the droplets public IP address and port as 22 to the Putty client to ssh into the droplet. You can find the droplets' public IP address on the droplet page.

Droplet public IP

Below is how you ssh using Putty utility

ssh using Putty utility

Use root as the username and the droplet password you gave when creating the droplet, as the password.

After logging in, the first thing we should do is adding a user for our work as it's not a good practice to use the root user for all the work we are going to do. Why not have a user with your name then?

root@angulawebapi:~# adduser nipun

Give a password and fill out the other information and you will have your new user created. But this user needs Admin privileges to perform our next actions. Let's give it some powers by adding this user to the sudo group.

root@angulawebapi:~# usermod -aG sudo nipun

user nipun is now added to sudo group and can perform superuser actions with sudo command.

close the current Putty session and open up a new session to log in with the new user created. From now onwards we will be using this user for all the work.

Installing MySQL on the droplet

Now that we have droplet up and running and all the users in place we can make use of this nice tutorial by the Digital Ocean community to install MySQL on our Ubuntu droplet. I used Ubuntu version 20.04 but there is no difference in the installation process from the below tutorial. The only thing to note here is that to give a proper name according to your wish when creating MySQL user during the tutorial.

With the MySQL installation and server running on the droplet you can even use this as your cloud MySQL server for any of your database needs for any client application running on an on-prem server that has an active internet connection. For that let's go ahead and enable remote access to this MySQL server from outside the droplet. You need to edit the MySQL config file to do this.

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

this will open up the config file in the Putty terminal, Edit the bind-address and set it to the droplets public IP and save and exit. You can save while exiting the editor by pressing Ctrl + X and then typing Y.

MySQL bind-address

For the changes to take effect run the below command to restart MySQL server.

sudo systemctl restart mysql

Now we have an IP address assigned to our MySQL server and it runs on the default port — 3306 of our droplet. If you try to connect to this IP and port from any computer on the internet still it won't work. That's because you need to tell MySQL which IP addresses it needs to allow connections. Get back to MySQL prompt with:

 mysql -u root -p

Now, let's set the created MySQL user’s host as the connecting computer's IP. We can check the current configuration by running the below command. Let's assume the user we created for MySQL is “appuser”

mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;

note that the host is set to localhost for the appuser here. This is the thing we need to change and assign with our connecting machine's public IP address.

mysql> update mysql.user set Host=’12.134.149.235' where user=’appuser’;

The above command will set the host of the appuser to the given IP address. But most of the public IP addresses are dynamically assigned by the service providers and are likely to change when you restart the router or when the connection changes. Therefore it's cumbersome to change this host value whenever your client machine IP address changes. Therefore we need a way to allow all the IP addresses to MySQL server. There is a risk in doing this but you can do it by assigning “%” as the host.

mysql> update mysql.user set Host=’%' where user=’appuser’;
Allowing all IP addresses MySQL

Now if you telnet your droplets’ public IP address and the MySQL port(3306) you can see it works without any error message. This completes a MySQL server at your service in the cloud. Exciting right?

But this is not what we are here to do. Let's go ahead and make some room for our Web API and Angular apps in the cloud.

We need a server to run our Angular application. In-fact if we need to expose the WebAPI endpoints to the outside world we need a server for that as well. For this purpose, I used the Nginx server. You can Just google and see what Nginx is capable of in this context. Let's go ahead and install it in our Ubuntu droplet.

Installing Nginx

sudo apt updatesudo apt install nginx

after installing Nginx it automatically starts running on the droplet. Just like a normal web server you should be able to access the webserver landing page using any machine by typing the public IP of the droplet on the browser.

Nginx landing page

if you cannot access the landing page it's better to check the server status by using the following command.

systemctl status nginx

The success message would look like this

Nginx running

We need two server spaces to host our requirements in the droplet. One for the Angular client and the other for the .Net Core Web API back end. In Nginx, we can use the concept of Server Blocks to make this work using one server. In this, we create two separate directories (Folders) and put our files from the two applications in each different directory. When a request comes from outside we use Nginx configurations to identify the request and direct it to the correct application running on the designated directory. In fact for the WebAPI back end, we don't need a separate folder as it runs on a designated port and we can redirect the requests to that port using Nginx without needing a directory location. But for this tutorial let's create 2 server blocks and put these apps separately in each.

First cd into the webroot directory of Nginx

cd /var/www/html

now create two directories with suitable names for our 2 apps.

sudo mkdir -p angular
sudo mkdir -p webapi

change the ownership of the directories to the logged-in user

sudo chown -R $USER:$USER angular
sudo chown -R $USER:$USER webapi

add permission to the directories created

sudo chmod -R 775 angular
sudo chmod -R 775 webapi

Now we have to tell Nginx that we have different folders for two server blocks.

To do this we need to create two configuration files inside the Nginx sites-available directory with the same names we created the folders earlier. The sites-available directory is in the following path.

/etc/nginx/sites-available/

Let's create two files using the below commands.

sudo nano /etc/nginx/sites-available/angular
sudo nano /etc/nginx/sites-available/webapi

For the time being, add the below configuration in the angular file. Let's not touch the webapi file in this tutorial.

server {
listen 80;
listen [::]:80;
root /var/www/html/angular;
index index.html index.htm;
server_name yourdomain.com www.yourdomain.com;
location / {
try_files $uri $uri/ =404;
}
}

The above sample tells the Nginx if a request is coming as yourdomain.com or www.yourdomain.com use the files in the /var/www/html/angular directory to serve the request.

After saving this file you need to create a link to nginx/site-enabled directory as Nginx will look in that location for the configuration file in runtime. To do that use the below commnad.

sudo ln -s /etc/nginx/sites-available/angular /etc/nginx/sites-enabled/

Installing .Net Core Runtime in Ubuntu

Now the directory and the config file for Angular application hosting are ready we have to make the environment ready for the WebAPI to run. For this, we need to install .NetCore runtime in the droplet. There is a comprehensive article on the official Microsoft page to do this. Make sure you select and install the correct .NetCore runtime version you have used in the WebAPI project. Be mindful to use the commands in the correct Ubuntu version from the list as well.

Tip: To match the .Net Core version I used in the project I changed the .Net Core version of the installation command from 5.0 to 3.1 like below.

Change the above-highlighted version to the correct version

After the installation, it's time to move your build files from the local machine to the designated directory in the droplet. If you want to build the .NetCore application use the below command

dotnet publish --configuration Release

This will create the build files in the bin\Release\netcoreappx.x\publish folder. Use an FTP client such as WinSCP to upload the files to the droplet’s webapi folder we created.

Upload window — WinSCP

Additional Nginx Configurations

After uploading the files we need to configure the Nginx to forward the API requests to the Web API server. The default web API runs on port 5000 in localhost. My Angular application calls the back end with the following pattern

http://baseurl/api/resource

Therefore I had to match this pattern and forward the API requests to the localhost:5000 in my Ubuntu droplet. Let’s go ahead and configure the Nginx server to do that. What I can easily do here is that forward any request which has the api/ or API/ in the request URL to localhost:5000 which the Web API would run on. To do that let's open up the configuration file again. (You can have different ways of doing this, this is the way I tried out)

sudo nano /etc/nginx/sites-available/angular

Now let's update the existing configuration as below.

server {
listen 80;
server_name yourdomain.com *.yourdomain.com;
location / {
root /var/www/html/angular;
try_files $uri $uri/ /index.html;
}
location ~* /api {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

If you look at the above configuration, it does 2 things.

server {
listen 80;
server_name yourdomain.com *.yourdomain.com;

The server listens on port 80 for the requests which are coming with the domain names like “yourdomain.com”

location / {
root /var/www/html/angular;
try_files $uri $uri/ /index.html;
}

The “location /” depicts all the HTTP requests for port 80 that would be served with the server block running on the /var/www/html/angular directory.

2.

location ~* /api {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

By defining the “location ~* /api”, it checks the request URL of the HTTP request and matches the phrase /api or /API ( ~* denotes match with ignore case) in the URL and forwards matching requests to the localhost:5000 where our Web API application is running.

With the above configuration, the client requests for Angular application will be served with the angular folder files while the requests that come as REST API calls for the Web API back end (Requests that has /api or /API in the URL) would be forwarded to the localhost:5000 Web API application to process and respond accordingly.

Now after adding this configuration you can check if the configuration file syntax is correct using the below command

sudo nginx -t

If this turns out correct restart the Nginx server using the below command to newly added configurations to take effect.

sudo systemctl restart nginx

Building and adding the Angular app to the droplet

Now it's time to build our angular app and upload the files to the droplet.

ng build --prod

Get the files in the dist directory and use the FTP client to upload the files to /var/www/html/angular directory.

Since we have defined the domain in our configuration files (www.yourdomain.com) we can do a little hack in the hosts file in our local machine to mimic the request URL as above. Edit the hots file and add an entry like below. The hosts file is located in C:\Windows\System32\drivers\etc\hosts

Hosts file entry

If you have a domain or subdomain you can directly forward that to the Droplets public IP address to achieve the same result. Make sure to update the Nginx configuration file’s server_name directive with the correct domain name when you do this.

If you access www.yourdomain.com in your browser, now you should be able to get the home page of your Angular application in your browser. But still, we haven't started our WebAPI back end to receive the calls from the Angular application. Therefore before launching the angular application you need to start the web API application manually in the droplet so that the Rest API calls from the Angular application would be received and responded accordingly. To do that go to the folder where we uploaded the WebAPI backend files.

cd /var/www/html/webapi

find the correct dll name by looking at the application dlls in the directory. In my case it was webapi.dll. The command is simple

dotnet WebApi.dll
Running Web API

This will start the .Net Core Web API application and it will run on the localhost:5000 (Default port). If you want to change this port I found the below link helpful. You need to do the change and rebuild the . Net Core application and replace the files you uploaded in the webapi folder.

Tip: Make sure to update the base URLs of all API calls in the Angular application with the URL you use to access the droplet.

Now you should be able to launch the Angular application using www.yourdomain.com and the Angular application should be able to access the Web API back end by calling the Rest API endpoints with the below URL pattern.

http://yourdomain.com/api/resourcename

That's it !!

--

--

No responses yet