Prerequisites
Before we get started, we need to make sure Nginx web server is set up. Use the below link to set up Nginx server on Ubuntu.
- How to install Nginx on Ubuntu Linux.
- Ensuring that the domain name is correctly directed to the appropriate server.
NOTE: DNS setup is beyond the scope of the article. Please ensure domain name is pointing to correct server before proceeding with the below guide.
Step 1: Install .Net SDK 7.0 On Ubuntu 22.04
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-7.0
Output:
rahil@ubuntu:~$ sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-7.0
[sudo] password for rahil:
Hit:1 http://mirrors.digitalocean.com/ubuntu jammy InRelease
Get:2 http://mirrors.digitalocean.com/ubuntu jammy-updates InRelease [119 kB]
Hit:3 https://repos-droplet.digitalocean.com/apt/droplet-agent main InRelease
Get:4 http://mirrors.digitalocean.com/ubuntu jammy-backports InRelease [109 kB]
Get:5 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
Get:6 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [722 kB]
Get:7 http://security.ubuntu.com/ubuntu jammy-security/main Translation-en [160 kB]
Get:8 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [767 kB]
Get:9 http://security.ubuntu.com/ubuntu jammy-security/restricted Translation-en [123 kB]
Fetched 2109 kB in 6s (379 kB/s)
Reading package lists... Done
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
aspnetcore-runtime-7.0 aspnetcore-targeting-pack-7.0 dotnet-apphost-pack-7.0 dotnet-host-7.0 dotnet-hostfxr-7.0
dotnet-runtime-7.0 dotnet-targeting-pack-7.0 dotnet-templates-7.0 liblttng-ust-common1 liblttng-ust-ctl5 liblttng-ust1
netstandard-targeting-pack-2.1-7.0
The following NEW packages will be installed:
aspnetcore-runtime-7.0 aspnetcore-targeting-pack-7.0 dotnet-apphost-pack-7.0 dotnet-host-7.0 dotnet-hostfxr-7.0
dotnet-runtime-7.0 dotnet-sdk-7.0 dotnet-targeting-pack-7.0 dotnet-templates-7.0 liblttng-ust-common1 liblttng-ust-ctl5
liblttng-ust1 netstandard-targeting-pack-2.1-7.0
0 upgraded, 13 newly installed, 0 to remove and 61 not upgraded.
Need to get 136 MB of archives.
...
Step 2: Install .Net Runtime
sudo apt-get update && \
sudo apt-get install -y aspnetcore-runtime-7.0
Output:
rahil@ubuntu:~$ sudo apt-get update && \
sudo apt-get install -y aspnetcore-runtime-7.0
Hit:1 http://mirrors.digitalocean.com/ubuntu jammy InRelease
Hit:2 https://repos-droplet.digitalocean.com/apt/droplet-agent main InRelease
Hit:3 http://mirrors.digitalocean.com/ubuntu jammy-updates InRelease
Hit:4 http://mirrors.digitalocean.com/ubuntu jammy-backports InRelease
Hit:5 http://security.ubuntu.com/ubuntu jammy-security InRelease
Reading package lists... Done
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
aspnetcore-runtime-7.0 is already the newest version (7.0.110-0ubuntu1~22.04.1).
aspnetcore-runtime-7.0 set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 61 not upgraded.
As an alternative to the ASP.NET Core Runtime, you also can install the .NET Runtime. The package doesn’t include ASP.NET Core support. You can replace aspnetcore-runtime-7.0
in the previous command with dotnet-runtime-7.0
.
sudo apt-get install -y dotnet-runtime-7.0
In my case, I will stick with option one as I am using ASP.NET Core in my WebApp.
Step 3: Download ASP.NET WebApp from GitHub
In this tutorial, I am using a sample app I created here. You can setup CICD pipeline to build and deploy the package directly on Ubuntu web server if you prefer. For the purpose of this tutorial, I will follow manual download, unzip, and staging of package to hosting path.
wget https://github.com/tekspace-io/DotNetWebApp/archive/refs/heads/master.zip
Output:
rahil@ubuntu:~$ wget https://github.com/tekspace-io/DotNetWebApp/archive/refs/heads/master.zip
--2023-08-31 16:24:22-- https://github.com/tekspace-io/DotNetWebApp/archive/refs/heads/master.zip
Resolving github.com (github.com)... 192.30.255.112
Connecting to github.com (github.com)|192.30.255.112|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/tekspace-io/DotNetWebApp/zip/refs/heads/master [following]
--2023-08-31 16:24:22-- https://codeload.github.com/tekspace-io/DotNetWebApp/zip/refs/heads/master
Resolving codeload.github.com (codeload.github.com)... 192.30.255.121
Connecting to codeload.github.com (codeload.github.com)|192.30.255.121|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/zip]
Saving to: ‘master.zip’
Extract zip file that was downloaded on your Ubuntu server.
Install unzip, if the command doesn’t exist.
sudo apt-get install unzip
We will execute unzip
command to extract master.zip
file that we downloaded from GitHub repository.
unzip master.zip -d webapp
Output:
rahil@ubuntu:~$ unzip master.zip -d webapp
Archive: master.zip
bd1ef2df340c39be5bebd55dd2f5938dd3679677
creating: webapp/DotNetWebApp-master/
inflating: webapp/DotNetWebApp-master/.gitattributes
inflating: webapp/DotNetWebApp-master/.gitignore
inflating: webapp/DotNetWebApp-master/DotNetWebApp.sln
creating: webapp/DotNetWebApp-master/DotNetWebApp/
inflating: webapp/DotNetWebApp-master/DotNetWebApp/DotNetWebApp.csproj
creating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/
inflating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Error.cshtml
inflating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Error.cshtml.cs
inflating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Index.cshtml
inflating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Index.cshtml.cs
inflating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Privacy.cshtml
inflating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Privacy.cshtml.cs
creating: webapp/DotNetWebApp-master/DotNetWebApp/Pages/Shared/
...
Now navigate to the solution path ~/webapp/DotNetWebApp-master
cd ~/webapp/DotNetWebApp-master
Step 4: Publish DotNet code
Run dotnet publish to compile the code and publish.
dotnet publish --configuration Release
Output:
rahil@ubuntu:~/webapp/DotNetWebApp-master$ dotnet publish --configuration Release
MSBuild version 17.4.8+6918b863a for .NET
Determining projects to restore...
Restored /home/rahil/webapp/DotNetWebApp-master/DotNetWebApp/DotNetWebApp.csproj (in 102 ms).
DotNetWebApp -> /home/rahil/webapp/DotNetWebApp-master/DotNetWebApp/bin/Release/net7.0/DotNetWebApp.dll
DotNetWebApp -> /home/rahil/webapp/DotNetWebApp-master/DotNetWebApp/bin/Release/net7.0/publish/
Upon publish, you will notice the runtime code was created under ~/webapp/DotNetWebApp-master/DotNetWebApp/bin/Release/net7.0
. We will copy the files from the published location to /var/www/demo_tekspace_io/
.
NOTE: I am using
/var/www/demo_tekspace_io/
path based on Nginx & let’s encrypt tutorial. If you would like to updatedemo_tekspace_io
to your preferred folder name, you can do so as per your requirements.
Step 5: Stage published code to Nginx hosting path
Copy net.7.0
folder from ~/webapp/DotNetWebApp-master/DotNetWebApp/bin/Release
to /var/www/demo_tekspace_io/
.
sudo cp -r ~/webapp/DotNetWebApp-master/DotNetWebApp/bin/Release/net7.0/ /var/www/demo_tekspace_io/
To confirm if net7.0 was copied to the hosting path, execute ls -l /var/www/demo_tekspace_io/
:
rahil@ubuntu:~$ ls -l /var/www/demo_tekspace_io/
total 8
drwxr-xr-x 2 rahil rahil 4096 Aug 30 21:20 html
drwxr-xr-x 3 root root 4096 Aug 31 18:13 net7.0
Run the below command to change ownership of net7.0
from root to logged in sudo user.
sudo chown -R $USER:$USER /var/www/demo_tekspace_io/net7.0
You only need to do this if the folder is owned by root user.
Step 6: Configure Nginx to host DotNet 7.0
If you have followed along our previous tutorial to set up Nginx server and Let’s Encrypt guide. Below is the server block setup by Nginx and CertBot. We will modify this file and add dotnet-related configuration to host the WebApp we are referencing for this tutorial.
rahil@ubuntu:~$ cat /etc/nginx/sites-available/demo_tekspace_io
server {
root /var/www/demo_tekspace_io/html;
index index.html index.htm index.nginx-debian.html;
server_name demo.tekspace.io www.demo.tekspace.io;
location / {
try_files $uri $uri/ =404;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/demo.tekspace.io/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/demo.tekspace.io/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = demo.tekspace.io) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name demo.tekspace.io www.demo.tekspace.io;
return 404; # managed by Certbot
}
Next, we will add a reverse proxy configuration to tell Nginx web server to serve all the traffic that is served on port 80 or 443 to localhost with port 5000.
Edit file with nano editor:
sudo nano /etc/nginx/sites-available/demo_tekspace_io
Add the following content inside location / {
proxy_pass http://127.0.0.1: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;
Next, replace root /var/www/demo_tekspace_io/html;
with root /var/www/demo_tekspace_io/net7.0;
Save the file. Your server block content should look like this:
server {
root /var/www/demo_tekspace_io/net7.0/publish/wwwroot;
index index.html index.htm index.nginx-debian.html;
server_name demo.tekspace.io www.demo.tekspace.io;
location / {
try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1: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;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/demo.tekspace.io/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/demo.tekspace.io/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = demo.tekspace.io) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name demo.tekspace.io www.demo.tekspace.io;
return 404; # managed by Certbot
}
Now, we will test Nginx
configuration by executing the below command:
sudo nginx -t
If you see below output, that means your configurations are set correctly.
rahil@ubuntu:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Step 7: Create service to run .net WebApp in the background
First, we will create a service definition file:
sudo nano /etc/systemd/system/kestrel-webapp.service
Enter below configuration and make changes to hosting path as needed:
[Unit]
Description=Example .NET Web API App running on Linux
[Service]
WorkingDirectory=/var/www/demo_tekspace_io/net7.0/publish
ExecStart=/usr/bin/dotnet /var/www/demo_tekspace_io/net7.0/publish/DotNetWebApp.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-example
User=rahil
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
Save the file and enable the service:
sudo systemctl enable kestrel-webapp.service
Output:
rahil@ubuntu:~$ sudo systemctl enable kestrel-webapp.service
Created symlink /etc/systemd/system/multi-user.target.wants/kestrel-webapp.service → /etc/systemd/system/kestrel-webapp.service.
Next, we will start the service:
sudo systemctl start kestrel-webapp.service
Next, we will check the status of the service to make sure it has started successfully.
sudo systemctl status kestrel-webapp.service
Output:
rahil@ubuntu:~$ sudo systemctl status kestrel-webapp.service
● kestrel-webapp.service - Example .NET Web API App running on Linux
Loaded: loaded (/etc/systemd/system/kestrel-webapp.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2023-08-31 19:57:03 UTC; 15s ago
Main PID: 30099 (dotnet)
Tasks: 16 (limit: 512)
Memory: 22.3M
CPU: 383ms
CGroup: /system.slice/kestrel-webapp.service
└─30099 /usr/bin/dotnet /var/www/demo_tekspace_io/net7.0/publish/DotNetWebApp.dll
Aug 31 19:57:03 ubuntu systemd[1]: Started Example .NET Web API App running on Linux.
Aug 31 19:57:03 ubuntu dotnet-example[30099]: info: Microsoft.Hosting.Lifetime[14]
Aug 31 19:57:03 ubuntu dotnet-example[30099]: Now listening on: http://localhost:5000
Aug 31 19:57:03 ubuntu dotnet-example[30099]: info: Microsoft.Hosting.Lifetime[0]
Aug 31 19:57:03 ubuntu dotnet-example[30099]: Application started. Press Ctrl+C to shut down.
Aug 31 19:57:03 ubuntu dotnet-example[30099]: info: Microsoft.Hosting.Lifetime[0]
Aug 31 19:57:03 ubuntu dotnet-example[30099]: Hosting environment: Production
Aug 31 19:57:03 ubuntu dotnet-example[30099]: info: Microsoft.Hosting.Lifetime[0]
Aug 31 19:57:03 ubuntu dotnet-example[30099]: Content root path: /var/www/demo_tekspace_io/net7.0/publish
If you see the service status as active (running), that means all your setup is configured successfully.
Before we end the tutorial, we need to make sure the Nginx has been reloaded to apply new changes we make to Nginx server block.
sudo systemctl reload nginx
Once the Nginx service has been reloaded, browse the app via the URL you set up in the server block. In my case, I will browse demo.tekspace.io
.
You will see the HTML page load on your browser. If you see all the css and js file load correctly, that means the app is configured successfully to run on Nginx web server.