I recently bought a Raspberry Pi 4 with 4GB RAM. I've managed to run an ASP.NET Core application on this Raspberry Pi 4. 

05/13/2022 Update content for .NET 6.0 LTS for ARM64. Original post is for .NET Core 3.1 ARM32.

ARM32 or ARM64?


You can't run ARM64 version of .NET Core runtime or SDK on a Raspberry Pi 4 with the official Raspbian system today. Although Raspberry Pi 4's CPU supports 64 bit, its official operating system "Raspbian" isn't 64 bit. However, Ubuntu for Raspberry Pi 4 can install ARM64 .NET 6.0 SDK. It's up to you to choose ARM32 or ARM64. The setup steps are the same. 

Prepare


Required: An Internet connection for downloading .NET SDK and Runtime.

Optional: Visual Studio Code / Visual Studio on your desktop machine to create a sample project

Update your Raspbian:

sudo apt-get update
sudo apt-get upgrade

I also suggest using a monitor for your Raspberry Pi or a VNC connection, so that you can test your ASP.NET Core websites at the final step without messing up the port and network settings.

Download .NET SDK and Runtime


On your desktop machine, open https://dotnet.microsoft.com/download/dotnet-core/6.0

Find Linux distributions for ARM32/64 SDK and ASP.NET Core Runtime, click into it, you can then copy the Direct Link. These 2 links are what we use in Raspbian / Ubuntu to download the latest SDK and Runtime. For example:

Use wget on your Raspbian to download .NET SDK and ASP.NET Core runtime, for example, Linux ARM64:

wget https://download.visualstudio.microsoft.com/download/pr/7c62b503-4ede-4ff2-bc38-50f250a86d89/3b5e9db04cbe0169e852cb050a0dffce/dotnet-sdk-6.0.300-linux-arm64.tar.gz
wget https://download.visualstudio.microsoft.com/download/pr/8ba7087e-4513-41e5-8359-a4bcd2a3661f/e6828f0d8cf1ecc63074c9ff57685e27/aspnetcore-runtime-6.0.5-linux-arm64.tar.gz

Please always check for updated URLs for a newer version of SDK and Runtime.

Install .NET SDK and Runtime


Create a folder named for example "dotnet-arm64" and unzip them into it. 

mkdir dotnet-arm64
tar zxf dotnet-sdk-6.0.300-linux-arm64.tar.gz -C $HOME/dotnet-arm64
tar zxf aspnetcore-runtime-6.0.5-linux-arm64.tar.gz -C $HOME/dotnet-arm64

So far you can only run .NET commands inside the dotnet-arm64 folder. To make "dotnet" command can be run everywhere, you have to make something like environment variables, which will link dotnet-arm64 folder to system level.

export DOTNET_ROOT=$HOME/dotnet-arm64
export PATH=$PATH:$HOME/dotnet-arm64

Now, you can run dotnet --info to have a quick test:

Create .NET Applications


Now that we have the SDK, we don't have to use another machine to develop and publish the App. We can just do it directly on our Raspberry Pi. 

mkdir hello-dotnet
cd hello-dotnet
dotnet new console

After the project files are created and NuGet restore completes. You can run the project using "dotnet run" command.

You can even use nano as a basic code editor.

sudo nano Program.cs

Deploy and Run ASP.NET Core Applications


In a more real-world scenario. You just can't develop complex .NET applications without an IDE on Raspbain. We usually develop and test our App on desktop machines first and then publish it to Raspberry Pi to run it.

To run an ASP.NET Core application. You need to first publish it. For example, from Visual Studio 2022. What I have is an empty ASP.NET Core application with a little more output information in Startup.cs

await context.Response.WriteAsync($"Empower every person and every organization on the planet to achieve more{Environment.NewLine}" +
                                  $".NET Core {Environment.Version}{Environment.NewLine}" +
                                  $"Environment.OSVersion: {Environment.OSVersion}{Environment.NewLine}" +
                                  $"Environment.Is64BitOperatingSystem: {Environment.Is64BitOperatingSystem}{Environment.NewLine}" +
                                  $"Environment.Is64BitProcess: {Environment.Is64BitProcess}", Encoding.UTF8);

Publish it using Framework-Dependent (FDD) and Portable. Because we have the native runtime on Raspberry Pi. We DON'T have to build it into linux-arm RID! Portable will run just fine!

Copy the published directory to Raspberry Pi.

Now you can use "dotnet <Your DLL Name>.dll" to run your ASP.NET Core application.

"Auto Start" .NET Environment


Every time you restart your Raspberry Pi, you'll have to re-configure the DOTNET_ROOT and PATH environment variables or .NET CLI won't work. To make them auto start with the system, we can modify the ".profile".

sudo nano .profile

Add those lines at the end of this file

# set .NET SDK and Runtime path
export DOTNET_ROOT=$HOME/dotnet-arm64
export PATH=$PATH:$HOME/dotnet-arm64

Looks like this

Save the file and restart your Raspbian system. Now you can run "dotnet --info" directly without manually setting the environment variables again.

Access ASP.NET Core Website from LAN


So far your ASP.NET Core application can only be visited on Raspberry Pi itself using "localhost". It would be much more useful if we expose it to LAN. There are two ways to do that.

Host by Kestrel itself

Just give dotnet command a --urls parameter, it would listen on the domain and port you provided. I don't want to limit to any domain name, so I use * instead.

dotnet Empower.dll --urls "http://*:8080"

Now you will be able to access your Raspberry Pi via LAN.

But this hosting model has its disadvantages, for example, when you have an exception in your code, the dotnet process will end and you have to manually restart it. Let's check out a more real-world hosting model.

Host by Nginx with auto-restart capability

First, install and start Nginx:

sudo apt-get install nginx
sudo /etc/init.d/nginx start

Open Nginx config file:

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

Replace its content with:

server {
    listen        80 default_server;
    server_name   _;
    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;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

server_name is set to _; means I don't limit domain names, just accept any hostnames. proxy_pass is the default Kestrel HTTP endpoint, which runs our ASP.NET Core website.

Check and apply the config file:

sudo nginx -t
sudo nginx -s reload

Now, start your ASP.NET Core website, you will be able to access your Raspberry Pi using port 80.

dotnet Empower.dll

Now we have one last step to go, make dotnet process auto-restart in case your code accidentally blow up. To do that, we will create a systemd service.

sudo nano /etc/systemd/system/kestrel-empowerapp.service

With the following content:

[Unit]
Description=ASP.NET Core App - Empower

[Service]
WorkingDirectory=/home/pi/dotnet-playground/empower/portable-fdd
ExecStart=/home/pi/dotnet-arm64/dotnet /home/pi/dotnet-playground/empower/portable-fdd/Empower.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-empower
User=pi
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target

Note, we can only use absolute path in systemd configuation. 

Register and start the service:

sudo systemctl enable kestrel-empowerapp.service
sudo systemctl start kestrel-empowerapp.service
sudo systemctl status kestrel-empowerapp.service

Now, you have the auto start and failure restart capability for your ASP.NET Core 3.0 application running on Raspberry Pi 4. Try to reboot your Raspberry Pi, your website will auto start! It is a web server now!

For more configuration details, you can refer to Microsoft official document here.

Want Docker?


There are Microsoft official docker images supporting ARM32 on Raspberry Pi 4, if you managed to install docker on your Raspberry Pi 4, have fun :)

Reference: https://www.hanselman.com/blog/InstallingTheNETCore2xSDKOnARaspberryPiAndBlinkingAnLEDWithSystemDeviceGpio.aspx