Skip to main content

Provisioning a New Server

This guide explains how to provision a new server for SwanMaint and deploy the API using Docker, PostgreSQL, GitHub Container Registry, and GitHub Actions.

Overview

Deployment flow:

  1. GitHub Actions builds and tests the application
  2. Builds a Docker image
  3. Pushes to GitHub Container Registry (GHCR)
  4. VPS pulls latest image
  5. Docker Compose runs API + PostgreSQL
  6. Optional: expose via Cloudflare Tunnel

Prerequisites

  • VPS with SSH access
  • Docker + Docker Compose installed
  • GitHub repo with Actions enabled
  • GHCR package created
  • PAT (classic) with read:packages
  • Domain (optional)

1. Prepare Server

mkdir -p /opt/swanmaint-test
cd /opt/swanmaint-test

2. Install Docker

apt update
apt install -y docker.io docker-compose-plugin
systemctl enable docker
systemctl start docker

3. Login to GHCR

docker login ghcr.io -u YOUR_USERNAME

(password = PAT)

Test:

docker pull ghcr.io/maintfm/maintfmapi:latest

4. Create .env

nano .env
DB_CONNECTION_STRING=Server=db;Port=5432;Database=swanmaint;Username=swanuser;Password=swanpass;Include Error Detail=True;
ASPNETCORE_ENVIRONMENT=Test

5. Create docker-compose

nano docker-compose.test.yml
services:
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: swanmaint
POSTGRES_USER: swanuser
POSTGRES_PASSWORD: swanpass
volumes:
- swanmaint_postgres_data:/var/lib/postgresql/data

api:
image: ghcr.io/maintfm/maintfmapi:latest
restart: unless-stopped
depends_on:
- db
env_file:
- .env
ports:
- "5216:5216"

volumes:
swanmaint_postgres_data:

6. Start Services

docker compose -f docker-compose.test.yml pull
docker compose -f docker-compose.test.yml up -d

Check:

docker ps
docker logs --tail 100 swanmaint-api

7. Healthcheck

Add endpoint in API:

app.MapGet("/healthz", () => Results.Ok("ok")).AllowAnonymous();

Dockerfile:

HEALTHCHECK CMD wget -qO- http://127.0.0.1:5216/healthz || exit 1

8. SSH Key Setup (for deployment)

On your machine:

ssh-keygen -t ed25519 -C "github-actions-hostinger" -f ~/.ssh/hostinger_github_actions

Add public key to VPS:

nano ~/.ssh/authorized_keys

9. GitHub Secrets

Add:

  • VPS_HOST
  • VPS_USER
  • VPS_SSH_KEY

10. GitHub Actions Workflow

name: Build, Publish, Deploy

on:
push:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- run: dotnet restore
- run: dotnet build --configuration Release
- run: dotnet test --configuration Release

- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository_owner }}/maintfmapi:latest

deploy:
needs: build
runs-on: ubuntu-latest
environment: test

steps:
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /opt/swanmaint-test
docker compose pull
docker compose up -d

11. Deploy

Push to dev:

git push origin dev

Done

Your server will now:

  • pull latest image
  • restart containers
  • run API + Postgres