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:
- GitHub Actions builds and tests the application
- Builds a Docker image
- Pushes to GitHub Container Registry (GHCR)
- VPS pulls latest image
- Docker Compose runs API + PostgreSQL
- 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