Deploying .NET Core Apps to Ubuntu with rsync

A common way for reliably hosting .NET Core Apps on Ubuntu is to use supervisor to monitor the dotnet self-hosting processes behind an nginx reverse proxy which handles external HTTP requests to your website and proxies them to the dotnet process running your Web App on a local port. You'll need access to a Unix environment on your client Desktop, either using Linux, OSX or Installing Windows Subsystem for Linux (WSL).

Setup the deploy User Account

We'll start by creating a dedicated user account for hosting and running your .NET Core Apps to mitigate potential abuse. SSH into your Ubuntu server and create the deploy user account with a /home/deploy home directory and add them to the sudo group:

$ sudo useradd -m deploy
$ sudo usermod -aG sudo deploy

For seamless deployments use visudo:

visudo

To allow deploy to run supervisorctl without prompting for a password:

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL
%deploy ALL=(ALL:ALL) NOPASSWD: /usr/bin/supervisorctl

Tip

In vi type i to start editing a file and ESC to quit edit mode and :wq to save your changes before exiting

Setup supervisor

Install supervisor using apt-get:

sudo apt-get install supervisor

You'll need to create a separate config file for each app in /etc/supervisor/conf.d/. We can use the same template below by replacing myapp with the name of your App:

/etc/supervisor/conf.d/web.myapp.conf

[program:web-myapp]
command=/usr/bin/dotnet /home/deploy/apps/myapp/MyApp.dll
directory=/home/deploy/apps/myapp
autostart=true
autorestart=true
stderr_logfile=/var/log/web-myapp.err.log
stdout_logfile=/var/log/web-myapp.out.log
environment=ASPNETCORE_ENVIRONMENT=Production
user=deploy
stopsignal=INT

Setup nginx

You'll also need to create a separate config for each website on nginx in /etc/nginx/sites-available/. You can use the same template for each website but you'll need to change the server_name with the domain name you want to use for the App and use a different port number for each App:

/etc/nginx/sites-available/myapp.example.org

server {
    listen       80;
    server_name myapp.example.org;

    location / {
        proxy_pass http://localhost:5001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_ignore_client_abort off;
        proxy_intercept_errors on;

        client_max_body_size 500m;
    }
}

You'll then need to create symlink for each website to tell nginx you want each website to be enabled:

ln -s /etc/nginx/sites-available/myapp.example.org /etc/nginx/sites-enabled/myapp.example.org

After this we can tell nginx to reload its configuration, as there's nothing listening to http://localhost:5001 yet nginx will return a 502 Bad Gateway response but will start working as soon as our deployed .NET Core Apps are up and running.

/etc/init.d/nginx reload

Setting up SSH keys

We can now exit our remote Linux server and return to our local machine and prepare our deployment script. Before doing this we recommend setting up SSH and copying your SSH public key to your remote server which is both more secure and more convenient than using a password.

Create the deployment script

rsync is a beautiful utility that provides a fast, secure file transfer over SSH which you can use to sync the contents of folders to a remote site. There's only 2 commands you need to run to deploy a local .NET Core App remotely, rsync to sync the published .NET Core App files and supervisorctl to restart the supervisord process that runs and monitor the .NET Core App which you can add to a deploy.sh that you can run with WSL bash:

rsync -avz -e 'ssh' bin/Release/netcoreapp3.1/publish/ ubuntu@myapp.example.org:/home/deploy/apps/myapp
ssh ubuntu@myapp.example.org "sudo supervisorctl restart web-myapp"

To automate the entire deployment down to a single command you can add an npm script to your project's package.json that creates a production client and server build of your App before running WSL's bash to run the deploy script. All Webpack Single Page App Templates already have a publish npm script, so you would just need to add a deploy script to run publish before running the above deploy.sh

{
    "publish": "nuxt build && dotnet publish -c Release",
    "deploy": "npm run publish && bash deploy.sh"
}

Now to deploy your App you can just run:

npm run deploy

Which deploys your published App to your remote Ubuntu server instance using rsync to only copy the incremental parts of the App that's changed (typically completing in <1s) and ssh to run a remote command to restart the suprvisord process, starting the .NET Core App with the latest deployed version.