The Runtime Identifier Imperative in .NET Core 3 Deployment


The Runtime Identifier Imperative in .NET Core 3 Deployment

Deploying a ASP.NET Core 3.1 applications have gotten a little more specific than previous versions.

/appengine/docs/flexible/dotnet/reference/request-headers), but I digress.

We've got a web application that uses Google Cloud's Firestore database for storing web page and blog content. When deploying this app to the server, I ran into an issue regarding I recently ran into an encounter where I tried deploying a .NET Core 3.1 web application to a Linux VM (they just run so much faster - and cheaper - than containers!) when I ran into a deployment issue I hadn't ran into before with my 2.2 apps.

Let me quickly spell out the two errors I ran into in my deployment, and then tell you how I fixed them both.

First I got this:

An assembly specified in the application dependencies manifest (MyApp.deps.json) was not found:
    package: 'Grpc.Core', version: '1.22.1'
    path: 'runtimes/linux/native/'

After changing my deployment method to specify the framework and and flag my app as self-contained, I ended up getting this:

The library '' required to execute the application was not found

The short answer as to why I was getting this problem was pretty simple - I use rsync to push my apps into the VM, and my parameters were wrong. I was using rsync -rC for uploading files. Once I changed my command to rsync -arvz I could see what was deploying, and everything was syncing correctly.

Because I am deploying from a Linux machine to another Linux machine, everything has been pretty consistent. In fact, if I specify my command in this manner, I can deploy without any issues:

dotnet publish -c Release

However, if you watch the rsync start syncing, you notice there are a lot of .so files loading into the directory that don't necessarily need to be there.

That's a result of the Runtime Identifier Catalog. .NET Core doesn't know where it's going - it only knows what platform you're compiling on, and assumes you're going to the same general destination. And because no specific version is specified, it assumes linux, which in turn assumes unix, which in turn imports all of them.

What does that mean? It means if you're deploying into a 64-bit Linux server, you'd do something like this:

dotnet publish -c Release -r linux-x64

For a 64-bit Ubuntu 18.04 server, you'll want to configure your publish command like so:

dotnet publish -c Release -r ubuntu.18.04-x64 -f netcoreapp3.1

Note that we probably don't need the -f command since Rider does a good job of specifying the framework version, but it never hurts to throw it in there for good measure - just in case.


The full list of OS runtimes in the Runtime Identifier Catalog can be found at Make sure you save it somewhere, and use it as a reference when configuring your deployment!

Sample Deployment Script

Here's the script I now use to deploy my web application over SSH into my web server:

echo 'stopping app...'
ssh -t user@my.ip.address sudo systemctl stop mywebapp.service
echo 'building app...'
dotnet publish -c Release  -r ubuntu.18.04-x64 -f netcoreapp3.1 #--self-contained
echo '--------'
echo 'syncing build output...'
rsync -arvz /my/publish/path/ user@my.ip.address:/var/www/mywebapp/
echo 'starting app...'
ssh -t user@my.ip.address sudo systemctl start mywebapp.service
echo '--------'
echo 'sync complete!'

In another post, I'll list out the steps on how to set up and configure web apps on Ubuntu using Nginx and Let's Encrypt.

Looking for help building or integrating your web application? Contact us for a quote - not only is it free, but we can help you find what you're looking for at a better price than most consulting firms!