Introduction
I wanted to test how fast a website with a CMS could run on .NET Core on a Raspberry Pi hosted on a cheap business broadband with static IP. This would allow me to host my own web projects basically for free, rather than using commercial hosting.
What I Used
I'm from the UK so the following are UK providers and costs. I used a Raspberry Pi 3 (https://uk.rs-online.com/web/p/processor-microcontroller-development-kits/8968660/?src=raspberrypi) currently £35. Into this, I installed a Samsung EVO Plus Micro SDXC 64GB up to 100MB / s, Memory Card (including SD Adapter) MB-MC64GA/AMZ (£17.95). I set this up on Unlimited Business Broadband from PlusNet which provides a static IP.
Software-wise, I was using .NET Core 2.0 on Windows 10 with Visual Studio 2017 Community. I used a CMS which I was involved in building, Lynicon CMS for .Net Core. Another key piece of software was Windows Subsystem for Linux. On the Pi, I used Raspbian Pi Desktop as the OS, and I installed Apache webserver.
Setting Up the Pi
The first job was to get the Pi up and running with Raspbian Linux on a micro SD card. Apparently, not every SD card out there will work with the Raspberry Pi, so I'd recommend checking https://elinux.org/RPi_SD_cards to find one someone else has checked, if you don't use the one I did. I needed one with an SD adapter so I could use it on my Surface Laptop.
This article tells you how to format the card. I wanted a decent sized one and they need to be reformatted to work properly.
I ran the SD formatter mentioned there, then guiformat.exe.
Then I downloaded Raspbian as described in:
and used Etcher (details in the article) to flash it onto the sd card.
I then followed the instructions here (steps 1 to 6 of section 1 are covered above). This tells you how to set up the Pi and install .NET Core 2 on it.
To be able to create the 'ssh' file it mentions in Section 1 step 7, eject the newly flashed sd card and reinsert.
Work On Your Pi from Your Laptop
Before trying to connect to the Pi from your PC across your home network, I'd highly recommend setting up Windows Subsystem for Linux, as described here:
I was using the Ubuntu distro because I'd already installed it, but I'd probably think Debian was better to use for Raspberry Pi work as Raspbian is very close to Debian, and things will be more similar on both sides.
You don't have this choice if you're not on Windows 10, in this case, I'd recommend using Putty:
and using pscp to transfer files - installation instructions:
To connect to your pi, open up your Debian terminal and type:
ssh pi@192.168.1.1
changing the IP address to whatever IP your Pi is on. You'll then be prompted for the password for the 'pi
' user which you set up earlier. After this, you'll get a remote command prompt on the Pi.
If you don't know Linux, it's pretty similar to the cmd shell in Windows, there's plenty of online information.
Getting your Pi on a Fixed Internal IP
If you reboot your Pi the DHCP service on your internal network will dynamically assign it an IP. You don't want this as you want to be able to connect to it on the same internal IP reliably and also it may break your port forwarding if it changes. You can find out how to configure your Pi to have a fixed internal ip here:
https://raspberrypi.stackexchange.com/questions/37920/how-do-i-set-up-networking-wifi-static-ip-address/74428#74428
Installing Apache
I then installed Apache. Theoretically, you could use the Kestrel server which is built in to ASP.NET Core bare, but if you want to run several sites on your Pi and you want some of them to be just static, it's a much better idea to set up a Reverse Proxy with Apache.
Before installing anything, run:
sudo apt-get update
If you don't know Linux, what this does is update all the references on the installer utility apt-get
. When you put sudo
in front of a command, Linux runs it as an administrator.
A guide to installing Apache can be found at the link below:
You also need to set up proxy
and proxy_http
modules. These are required to reverse proxy to Kestrel.
cd /etc/apache2/mods-enabled
sudo ln ../mods-available/proxy.load
sudo ln ../mods-available/proxy_http.load
sudo mv proxy_http.load z_proxy_http.load
Apache will load the modules it finds in the mods-available directory in alphabetical order, and you need proxy module to load before proxy_http
, hence the last command.
The ASP.NET Core Site
While you could install any ASP.NET Core site in the way described here, I wanted to take a step further and run one with a CMS, partly because I'd been involved in building one for .NET Core. Lynicon CMS is available as open source Nuget packages, and after installing the basic one here, you can continue to install another, the Lynicon.Extra nuget package which allows you to run without a database as described here.
With this module installed, the CMS keeps content data in memory and backs it up to a file, which is very fast for smaller sites and means you don't need to try and run a database on your Pi!
If you want to use a demo site already set up with the CMS, you can find one on the Github repository for the CMS here.
Setting Up the Site on the Pi
An additional bit of code needed to support the reverse proxy with Apache scenario is described under Configure a Proxy Server in this article which we will go back to later to finish setting up the reverse proxy.
Now we need to publish our site on the command line in Windows. Enter cmd
into the Windows search box and right click to Run as Administrator.
dotnet publish -r linux-arm -o <path to publish folder>
This compiles down to the native binaries which will run on the Pi (they won't run on Windows by the way).
Now open your Windows Subsystem for Linux (WSL) distro terminal and do this:
cd /mnt/c/<path to parent of publish folder>
tar -zcvf published.tar.gz <publish folder name>
scp published.tar.gz pi@192.168.1.1:/var/www/<site folder>
remembering to change the IP address to your pi's address. On the first line, you get to the C drive root on Windows from Linux via the path /mnt/c. Then add the rest of the path to get you to the parent folder of the publish folder (remembering to use forward slash separators for Linux).
The scp
command copies the zipped site file onto the Pi, it will ask you for the Pi password.
You may well find you don't have permissions to copy there in which case:
ssh pi@<pi's ip address>
<enter the password for pi>
(now on pi)
cd /var/www
sudo chmod 777 <site folder>
exit
Then do the following to unzip and set up the site files:
ssh pi@<pi's ip address>
<enter the password for pi>
(now on pi)
cd /var/www/<site folder>
tar -xzvf published.tar.gz
mv -v <publish folder name>/* .
rm published.tar.gz
rmdir <publish folder name>
./<project name>
The last command should run the site under Kestrel, which should succeed. The command prompt won't come back as the window is now running Kestrel. Hit CTRL-C to stop Kestrel.
Set Up the Reverse Proxy
Now follow the instructions to reverse proxy Kestrel from Apache here. You need the instructions under Configure Apache for reverse proxy and Monitoring the App.
Since we're using Raspbian/Debian, the following differences apply:
The apache config set up directory:
/etc/apache2/sites-available
To test config:
apachectl configtest
To restart apache:
sudo /etc/init.d/apache2 restart
Create the service definition file as specified. Note the WorkingDirectory
will be:
/var/www/<site folder>
and the second argument for ExecStart
will be:
/var/www/<site folder>/<site executable file>
The site executable file will have the name of the site, and have no extension.
Once the file is created, create the apache
user:
sudo adduser apache
At this point, it should be possible to see the site locally on your Windows machine by setting up an entry in the Hosts file (C:\Windows\System32\drivers\etc\hosts) which is the IP of the Pi followed by a tab followed by the domain you set up in the steps above in the .conf file, on the line labelled ServerName
.
This overrides DNS locally so that this domain will connect to the IP of the Pi. If you put the domain into your browser now, it should show you the site. If it doesn't work, Apache logs errors to /var/log/apache2/<entry under ErrorLog in the .conf file>. In your site, in the root directory, lynicon.log should be there if you are using Lynicon CMS, or whatever log file location you set up in your code.
Gotchas
The big thing to be aware of if you haven't done much Linux web work before is that all URLs are case sensitive in Linux. This applies to both statically served files like images and MVC routes.
Another issue is that to make file paths work cross platform, you cannot use literal backslash characters, you have to use Path.Combine
or Path.DirectorySeparatorChar
to compose file system paths that work on both Windows and Linux.
A final one is that you may see a 404 error when there is a code error in your site in this setup. Check permissions are not causing a problem.
Let the World See Your Site
Now all that remains is to set up a port forward on your router from port 80 (http) and port 443 (https) on your pi. Exactly how to do this depends on your router, but essentially the process is:
- Open a cmd window on your Windows box
- Run '
ipconfig /all
' - Find the section in the output headed Wireless LAN Adaptor WiFi or something similar (assuming you're not on an ethernet connection to your router)
- Find the IP labelled Default Gateway in that section
- Enter that IP into the URL bar on your browser
- You should see an admin page for your router
- Log in with whatever credentials you have
- Find Port Forwarding
- Add a port forward from the IP address (or MAC address) of the Pi
- You can find this by logging onto the Pi using the SSH commmand we used before and typing:
ifconfig
The IP address and MAC address are listed under 'eth0
'.
Updating Your Site
When you want to update your site, follow the instructions above to publish your site again, zip it and transfer it to the pi. You will have to do:
systemctl stop <name of service>.service
where the service is the one you created to maintain Kestrel running in Set up the Reverse Proxy above. This stops Kestrel, allowing you to test the new site without throwing an error owing to the URL it is trying to start up on being already occupied. You can then restart the service, starting Kestrel running with the new version of the site, with:
systemctl start <name of service>.service
Here's the Result
Here's the main site for the Lynicon CMS which is now on .NET Core on a Raspberry Pi: http://www.lynicon.com.