Coding with Titans

so breaking things happens constantly, but never on purpose

Install .NET Core on Raspberry Pi

.NET Core 3.1 for ARM32

Installing .NET Core on Raspberry Pi 4 is a very straightforward process these days. There are plenty descriptions in the wild, so please welcome my own guide on this subject too.

  1. Login into Raspberry Pi and upgrade all current modules to latest.

    sudo apt update
    sudo apt upgrade
    
  2. Install some dependencies, that .NET Core relies on (as described here). Potentially all of them are already on your system, but let’s be sure.

    sudo apt-get install curl libunwind8 gettext apt-transport-https
    
  3. Create dotnet-core folder in your home and navigate there. This is only place to keep things downloaded during the installation process. At the end, the whole folder can be deleted without any harm.

    mkdir dotnet-core
    cd dotnet-core
    
  4. Download .NET Core SDK 3.1.403 (latest stable at the time of writing) from here.

    Remember that Raspberry OS runs as 32-bit OS, even if the device itself is 64-bit SoC.

    wget https://download.visualstudio.microsoft.com/download/pr/8a2da583-cac8-4490-bcca-2a3667d51142/6a0f7fb4b678904cdb79f3cd4d4767d5/dotnet-sdk-3.1.403-linux-arm.tar.gz
    
  5. Extract it into /opt and make proper links to expose the dotnet binary into your $PATH.

    sudo mkdir /opt/dotnet-3.1.403
    sudo tar zxf dotnet-sdk-3.1.403-linux-arm.tar.gz -C /opt/dotnet-3.1.403
    
    sudo rm dotnet -f
    sudo ln -s /opt/dotnet-3.1.403 /opt/dotnet
    
    sudo rm /usr/local/bin/dotnet -f
    sudo ln -s /opt/dotnet/dotnet /usr/local/bin
    
  6. Check, if everything is correctly applied by command:

    dotnet --info
    

    Expected output:

    .NET Core SDK (reflecting any global.json):
    Version:   3.1.403
    Commit:    9e895200cd
    
    Runtime Environment:
    OS Name:     raspbian
    OS Version:  10
    OS Platform: Linux
    RID:         linux-arm
    Base Path:   /opt/dotnet-3.1.403/sdk/3.1.403/
    
    Host (useful for support):
    Version: 3.1.9
    Commit:  774fc3d6a9
    
    .NET Core SDKs installed:
    3.1.403 [/opt/dotnet-3.1.403/sdk]
    
    .NET Core runtimes installed:
    Microsoft.AspNetCore.App 3.1.9 [/opt/dotnet-3.1.403/shared/Microsoft.AspNetCore.App]
    Microsoft.NETCore.App 3.1.9 [/opt/dotnet-3.1.403/shared/Microsoft.NETCore.App]
    
    To install additional .NET Core runtimes or SDKs:
    https://aka.ms/dotnet-download
    

From now on, it should be possible to compile any .NET Core application on this device and run any .NET Core application, even the one created elsewhere (using command: dotnet publish -r linux-arm). Bravo!

If you need to use as less space as required, the download page contains links for runtimes-only editions, which could be installed instead. Please remember that in this scenario you would need to install ASP.NET Core separately, if required.

ASP.NET Core

Once you play a bit with console applications, you will see that true power of Raspberry Pi lies in hosting your own web-sites, Web APIs and storing data in local databases. In this second part I would like to show, how the Web App / Web API created using Visual Studio 2019 or JetBrains Rider could be hosted seamlessly on the device.

Note: To improve performance all data can be stored on external SSD disk. This will also increase the live of the microSD card used by the Raspberry Pi. If you encounter any issues with moving data to dedicated volume, I already fought it a lot here. Maybe it will show itself useful. I would also recommend to install PostgreSQL or MariaDB (a.k.a MySQL) to store your data. Both can be also configured to keep the data outside of the microSD card.

  1. Create simple Web API project using IDE mentioned above.

  2. Deploy your application onto the device, here into /volumes/www/home-library and start it.

    cd /volumes/www/home-library
    dotnet MyWebServer.dll
    

    This will case the local .NET service to start hosting the app (using .NET Kestrel server). It will have however few major issues:

     * it will stop in case of any application termination exception occur and will never restart
     * it won't also automatically get up, when the device rebooted
     * additionally, it won't be inaccessible for external connections.
    

    To overcome those limitation we need to register the application as a service inside the autostart chain, then install nginx (WWW server) to let the Web API be consumed by remote clients. Official Microsoft guide is located here.

  3. First things first. Creation of startup service is as easy as creation of text file in respective location:

    sudo nano /etc/systemd/system/home-library-app.service
    

    And put following content into this file:

    [Unit]
    Description=CodeTitans HomeLibrary ASP.NET Core 3.0 App
    
    [Service]
    WorkingDirectory=/volumes/www/home-library
    ExecStart=/opt/dotnet/dotnet /volumes/www/home-library/MyWebServer.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=dotnet-home-library
    User=pi
    Environment=ASPNETCORE_ENVIRONMENT=Production
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    
    [Install]
    WantedBy=multi-user.target
    

    Then enable it and start:

    sudo systemctl enable home-library-app.service
    sudo systemctl start home-library-app.service
    

    Of course, this service can be restarted at any time using following command:

    sudo systemctl restart home-library-app.service
    
  4. Now, make sure nginx (WWW server) is installed and enabled.

    sudo apt install nginx
    sudo /etc/init.d/nginx start
    
  5. Optional - if you have a public domain, you can use the Let's Encrypt service to make the server connection more secure. Please refer to this guide for details. It explains how to use certbot to actually generate SSL certificate and enable it within nginx.

  6. Create a new site definition for nginx and enable it:

    sudo nano /etc/nginx/sites-available/home-library-site
    
    sudo ln -s /etc/nginx/sites-available/home-library-site /etc/nginx/sites-enabled
    

    The content of home-library-site without any HTTPS support (without any certificated) should look like following:

    server {
        listen        80;
        server_name   _;    # don't limit to any domains
        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;
        }
    }
    

    With HTTPS enabled, there are 2 things defined inside home-library-site. First we declare listening on port 443 instead of default 80 and paths to SSL keys. Secondly here appears the permanent redirect from non-encrypted traffic, that users can accidentally request to fully encrypted location. It will automatically pass the path and other arguments.

    server {
        listen 443 ssl;
        server_name homelib abc.example.com;
        ssl_certificate     /volumes/www/ssl/fullchain.pem;
        ssl_certificate_key /volumes/www/ssl/privkey.pem;
    
        location / {
            client_max_body_size 256M;
            proxy_pass http://localhost:5000;
            proxy_connect_timeout 600;
            proxy_send_timeout 600;
            proxy_read_timeout 600;
            send_timeout 600;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
    server {
        listen 80;
        server_name homelib abc.example.com;
        return 301 https://abc.example.com$request_uri;
    }
    
  7. After changing any settings Nginx have to be restarted.

    Here are some few ways of doing it:

    sudo nginx -t
    sudo nginx -s reload
    

    or

    service nginx restart
    

That’s actually all. From now on only your imagination and development skills are your limit!

Post scriptum - NuGet Server

There is also a great guide on how to setup own private NuGet server from Scott Hanselman. It’s a useful addon, once the .NET Core is working locally! Unfortunately this server can’t be deployed in Unix environment. The better option would be to use BaGet server instead.

Have a good day, comrade.