Running ASP.NET Core on Linux Part 1

Published on Tuesday, April 7, 2020

In the last couple of weeks, I have been busy setting up a CI/CD pipeline for one of my side projects and getting it to run on Linux. I'll try to write about what I have learned in the process.

This will be a series of blog posts because I don't want it to become too long. This is the first part. We will create a new ASP.NET Core application and manually run it on Linux and configure Nginx and systemd.


Before anything lets create a new ASP.NET Core Razor Pages web application by running:

dotnet new webapp

Good, now add it to a git repository and upload it to GitHub, it will be useful for us in the next part of the series.

Setting up VSCode

VSCode is great for accessing remote Linux servers. It allows you to open multiple terminals and edit files and allows you to download and upload them too. Follow these steps to set it up:

  1. Install the Remote - SSH extension to be able to login into the server and be able to run commands and edit files.

  2. In Remote Explorer tab add a new SSH Target for your server and inter username and IP address and follow the instructions. After you added the server to Remote Explorer connect to it and in Explorer tab open the / directory.

  3. Download and install the ASP.NET Core runtime on the server. You can find instructions here.

Publishing the website

Before doing anything we have to publish the website first. We will automate this process later but for now, we will do it manually.

dotnet publish -c Release

Note: This will publish the web app in Framework dependent (Meaning that ASP.NET Core runtime has to be installed on the server) and cross-platform (Meaning that it can work on any operating system) mode. You might need to publish it for a specific platform if you're using native libraries or one of your Nuget packages uses them by using the --runtime switch.

Compress the contents of bin\Release\netcoreapp3.1\publish\ folder (and rename it to publish.zip) and upload it to /home/www/sample folder by dragging it into Explorer tab of VSCode.

Run these commands to unzip and delete the zip file:

cd /home/www/sample
unzip publish.zip
rm publish.zip

Okay, now that all of the necessary files are on the server, you can now test if the app actually works:

dotnet AspNetOnLinux.dll --urls=http://localhost:9999

If everything is set up correctly and port 9999 is not being used by any other processes, kestrel must be up and running now.

  • If there were an exception like System.IO.IOException: Failed to bind to address http://127.0.0.1:9999: address already in use. Then change the port to something else.
  • If There was an error like Command 'dotnet' not found Then it means that ASP.NET Core runtime has not been installed properly. Install them and try again.

Now that we have confirmed that everything works press next we need to configure Nginx as a reverse proxy, i.e. it listens for an external port on an IP address or domain name and re-routes it to kestrel.

Configuring Nginx

Keep kestrel running and open a new terminal in VSCode and run:

code /etc/nginx/sites-available/default

This will open Nginx default configurations in VSCode.

server {
    listen        IP_ADDRESS;
    server_name   HOST_NAME;
    location / {
        proxy_pass         KESTREL_URL;
        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;
    }
}

Copy the above snippet to the configuration file and make these adjustments:

  1. Instead of IP_ADDRESS write the external ip address to which you want Nginx to listen. Example: 8080. If HOST_NAME is a domain name you might want to use port 80.
  2. Instead of HOST_NAME write the domain name or IP address you want Nginx to listen to. If you've rented a VM they usually give you a static public IP that you can use for this. or if you want to use a domain name, you need to configure its DNS settings so that it points to your server's public IP address.
  3. Instead of KESTREL_URL write the URL you have kestrel listening to. Example: http://localhost:9999

Save the file and run this command for Nginx to reload the configuration file:

# Validate configuration file
nginx -t
# Reload configuration file
nginx -s reload

Now in a browser go to http://HOST_NAME:IP_ADDRESS/ and it should show our web app running 😊

For more information on configuration Nginx go to Microsoft docs.

Setting up systemd

Everything seems to be working now. But if you close the ssh session our website will no longer be running and Nginx will return ERROR 502: Bad Gateway if you visited the website. To make sure the website will always be running and will be restarted if it crashes we can use systemd.

Go back to the terminal that's running kestrel and press CTRL+C to stop it. And then run these commands:

code /etc/systemd/system/sample.service

This will open up a blank file in VSCode. Write this configuration in the file:

[Unit]
Description=Sample ASP.NET Core application

[Service]
WorkingDirectory=/home/www/sample
ExecStart=/usr/bin/dotnet /home/www/sample/sample.dll --urls=http://localhost:9999
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-ec
User=root
# You can put your environment variables here:
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

Save it and then run these commands:

# Reload systemd configuration
systemctl daemon-reload

systemctl enable sample
systemctl start sample

You can verify that our application is up and running by running this command:

systemctl status sample

It should not show any errors if everything has gone according to our plan. Press CTRL+C to stop showing status.

Now our sample web application will be running reliably and will be restarted if it crashed. You can close VSCode and visit http://HOST_NAME:IP_ADDRESS/ and it should be serving requests.


In the next post, we will talk about setting up a CI/CD pipeline using GitHub actions.

The code is available on GitHub.

comments powered by Disqus