hero image of Deploy .NET Project into VPS using Github Action

Deploy .NET Project into VPS using Github Action


What is Github Actions ?

Github Actions is a development tools that allows us to tell Github to do some tasks when some events happen in our repository. What kind of events ? it’s up to us!. For example, when we make a push into main branch, we can tell Github to run some tests, build, and even deploy our project to a server. So, basically, Github gives us a temporary virtual machine and we can tell it what to do and it’s free!

Deploying .NET project into a VPS

Before we start, make sure you have a VPS that runs on linux and you have the SSH access to it. Since it is easier to use docker to run our .NET project, make sure you have docker installed on your VPS. If you don’t have docker installed, you can follow the instructions here. Also make sure you have the docker hub account. If you don’t want to use docker, you can actually use scp to copy the published files to your VPS.

Step 1: Create a new Github Action workflow

First, we need to create a new workflow file in our repository. To do that, create a new file in your repository under the path .github/workflows/deploy.yml. You can name the file whatever you want, but it must be under the .github/workflows directory.

Step 2: Define the workflow

In the deploy.yml file, we need to define the workflow. I will assume you have setup your Dockerfile. Here is an example of a workflow that will deploy a .NET project to a VPS using SSH and Docker:

name: Deploy .NET apps into non-production VPS

on:
    push:
        branches: [uat]

jobs:
    build-and-deploy:
        runs-on: ubuntu-latest
        environment: uat

        steps:
            - name: Checkout repository
              uses: actions/checkout@v4

            - name: Login to Docker Hub
              uses: docker/login-action@v2
              with:
                  username: ${{ secrets.DOCKER_HUB_USERNAME }}
                  password: ${{ secrets.DOCKER_HUB_TOKEN }}

            - name: Build and push Docker image
              run: |
                  docker build -t ${{ secrets.DOCKER_IMAGE_NAME }} .
                  docker push ${{ secrets.DOCKER_IMAGE_NAME }}

            - name: Deploy to VPS
              uses: appleboy/ssh-action@master
              with:
                host: ${{ secrets.VPS_HOST }}
                username: ${{ secrets.VPS_USERNAME }}
                key: ${{ secrets.VPS_SSH_KEY }}
                script: |
                  docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin <<< "${{ secrets.DOCKER_HUB_TOKEN }}"
                  docker pull ${{ secrets.DOCKER_IMAGE_NAME }}
                  docker stop be || true
                  docker rm be || true
                  docker run -d \
                    --name be \
                    --restart unless-stopped \
                    -p 8080:8080 \
                    ${{ secrets.DOCKER_IMAGE_NAME }}

Step 3: Add secrets to your repository

In the above workflow, we are using some secrets to store sensitive information like Docker Hub credentials and VPS SSH key. To add secrets to your repository, go to your repository on GitHub, click on Settings, then click on Secrets in the left sidebar, and then click on New repository secret. Notice that in the above workflow, we are uat environtment. Add the following secrets:

  • DOCKER_HUB_USERNAME: Your Docker Hub username.
  • DOCKER_HUB_TOKEN: A Docker Hub access token. You can create one here.
  • DOCKER_IMAGE_NAME: The name of your Docker image (e.g., username/repository:tag).
  • VPS_HOST: The IP address or hostname of your VPS.
  • VPS_USERNAME: The username to SSH into your VPS.
  • VPS_SSH_KEY: The private SSH key to access your VPS. Make sure the corresponding public key is added to the ~/.ssh/authorized_keys file on your VPS

Step 4: Push changes to the repository

Now, whenever you push changes to the uat branch, the workflow will be triggered, and your .NET project will be built, pushed to Docker Hub, and deployed to your VPS.

What if I don’t want to use Docker?

If you don’t want to use Docker, you can modify any command that uses docker here. Instead of build the docker image, you build & publish the project in the github action machine. Instead of you pull and run the docker image, you copy (using scp) the published project into your vps. Here is an example of how to do that:

name: Deploy .NET apps into non-production VPS

on:
    push:
        branches: [uat]

jobs:
    build-and-deploy:
        runs-on: ubuntu-latest
        environment: uat

        steps:
            - name: Checkout repository
              uses: actions/checkout@v4

            - name: Setup .NET
              uses: actions/setup-dotnet@v3
              with:
                  dotnet-version: '9.0.0' # Change this to your .NET version

            - name: Restore, Build & Publish
              run: |
                dotnet restore
                dotnet build --no-restore -c Release
                dotnet publish -c Release -o output

            - name: Copy files to VPS
              uses: appleboy/scp-action@master
              with:
                host: ${{ secrets.VPS_HOST }}
                username: ${{ secrets.VPS_USERNAME }}
                key: ${{ secrets.VPS_SSH_KEY }}
                source: "./output/*"
                target: "~/yourapp"

            - name: Deploy to VPS
              uses: appleboy/ssh-action@master
              with:
                host: ${{ secrets.VPS_HOST }}
                username: ${{ secrets.VPS_USERNAME }}
                key: ${{ secrets.VPS_SSH_KEY }}
                script: |
                  pkill -f 'dotnet yourapp.dll' || true
                  cd ~/yourapp
                  nohup dotnet yourapp.dll > app.log 2>&1 &

Bottom line

So now we have learned how to deploy a .NET project to a VPS using Github Actions. The bottom line all we to do is to tell Github to do following tasks:

  1. Checkout the repository
  2. Build & publish the project (be it in docker or not)
  3. Tell Github to connect to our VPS using SSH then run some commands to copy the files or pull the docker image then run it.

Rooms for improvement

There are some rooms for improvement in the above workflow. For examples:

  • Add some tests before deploying the project.
  • Add notification to Discord/Slack when the deployment is successful or failed.
  • Split the workflow into multiple jobs for better readability and maintainability.
  • Implement blue-green deployment or canary deployment for zero-downtime deployment.