Publishing an ASP.NET Core website to a Linux host
I recently move my web site to a Linux host, remember, .NET Core is multi-platform đ This choice is mainly motivated by the price. Indeed, you can find many cheap VPS providers (Virtual Private Server) on the internet. For instance, you can choose OVH. They have great VPS for about 3⏠per month (I'm not paid by them, but I've been using their services for a few years without any issue).
Hosting a site on Linux is not as easy as hosting on Windows, at least for me. I think this is mainly because I don't use Linux daily đ
So, this is the steps I followed to deploy my websites on Ubuntu 16.04
and use Let's Encrypt for SSL.
I use vim
to edit files. If you are not familiar with it, you may prefer nano
. Just replace vim
with nano
in the commands lines.
#Step 1 - Connect to the VPS using ssh
Once you get your VPS, you should have an IP address and the password of the root user. This allows connecting to the machine using SSH
. If you are running Windows 10, you can enable the Windows subsystem for Linux
(WSL) feature. Then, you can use the native SSH
command.
bash
ssh root@ip
#Step 2 - Change the root password
For obvious security reasons, you must change the password of the root user using the passwd
command:
passwd
Then, enter the new password.
#Step 3 - create a non-root user
Using the root
user is not a good practice. Instead, you should create a new user:
adduser myuser # you'll be prompt for the password
usermod -aG sudo myuser # Add the user to the sudo group to be able to use admin commands
exit # terminate the session
Then, reconnect to the server using the new user:
ssh myuser@ip
#Step 4 - Update the server
Before doing anything, you should install updates:
sudo apt-get update # Fetches the list of available updates
sudo apt-get upgrade # Strictly upgrades the current packages
sudo apt-get dist-upgrade # Installs updates (new ones)
sudo reboot
If you prefer updates to be installed automatically, you can use unattended-upgrades
:
sudo apt-get install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
#Step 5 - Configure the firewall
By default, all ports are opened. For a web server, you only need to open the ports 80 and 443. Don't forget to leave the port 22 opened, or you won't be able to use SSH anymore.
sudo ufw default deny # deny all incoming connections
sudo ufw default allow outgoing # allow all outgoing connections
sudo ufw allow ssh # allow the incoming ssh port
sudo ufw allow 80/tcp # allow the incoming 80 port
sudo ufw allow 443/tcp # allow the incoming 443 port
sudo ufw enable # enable the firewall
sudo ufw status verbose # check everything is ok
#Step 6 - Copy your site to the server
If you don't have an ASP.NET Core app yet, you can easily create one using only one command:
dotnet new mvc
You should use the Microsoft.AspNetCore.HttpOverrides
package and app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All })
to get the actual url instead of localhost:5000
in the application as the web site behind a reverse proxy.
Then, copy the publish output to /var/www/mysite
using filezilla
, scp
or rsync
:
# compile and generate the website files
dotnet restore
dotnet publish -c release
# Copy the files to the server (replace ip by the actual ip address)
scp -r ./bin/release/netcoreapp1.1/publish/* myuser@ip:/var/www/mysite
#Step 7 - Install .NET Core
To run the website, you must install the .NET Core shared runtime (unless you create a self-contained application).
# If not Ubuntu 16.04, read the doc https://www.microsoft.com/net/core#linuxubuntu
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893
sudo apt-get update
# Replace 1.1.1 by the version your application need
sudo apt-get install dotnet-sharedframework-microsoft.netcore.app-1.1.1
info: At this step, you should be able to start the application on the server using
/usr/bin/dotnet /var/www/mysite/mysite.dll
. Once you have validated it works by checking the console output, stop the application usingCTRL+C
.
#Step 8 - Start the web site
You must configure systemd
to start dotnet
as a service.
sudo vim /etc/systemd/system/kestrel-mysite.service
[Unit]
Description=MySite
[Service]
WorkingDirectory=/var/www/mysite
ExecStart=/usr/bin/dotnet /var/www/mysite/mysite.dll
Restart=always
RestartSec=10 # Restart service after 10 seconds if dotnet service crashes
SyslogIdentifier=dotnet-mysite
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
sudo systemctl enable kestrel-mysite.service # register the service
sudo systemctl start kestrel-mysite.service # start the service
sudo systemctl status kestrel-mysite.service # check the service is running
Now you can access the website on the port 5000. You can test it using the wget
command:
wget http://localhost:5000
#Step 9 - nginx
Kestrel
is a good web server. However, Microsoft advice to not expose it directly to the internet. Instead, you should place it behind a reverse proxy such as nginx
.
sudo apt-get install nginx
sudo vim /etc/nginx/sites-available/default
Replace everything with:
server {
listen 80;
location / {
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;
}
}
Test the configuration:
sudo nginx -t
And reload the configuration:
sudo nginx -s reload
Now, the website is visible on the internet. You can open your favorite web browser, and navigate to http://ip
.
#Step 10 - Viewing logs
For debugging purpose, you can view the logs of the dotnet
process using the following command:
sudo journalctl -fu kestrel-mysite.service
#Conclusion
Your website is now online on the 80 port. Don't hesitate to leave a comment if I forgot something. Again, I'm not a Linux expert đ
The next post is about securing the website using a free SSL certificate provided by Let's Encrypt.
Do you have a question or a suggestion about this post? Contact me!