Developing A .NET Core Site In Windows and Deploying It To A Budget Linux Host

Looking for more information about deploying a ASP.NET Core site on a budget? Be sure to check out the follow up to this post, ASP.NET Core Deployment Stack On A Budget.

I recently migrated the entirety of Pioneer Code to .NET Core and with it came new choices on how and where I was going to publish moving forward. Being I am cost conscious dev (aren't we all), switching to .NET Core allowed me to make a jump from IIS specific shared hosting to a fully controlled Linux VM. All while seeing a significant percentage of savings.

Linux Hosting

Linux VMs are cheap. Much cheaper than your typical IIS hosting. There are also plenty of more companies to choose from. Linode, Azure and Vultr all offer cheap options that can be easily scaled be needed. That being said, I picked DigitalOcean for no other reason than it offers Linux VMs for $5 a month. Can't get much cheaper than that!

Regardless who you choose and once you are past the setup phase specific to that company, the standup of your Linux VM should be for the most part identical to what I am going to outline below.

If you decide to go with DigitalOcean, use this link for a $10 credit when signing up.

DigitalOcean

Setting up a VM in DigitalOcean is straight forward.

  • Create an account.
  • Create a Droplet with the following configuration.

That is it. You should now receive an email with more details about your newly created VM.

Bash - SSH

Working with a Linux VM typically requires SSH access and there are plenty of options on how to accomplish this in Windows. Luckily I created a post on how to do exactly this! Check it out at How To Install Bash On Windows 10.

Once your BASH CLI is ready on your Windows dev machine, SSH into your server.

ssh root@{ip}

Server Setup

Setting up a fresh Ubuntu server requires some sanity setup before it is production ready.

  • Confirming root login.
  • Create a new user.
  • Securing login by using Public Key Authentication.
  • Setting up a firewall.

No need to reinvent the wheel as all these steps are wonderfully documented at the following DigitalOcean tutorial, Initial Server Setup with Ubuntu 16.04.

Installing .NET Core on your VM

Again, remember we are working with Ubuntu 16.04 and by way of these instructions are catered to that particular distribution. That being said, .Net Core can be installed on a healthy number of Linux distributions. Head over to Dot.Net for a detailed list.

Run the following commands from your new Bash CLI.

1: Add the dotnet feed to apt-get

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 apt-mo.trafficmanager.net --recv-keys 417A0893
sudo apt-get update

2: Install the .NET Core SDK

sudo apt-get install dotnet-dev-1.0.0-preview2-003131

At the time of this writing, 1.0.0-preview2-003131 was the latest .NET Core version. Be sure to head over to Dot.Net to check for the latest version.

3: Verify installation

dotnet

You should end up with something like this.

Verify .NET Core install

Installing Nginx

We will need a production ready web server to use as a proxy for the .NET Core Kestrel Server. Kestrel Server works well in development but being it is still early in its life, it does not have a full feature set that might be required for production. For that, we will turn to NGINX.

1: Install Nginx

sudo apt-get update
sudo apt-get install nginx
sudo service nginx start

2: Let Nginx through the firewall.

There are three levels of access we can give Nginx

  • Nginx Full
    • Opens both 80 and 443.
  • Nginx HTTP
    • Opens 80 only.
  • Nginx HTTPS
    • Opens 443 only.

As with most things in life, you should explicit about access and only open what you need.

sudo ufw allow 'Nginx HTTP'

3: Verify

Run the following command to verify your firewall was setup correctly.

sudo ufw status
Nginx Firewall Verify

Run the following command to insure Nginx was setup properly.

systemctl status nginx
Nginx

At this point should be able to hit Nginx from the public IP assigned to your server.

Nginx in a browser

Configure Reverse Proxy

We will need to edit a Ngnix configuration file in order to setup our reverse proxy. To do this from a command line we can use nano. If not already installed, run the following commands.

sudo apt-get update
sudo apt-get install nano

Once installed, open the config file in question by running the following.

sudo nano /etc/nginx/sites-available/default

In the following picture you will see what I used to get started. Any place that there is a # in the first column, it signifies something I commented out. Any lines that are highlighted, it signifies something I added. Anything that is off screen is commented out by default.

Nginx Reverse Proxy

Commit and save your changes by hitting ctrl+x, then y and finally enter.

Run the following command to insure your changes checkout as valid syntax for Nginx configuration.

sudo nginx -t

Run the following command to tell Nginx to reload based on our new configuration.

sudo nginx -s reload

The demonstrated configurations boils down the bear minimum needed to run the reverse proxy service. It is highly recommend that you take the time to fully understand Ngnix configuration and insure you server is secure.

Publish Your Site

There are few different methods for publishing a site, framework-dependent deployment (FDD) and self-contained deployments (SCD).

  • FDD - Your app takes a dependency on the server having the appropriate .NET Core release installed on said server.
  • SCD - Your app is deployed with the entire .NET Core framework as part of its release assets.

For more information, I recommend heading over to the official NET Core Application Deployment documentation.

What ever process deployment type your application uses, the following should generally hold true.

1: Build Assets On Your Windows Machine.

On you development machine (in our a case our Windows machine), open a command prompt at the root of your web application and run the following.

FDD

dotnet publish -c release

SCD

dotnet publish -c release -r ubuntu.16.04-x64

After completing, your assets will end up in one of the following folder depending on your release type.

  • FDD: ../bin/release/netcoreapp.1.0/publish
  • SCD: ../bin/release/netcoreapp.1.0/ubuntu.16.04-x64

2: (S)FTP Client

Depending on your hosting provider, you most likely will need an FTP client to move your files over. On Windows it is hard to argue against using FileZilla to get the job done.

Configuration of your FTP client will be hosting specific. In the case of DigitalOcean they recommend using SFTP and provide a tutorial for such.

3: Moving Assets

  1. Create a folder on your new server to house your application and give your new user permissions to it. {your-app} equals your app name and {user-name} equals the none root user you created.
    sudo mkdir /var/{your-app}
    sudo chown {user-name}: /var/{your-app}
  2. User your (S)FTP client to move assets. Here is what it look like in FileZilla. FileZilla Example

Start Your Site

Now that we have our server configured and our files moved, we can test everything out by starting the .NET Core application directly from our BASH command line. There is a lot of confusion about this on the internet, partly because the .NET Core documentation does not mention how to do so.

Navigate to your application folder and run the following command.

dotnet name.of.your.app.dll

Be sure to update the command above to reflect the name of your projects DLL.

At this point you should see .NET Core logging on the command line stating "Now listening on: http://localhost:5000". Recall that we told Ngnix to proxy all request coming in on port 80 to http://localhost:5000. Meaning, we now should be able to hit our public IP address and see our app running in all of its glory.

When you make a request in the browser to your server, keep an eye on your BASH command line. As request are ran, you will see additional .NET Core logging that will help you understand the state of your app at any given time.

Make Sure Your Site Continues To Run

Now that we confirmed our site actually runs, we need to figure out a way to keep it running. The problem with what we just did is the fact that the second we shutdown our command line, our site will no longer be running. To solve this we will use an application named Supervisor.

Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems. Supervisor

Install Supervisor by running the following command on your server.

sudo apt-get install Supervisor

Once installed, we need to config Supervisor so that it will insure our site restart in the advent it dies.

Navigate to to the following directory.

cd /etc/supervisor/conf.d/

Create a config file where {name} equals name of file.

sudo touch {name}.conf

Open the file for editing with nano.

sudo nano {name}.conf

Copy and paste the following after you have updated all places that say your-app with the actual name of your app.

[program:your-app]
command=/usr/bin/dotnet /apps/your-app/your-app.dll
directory=/apps/your-app/
autostart=true
autorestart=true
stderr_logfile=/apps/log/your-app.err.log
stdout_logfile=/apps/log/your-app.out.log
environment=ASPNETCORE_ENVIRONMENT=Production
user=www-data
stopsignal=INT
stopasgroup=true
killasgroup=true

Commit and save your changes by hitting ctrl+x, then y and finally enter.

Reset Supervisor so that it grabs control of your applications process.

sudo service supervisor stop
sudo service supervisor start

Where Do We Go From Here

Me

Now that I am comfortably running Pioneer Code on a Linux environment, I think the next logical step is for me to containerize my application and start to utilize Docker for deployments.

You

  • Ensure you have a good understanding of NGINX and get your site secure ASAP.
  • (S)FTP is annoying slow.
    • Setup a CI (continuous integration) build to speed the process up.

As always, feel free to let me know your thoughts in the comments section below.