In this article, we are going to learn how to deploy an ASP.NET Core web application to the Ubuntu Linux server with Nginx.

The introduction of .NET Core opened the cross-platform doors, allowing developers to build and deploy applications on Linux systems. At the time of writing, .NET Core is available on Alpine, CentOS, Debian, Fedora, SUSE, and Ubuntu.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Setting Up the ASP.NET Core Web Application

Let’s create a new ASP.NET Core Web Application in Visual Studio by first creating a new project by selecting the “ASP.NET Core Web Application” template. Provide the project details, leave the settings by default, and click “Create” to generate the project. Visual Studio will create the project files and provide us with a solution explorer to start building the web application.

Now that we have a functioning app, let’s start the preparation of the Ubuntu environment.

Install the ASP.NET Core Runtime on Ubuntu 

To run our system, we’re using a virtual machine with Ubuntu 22.10 operating system through a local instance of VirtualBox, but the steps are equally applicable for servers hosted on the cloud or on-premises.

We have the option of installing either the ASP.NET Core SDK or the runtime. The SDK is required when developing an ASP.NET Core app, and the runtime comes bundled with it. Since we’ll only be deploying the application, we only need the runtime.

Let’s open a terminal on the server and run the commands to install the runtime:

sudo apt-get update && \
  sudo apt-get install -y aspnetcore-runtime-7.0

This command first updates the local package index and then installs the aspnetcore-runtime-7.0 package. When it’s complete, we can run dotnet --info to confirm which versions have been installed:

dotnet --info

  Version:      7.0.5
  Architecture: x64
  Commit:       8042d61b17

.NET SDKs installed:
  No SDKs were found.

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.5 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.5 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]

This shows that there are no SDKs installed, but we now have the Microsoft.AspNetCore.App and Microsoft.NETCore.App runtimes.

We are now ready to run the ASP.NET Core app on Ubuntu, so let’s deploy the app.

Deploy the ASP.NET Core Web App to Linux

We’re going to publish the app, copy the deployment files to the server, and then run it on a web server.

Publish the App

First, we need to publish the app to a folder. In Visual Studio, publish the project to create a new publish profile. When the profile has been created, publish the deployment files to the specified folder.

Copy the Files to the Linux Server

Next, we need to copy the deployment files to the Ubuntu server. Before we move the files, let’s create the destination folder on the server and set the permissions so that we can add files to it:

cd /var/www
sudo mkdir app
sudo chmod 666 app

Now, we are ready to copy the files.  Many file transfer tools exist, such as FileZilla, WinSCP, etc. Let’s use WinSCP as an example for now.

After logging in to the Ubuntu server, the left pane in WinSCP allows us to browse the local filesystem, while the right pane allows us to browse the server filesystem. On the left pane, let’s navigate to the folder where the files were published. On the right pane, navigate to /var/www/app. Select all the files on the left pane, then drag and drop them onto the right pane:


Run the App on a Kestrel Web Server

With the files now on Ubuntu, we need to run them on a web server. Kestrel is a lightweight cross-platform web server for ASP.NET core. You can read more about it on the official website. We will use Kestrel as our web server as it is included by default on all ASP.NET core project templates, so we already have access to it in our app.

In a terminal, navigate to the deployment path and run the app in Kestrel:

cd /var/www/app
sudo dotnet DeployingToLinuxWithNginx.dll
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /var/www/app

This launches the app and, by default, serves it on port 5000. We shall leave this terminal open because Kestrel runs the app in the foreground.

At this point, we are halfway there. We’ve created the app, installed the ASP.NET Core runtime on the server, deployed it, and ran it in Kestrel.

At this point, it’s important to note that while Kestrel excels at serving dynamic content, it lacks other web-serving capabilities, such as static content, caching requests, and compressing requests. A reverse proxy server (like Nginx) offloads that work. We shall use a single instance of Nginx installed on the same server for this purpose.

Install and Configure Nginx on the Linux Server

Let’s open a new terminal on the server and install Nginx using the apt package manager:

sudo apt-get install nginx

This command installs Nginx and all the dependent packages and then starts up the Nginx service after the installation. We can check the status using the command service nginx status:

# service nginx status
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running)

This shows that the service is running. We can also reach the Nginx landing page by accessing http://<server-ip>/ in a browser.

Next, we’ll configure Nginx as a reverse proxy to forward requests to our app. Let’s edit the configuration file at /etc/nginx/sites-available/default using a built-in text editor on Ubuntu called nano:

nano /etc/nginx/sites-available/default

server {
    listen        80;
    location / {
        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;

This configuration instructs Nginx to listen for traffic on port 80 and forward it to Kestrel, which is listening on port 5000. After editing the file, save the changes. Then run sudo nginx -t to validate the syntax:

sudo nginx -t

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

Then we’ll run sudo nginx -s reload for Nginx to reload the config file and pick up the changes:

sudo nginx -s reload

Now when we access http://<server-ip>/ in a browser, we can again see the default page of our app.

This is all good, but a terminal is still open to run Kestrel. This is inconvenient because the moment we close that terminal, the app stops as well. Let’s configure Kestrel to run as a background service to address this.

Configure the ASP.NET Core Web App To Run as a Service

Ubuntu contains a service manager called systemd , which acts as an initialization system and controls what programs and services run when the system boots up. We shall use systemd to manage our Kestrel server.

To create the service, we first need to create the configurations in a systemd unit file. The unit file contains information regarding the unit, which is a service in this case. For services, it should have the .service extension and contain some information about the service. These files are required to be in the /etc/systemd/system directory.

Let’s use “nano” to create the unit file and name it kestrel-app.service:

nano /etc/systemd/system/kestrel-app.service

We can add some directives about the service in the file:

Description=ASP.NET Core Web App running on Ubuntu

ExecStart=/usr/bin/dotnet /var/www/app/DeployingToLinuxWithNginx.dll
# Restart service after 10 seconds if the dotnet service crashes:
# This user should exist on the server and have ownership of the deployment directory


After adding the content, save it. Then let’s enable and start the service:

sudo systemctl enable kestrel-app.service
sudo systemctl start kestrel-app.service

When we check the status of the service, the output shows that it is running:

$ sudo systemctl status kestrel-app.service
● kestrel-app.service - ASP.NET Core Web App running on Ubuntu
     Loaded: loaded (/etc/systemd/system/kestrel-app.service; enabled; preset: enabled)
     Active: active (running) since Wed 2023-04-19 17:13:21 UTC; 3s ago
   Main PID: 1477 (dotnet)
      Tasks: 22 (limit: 9394)
     Memory: 22.5M
        CPU: 232ms
     CGroup: /system.slice/kestrel-app.service
             └─1477 /usr/bin/dotnet /var/www/app/DeployingToLinuxWithNginx.dll

And now, after closing all the terminals, we can still access the web app in a browser using http://<server-ip>/.

We have focused on deploying a web app, but sometimes we must work with long-running, CPU-intensive background services.

Please visit Running .NET Core Applications as a Linux Service article to learn more about deploying as a Linux worker service.


In this article, we’ve gone through the steps to deploy an ASP.NET Core web app to Ubuntu with Nginx as a reverse proxy. Deploying on Linux provides a cost-effective, high-performance, and secure platform for running our applications.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!