Microsoft's open source ASP.NET Core framework is cross-platform, but many official tutorials assume you'll be serving your site with Azure or IIS. These notes show you how I set up an ASP.NET Core site on Linux.
- Write an ASP.NET Core application
- Get a virtual private server
- Install Linux tools for Windows
- Prepare names and passwords
- Install an operating system on your server
- Log in as root
- If ssh fails...
- Set up Ubuntu
- Set up the hosts file, install Nginx and Postgresql
- Install the ASP.NET Core runtime
- Set up UFW (Uncomplicated Firewall) and install Fail2Ban
- Create a new user
- Set up ssh keys
- Set up Postgresql database and user
- Set up app environment
- Set up Nginx
- Publish your ASP.NET Core project
- Enable the sites in Nginx
- Create script to make the site launch on boot
Write an ASP.NET Core application
This post focuses on deploying an ASP.NET Core site to Linux, and assumes you have already written one that works on your machine.
Get a virtual private server
A virtual private server is a sandboxed portion of a physical server that behaves like an independent machine. It gives you complete control over how your environment is set up, even to the point of installing the operating system from scratch. I chose Linode because of their reputation. My plan is $10/month, but they go as low as $5.
Install Linux tools for Windows
Since I develop on Windows 10, I installed software that makes Linux command line tools available from the Powershell console. The first step is to go to Windows Features and enable Windows Subsystem for Linux, and the second is to install the Linux distribution of your choice from the Windows Store.
Prepare names and passwords
For setup, you will need:
- A password for your internet hosting account (i.e. Linode)
- A password for your Linux root user
- A username and password for your Linux non-root user
- A password for the ssh connection to your server
- A name for the server itself
- The IPv4 and IPv6 address of your internet hosting account
If you are setting up a Postgresql database, you will also need:
- A name for the Postgres database your application will use
- A username and password for the Postgres database user your application will use
Try to find a good balance between the length of your passwords and how easy they are to remember, particularly for the ssh and non-root Linux passwords, which you'll be using a lot.
Install an operating system on your server
After creating your Linode account, log in and click on the first (and only) Linode. On the Dashboards tab, click Deploy an Image. Select Ubuntu 18.04 in the Image dropdown, and enter your root password in the password field, then click Deploy. Wait for the process to complete, then click boot.
Log in as root
Wait for the server to boot, then in Powershell, type:
bash
This will get you to a Linux compatible prompt where you can use programs from the Linux toolchain. At the new prompt, type:
ssh root@8.8.8.8
If ssh fails...
If you've already worked through some of these steps, the ssh command may warn you that the remote host identification has changed. In that case, enter the command the error message suggests, which should look something like this:
ssh-keygen -f "/home/yourusername/.ssh/known_hosts" -R 8.8.8.8
It will warn you that the authenticity of the remote host cannot be established. Type yes anyway. If you had to do this step, remember to try the connection again:
ssh root@8.8.8.8
Set up Ubuntu
Once logged into the server as the root user, type these commands to update Ubuntu, get security updates automatically, and install a utility to restart your software when the server reboots:
apt-get -y update && apt-get -y upgrade
apt-get -y install unattended-upgrades supervisor
During the installation, you may be asked questions about the bootloader program, GRUB. If so, use the arrow keys and spacebar to select and enter that you would like to keep the version of GRUB already installed. On the next screen, choose to install it on any disk that is not a boot partition. When you return to a command prompt, type these commands to set the server name and timezone:
hostnamectl set-hostname myhostname
timedatectl set-timezone UTC
Set up the hosts file, install Nginx and Postgresql
On the command line, type:
nano /etc/hosts
Then in the editor that opens, add the following lines to the bottom of the file to associate your server's IPv4 and IPv6 addresses with your domain name and server name:
8.8.8.8 myhostname.mydomainname.com myhostname
2001:4860:4860::8888 myhostname.mydomainname.com myhostname
Exit and save using the commands at the bottom of the editor, and press Enter to keep the current filename. Then type the following on the command line to install Postgresql and Nginx:
apt-get -y install nginx postgresql postgresql-contrib
Install the ASP.NET Core runtime
Enter the following commands to add Microsoft's ASP.NET Core repository and install the runtime:
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
wget -q https://packages.microsoft.com/config/ubuntu/18.04/prod.list
mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
chown root:root /etc/apt/sources.list.d/microsoft-prod.list
apt-get install apt-transport-https
apt-get update
apt-get install aspnetcore-runtime-2.1
Set up the firewall (UFW) and install Fail2Ban
UFW prevents your server from being accessed except in the ways that you need (in this case: ssh connections, port 80, and port 443), and Fail2Ban helps stop brute force password attacks and denial of service attacks. Configure UFW with the following commands:
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
If a message asks you if you'd like to proceed despite the fact that it could disrupt existing ssh connections, type yes. Now install Fail2Ban and reboot:
apt-get -y install fail2ban
shutdown -r now
Create a new user
Log into the server again from your Powershell bash prompt:
ssh root@8.8.8.8
Add a new user. You will be asked to provide a password.
adduser myserveruser
usermod -aG sudo myserveruser
Since you are currently logged in as root, log out so you can log back in as the new user.
exit
Set up ssh keys
Having logged out of the server, type the following command:
ssh-copy-id myserveruser@8.8.8.8
Log back into the remote machine, this time as the new user:
ssh myserveruser@8.8.8.8
You will be asked to provide first the password you chose for this new user, and then a passphrase of your choosing that you will use whenever you connect to the remote server with this ssh connection. Once you are logged in, edit this ssh configuration with the following command:
sudo nano /etc/ssh/sshd_config
Ensure that the file contains the following three lines, changing the values of existing lines if necessary:
PermitRootLogin no
PasswordAuthentication no
AddressFamily inet
Set up Postgresql database and user
Run psql as the user "postgres", which is the built-in administrative user for Postgresql:
sudo -u postgres psql
This brings you to a command prompt just for the Postgresql database. Execute these three lines to create a database and a non-administrative user with permission to use it:
CREATE DATABASE mydatabase;
CREATE USER mydatabaseuser WITH PASSWORD 'myaw3som3passw0rd';
GRANT ALL PRIVILEGES ON DATABASE mydatabase TO mydatabaseuser;
Exit psql:
\q
Set up app environment
Change to the home directory of myserveruser:
cd ~/
Create a directory to hold your application:
mkdir myapplication
Depending on how your application works, you can create environment variables to be read when the application launches, or create an application.json file that lives outside the application directory, which is my own preference.
Learn more about ASP.NET Core configuration from Microsoft's documentation.Set up Nginx
Go to the Nginx sites-enabled directory:
cd /etc/nginx/sites-available
Type the following command to create and edit a new Nginx configuration file for your site:
sudo nano ./mydomainname.com
In this file, type the following code block:
server {
listen 443 default_server ssl;
server_name mydomainname.com www.mydomainname.com;
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;
}
}
Publish your ASP.NET Core project
First, log out of the server.
exit
In the root of your ASP.NET project, publish for release:
dotnet publish -c Release
From your Powershell bash prompt, move to the parent directory of the publish folder you just created (in my case: projectRoot/bin/Release/netcoreapp2.1) and type the following command to copy the publish folder to a directory within your user folder on the server:
scp -r ./publish/ sfbloguser@8.8.8.8:~/myapplication/
Enable the sites in Nginx
Go to the Nginx sites-enabled directory:
cd /etc/nginx/sites-enabled
Make a symbolic link to the file you created in sites-available (if you want to disable this site later, just delete the site in sites-enabled, and it will remain in sites-available):
sudo ln -s ../sites-available/mydomainname.com
Reload Nginx so it will read the new configuration:
sudo service nginx reload
Create script to make the site launch on boot
Go to the application directory:
cd ~/myapplication
Create and edit a file named init.
nano init
In the editor, type the following lines:
#!/bin/bash
cd /home/myserveruser/myapplication/publish
dotnet myapplication.dll
Save the file using the commands on the bottom of the editor. Then on the command line, make the file executable:
sudo chmod -x ./init
The commands in this file will run on startup, but not without being called by supervisor, which we installed earlier. Go to the supervisor configuration directory:
cd /etc/supervisor/conf.d
Create and edit a new file called myapplication.conf:
sudo nano sfblog.conf
In the editor, type the following lines:
[program:myapplication]
command=/home/myserveruser/myapplication/init
autostart=true
autorestart=true
stderr_logfile=/var/log/sfblog.err.log
stdout_logfile=/var/log/sfblog.out.log
On the Linode dashboard in your browser, click the Reboot button. Wait a few minutes, and navigate to your site in a browser. If all has gone well, your site is live and your server has basic security.