This is a part two of my .NET Core on Linux series, if you missed part one check out the first post Hosting .NET Core On Linux.

In this article we'll be setting up a continuous delivery pipeline to ensure that your code is building and deployable all the time. I'll show you how to use Visual Studio Team Services (VSTS) to build your code and then set up a release plan to deploy your code to a Linux server via SSH.

If you've never used VSTS before it is work a look. You get free private repos and the build and release tools that will be used in this article. It offers one of the easier ways to build your ASP.NET Core 1.x or 2.0 projects, and has a host of other features as well. To learn more check it out at

In my Hosting .NET Core On Linux article I used Ubuntu Linux so for consistency I will continue using it here as well. The Ubuntu server is hosted on Digital Ocean and you can get your own Digital Ocean account here (it even comes with a $10 credit that should be enough to run your own Ubuntu server for two months and try this out for yourself). This article will show you how to deploy with SSH so you can run your server elsewhere too.

Prepare Your Server for Deployments

Before we get to setting up the build and deployment on VSTS we'll need to do a few things on the server to get it ready to accept deplyoments.

Create a Deployment User & Group

First create a new user to use for deployments:

sudo adduser deploy

Next create a new group for the deployment capable users:

sudo addgroup deployers

Add your deploy user to the group:

sudo adduser deploy deployers

You may also want to add your personal user to the deployers group if you plan to ever manually access the directories where your content will be deployed.

Secure the Deployment User Account

Set up a new unique SSH key for the deploy user, you'll need to make sure that the new key is added to the deploy user's ~/.ssh/authorized_keys and keep the private key handy because you'll need it later while setting up the deployment.

To find out more about setting up your own SSH key check out this article written by the Digital Ocean community.

Give Your Deployment Group Access

Make sure that the locations that will receive deployments (or be manipulated by the deployment) owned by the deployment user & group:

sudo chown -R deploy:deployers /var/www/sample

In the above sample I'm changing the owner of all the files and directories my /var/www/sample directory, this is my Nginx folder where I'll deploy my files.

Allow Restarting the Service Without Sudo Access

Using sudoers or sudoers.d will allow you to specify commands that can be run by users as root without needing to be added to the sudo group. Add a new file to /etc/sudoers.d that will allow your deploy user to restart the systemd service for your ASP.NET Core site. Edit the file:

Cmnd_Alias RESTART_SVC = /bin/systemctl restart kestrel-sample.service

Save the edits and lock it down with chmod (640 or 440), your deploy user should now be able to restart the service.

Configure Visual Studio Team Services to Build & Deploy to Linux

In the past I've used two different methods to handle building and deploying code. One is a simple build script that also has an SSH copy task embedded in it. This method is quick and easy assuming you don't need to worry about promoting builds across environments or will never need to redeploy past builds. Using this method, if you need to redeploy a previous build you'd have to manually recreate the build and let the process deploy it, this is less than perfect.

Doing a little extra work to have more control over build artifacts, deployments, and promotions is worth it so I'll be showing you how to set up a two step build & release.

Building ASP.NET Core With VSTS

To get started set up a new project where you will store your source code, build, and release plans. Create a new project by clicking on the "New Project" menu item. We'll use Git for version control and you can select whatever you like for work items (they wont matter for this).

Create New Project

Next you'll need to get some code pushed to your repo. If you don't already have some code you'd like deployed to Linux, you can start off with a new sample ASP.NET Core project which can be created with the dotnet CLI dotnet new mvc -n DeploySample. After you've pushed some code to your repository click the "Build and Release" menu item from the top nav bar then select "Builds" from the secondary nav bar.

Build and Release

Click "New Definition" to start setting up a new build and choose the "ASP.NET Core" Template and click the "Apply" button.

Choose ASPNETCore

Initial VSTS Build Configuration

Before we configure the main part of the build we need to set a build agent and a name for the build. On the process task set a name for the build and set the "Agent Queue" to Hosted VS2017.

Setting up the Get Sources Task

Get sources is how your build plan acquires your code for the build process. Click the "Get Sources" item on the left side and select "This Project" as your from and make sure the Repository and branch are set for the code you want to build. For the clean options I like to specify "Clean" as true and set "Clean Options" to all build directories.

Get Sources

If you're using submodules in your git repository they won't automatically get pulled down for the build on VSTS, but there is a flag so that they can be included. Go to Get Sources > VSTS Project > Advanced settings and you can enable the "Checkout Submodules" setting.

Configure the actual ASP.NET Core Build on VSTS

Next Click on "Phase 1" found on the left side. Enter a "Display Name" value that makes sense to you for your build task. Now you'll need to configure the remaining steps. For the restore, build, test, and publish steps the only change that needs to be made from the default configuration is to set the Version to "2.* (preview)", leave the rest of the values to their default values.

You might have noticed I didn't mention configuring the Publish Artifact step, that's because you can leave its version set to "1.*".

After you've configured each step click on the "Save & queue" link near the top right side, just to the right of your build project's name. You'll get a popup to save the build definition and queue a build, click the "Save & queue" button at the bottom and you'll see an alert towards the top of the screen like this:

Queue New Build

You can click the link to watch the build do its thing, or just give it a couple minutes to complete.

Click on the Builds link on the top left to go back to your list of build definitions and you should see the status of your build is "succeeded". If it's still in progress keep waiting and check in another minute or two.

VSTS Release Configuration

Now that your code is building click the Releases menu item and then click the "New definition" button to start configuring a new release plan. You'll be greeted by a screen with a lot stuff on it, but don't worry because all we need for this definition is to start with the "Empty process" link from the top, so click it and then give your environment a name and close the modal.

New release plan

Configuring Your VSTS Deployment Environments

You can add one or more environments for your release to work with, for now we'll just stick to one environment. Click the "1 phase, 0 task" link in your new environment.

Adding an SSH Copy Task

Click the + next to "Agent phase"

Sample release plan

On the Add tasks screen use the search box to find the "Copy Files Over SSH" task and add it. Set the display name to whatever you find useful, skip the SSH endpoint for now.

Set the Source Folder by clicking the ellipsis (...) button to the right and select the linked artifact, it should end up with a source folder value along the lines of $(System.DefaultWorkingDirectory)/sample-project-ASP.NET Core-CI.

Leave the contents to ** and set your target folder to the location on your server you want to deploy the code, for this example I'm putting the code in /var/www/sample.

Adding a New SSH Endpoint

You'll recall we skipped over the SSH enpoint earlier, now it is time to set it up too. Click the "Manage" link to the right of SSH endpoint, then in the next screen click "New Service Endpoint" and select "SSH". This will prompt you for some information:

Manage SSH Keys

For the Private Key field you'll want to use the private key we created earlier so that VSTS can access your server to deploy the build.

After you've saved your new service endpoint, go back to the release definition and click the refresh button to the right of the SSH endpoint box, then you'll be able to select the end point you just created. Your copy task should end up looking something like this:

SSH Copy Configured

Adding an SSH Command Task to Delete Old Release Files

Now that the Release Agent can communicate with the Linux Server we'll be creating a few SSH tasks to make the deployment happen.

Add a new task to the Agent Phase and in the search box enter "SSH", then add the SSH Task to the Agent Phase. Set the SSH endpoint to the one you just created.

This first task will be in charge of deleting the old release on the server, so name it appropriately and then enter the commands needed to delete the old release files. Typically I use rm -f and specify the file(s) and/or directories to remove.

Adding an SSH Command Task to Unzip the VSTS Build Drop File

Create another SSH task, give it a good name and set the SSH endpoint. The VSTS build process creates a zip file artifact, in this task we'll unzip it to the location where we want to host the ASP.NET Core site. After unzipping the drop file we can remove it from the system.

Adding an SSH Command Task to Restart the Service

Create a third SSH task that will restart the systemd task for the ASP.NET Core web service. If you followed along with my Hosting .NET Core On Linux post you should have a service with a name along the lines of kestrel-sample.service this will be the service we need to restart.

Give your SSH task name and set the SSH endpoint and set the command to restart the aforementioned service:

sudo systemctl restart kestrel-sample.service

Did you notice that this command is using sudo? Do you recall when we set up the deploy user it wasn't added to the sudo group? This is why we set up sudoers.d in the preperation steps earlier in this post. That extra configuration means the deploy user should now be able to restart the service without being in the sudo group, which makes our server a little bit more secure.

Selecting Which Code to Deploy in VSTS Release Plans

When the build ran earlier it generated an artifact, which is the build results. To tell our release plan which build & artifact to deploy we will add an artifact by clicking on the Add Artifact box on the left side.

Build Artifacts Release

The source type should be set to build, and the you'll need to set the Source Build Definition. The dropdown should be populated with the name of the build that was just created. Select that build and the deployment will use the artifact published in the final step of the build.

Setting up Continuous Deployment for your VSTS Release Plan

Setting up the continuous deployment trigger isn't the clearest part of creating a new build definition. On the artifact that was added there is a circle with a lightning bolt in it, click it.

Continuous Deployment Trigger

In the box that pops up enable the trigger, then specify the branch you'd like to use to trigger this release. This tells VSTS to start a new release when a build completes successfully on the given build plan.

Wrapping Up

By now you should have a fully functional build & deployment pipeline for your ASP.NET Core application. Deploying the latest version of your code to your Ubuntu server should now be as simple as a git push and a couple minute wait.

If you missed my post on Hosting .NET Core On Linux be sure to go read it so you can get your ASP.NET Core application running properly on Ubuntu.