A little Linux VM on Azure is like $13 a month. You can get little Linux machines all over for between $10-15 a month. On Linode they are about $10 a month so I figured it would be interesting to setup an ASP.NET Core website running on .NET Core. As you may know, .NET Core is free, open source, cross platform and runs basically everywhere.

Step 0 - Get a cheap host

I went to Linode (or anywhere) and got the cheapest Linux machine they offered. In this case it's an Ubuntu 14.04 LTS Profile, 64-bit, 4.6.5 Kernel.

Since I'm on Windows but I want to SSH into this Linux machine I'll need a SSH client. There's a bunch of options.

Step 0.5 - Setup a user that isn't root

It's always a good idea to avoid being root. After logging into the system as root, I made a new user and give them sudo (super user do):

adduser scott

usermod -aG sudo scott

Then I'll logout and go back in as scott.

Step 1 - Get .NET Core on your Linux Machine

Head over to http://dot.net to get .NET Core and follow the instructions. There's at least 8 Linuxes supported in 6 flavors so you should have no trouble. I followed the Ubuntu instructions.

To make sure it works after you've set it up, make a quick console app like this and run it.

If it runs, then you've got .NET Core installed and you can move on to making a web app and exposing it to the internet.

Step 2 - Make an ASP.NET Core website

You can make an ASP.NET Core website that is very basic and very empty and that's OK. You can also get Yeoman and use the ASP.NET yeoman-based generators to get more choices. There is also the great ASP.NET MVC Boilerplate project for Visual Studio.

Or you can just start with:

dotnet new -t web

Today, this default site uses npm, gulp, and bower to manage JavaScript and CSS dependencies. In the future there will be options that don't require as much extra stuff but for now, in order to dotnet restore this site I'll need npm and what not so I'll do this to get node, npm, etc.

sudo apt-get install npm

sudo npm install gulp

sudo npm install bower

Now I can dotnet restore easily and run my web app to test. It will startup on localhost:5000 usually.

$ dotnet restore

$ dotnet run

scott@ubuntu:~/dotnettest$ dotnet run

Project dotnettest (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.

info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0]

User profile is available. Using '/home/scott/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.

Hosting environment: Production

Content root path: /home/scott/dotnettest

Now listening on: http://localhost:5000

Of course, having something startup on localhost:5000 doesn't help me as I'm over here at home so I can't test a local website like this. I want to expose this site (via a port) to the outside world. I want something like http://mysupermachine -> inside my machine -> localhost:5000.

Step 3 - Expose your web app to the outside.

I could tell Kestrel - that's the .NET Web Server - to expose itself to Port 80, although you usually want to have another process between you and the outside world.

You can do this a few ways. You can open open Program.cs with a editor like "pico" and add a .UseUrls() call to the WebHostBuilder like this.

Here the * binds to all the network adapters and it listens on Port 80. Putting http://0.0.0.0:80 also works.

You might have permission issues doing this and need to elevate the dotnet process and webserver which is also a problem so let's just keep it at a high internal port and reverse proxy the traffic with something like Nginx or Apache. We'll pull out the hard-coded port from the code and change the Program.cs to use a .json config file.

The hosting.json file is just this:

We can also use "AddCommandLine(args) instead of "AddJsonFile()" and pass in --server.urls=http://*:5123 on the command line. It's up to you. You can also use the ASPNETCORE_URLS environment variable.

NOTE: I'm doing this work a folder under my home folder ~ or now. I'll later compile and "publish" this website to something like /var/dotnettest when I want it seen.

Step 4 - Setup a Reverse Proxy like Nginx

I'm following the detailed instructions at the ASP.NET Core Docs site called "Publish to a Linux Production Environment." (All the docs are on GitHub as well)

I'm going to bring in Nginx and start it.

sudo apt-get install nginx

sudo service nginx start

I'm going to change the default Nginx site to point to my (future) running ASP.NET Core web app. I'll open and change /etc/nginx/sites-available/default and make it look like this. Note the port number. Nginx is a LOT more complex than this and has a lot of nuance, so when you are ready to go into Super Official Production, be sure to explore what the perfect Nginx Config File looks like and change it to your needs.

Then we'll check it and reload the config.

sudo nginx -t

sudo nginx -s reload

Step 5 - Keep your website running

The website isn't up and running on localhost:5123 yet (unless you've run it yourself and kept it running!) so we'll need an app or a monitor to run it and keep it running. There's an app called Supervisor that is good at that so I'll add it.

sudo apt-get install supervisor

Here is where you/we/I/errbody needs to get the paths and names right, so be aware. I'm over in ~/testapp or something. I need to publish my site into a final location so I'm going to run dotnet publish, then copy the reuslts into /var/dotnettest where it will live.

dotnet publish

publish: Published to /home/scott/dotnettest/bin/Debug/netcoreapp1.0/publish

sudo cp -a /home/scott/dotnettest/bin/Debug/netcoreapp1.0/publish /var/dotnettest

Now I'm going to make a file (again, I use pico because I'm not as awesome as emacs or vim) called /src/supervisor/conf.d/dotnettest.conf to start my app and keep it running:

[program:dotnettest]

command=/usr/bin/dotnet /var/dotnettest/dotnettest.dll --server.urls:http://*:5123

directory=/var/dotnettest/

autostart=true

autorestart=true

stderr_logfile=/var/log/dotnettest.err.log

stdout_logfile=/var/log/dotnettest.out.log

environment=ASPNETCORE_ENVIRONMENT=Production

user=www-data

stopsignal=INT

Now we start and stop Supervisor and watch/tail its logs to see our app startup!

sudo service supervisor stop

sudo service supervisor start

sudo tail -f /var/log/supervisor/supervisord.log

#and the application logs if you like

sudo tail -f /var/log/dotnettest.out.log

If all worked out (if it didn't, it'll be a name or a path so keep trying!) you'll see the supervisor log with dotnet starting up, running your app.