GitHub

On this page

CI/CD Pipeline

This runbook explains how Orcta deploys services from GitHub to the VPS using GitHub Actions, Docker, and a shared PostgreSQL cluster.

High-Level Architecture

  • Codebase: GitHub (dev and main branches).
  • CI/CD: GitHub Actions (SSH deploy to VPS) or Orcta Runtime (webhook-triggered).
  • Runtime: Ubuntu VPS running Docker + Docker Compose.
  • Deployment Automation: Orcta Runtime (runtime.orctatech.com) manages multi-app deployments.
  • Monitoring: Lookout (server metrics) + LogDeck (log monitoring).
  • Services:
    • App containers per repo.
    • Shared PostgreSQL cluster (Dockerized) with per-app DBs/roles.
    • Caddy reverse proxy for routing and TLS.

Deployment Flow

  1. Push to main or dev.
  2. GitHub fires webhook to runtime.orctatech.com/webhook.
  3. Orcta Runtime pulls the new image, runs pre-deploy health checks.
  4. Orcta starts the container with docker compose up -d.
  5. Health check polling verifies the app is healthy.
  6. On failure, automatic rollback to last known good version.

Option 2: Direct SSH (Legacy)

  1. Push to main or dev.
  2. GitHub Actions connects to the VPS via SSH.
  3. The repo is cloned or pulled into /srv/apps/<APP>.
  4. Docker Compose rebuilds and restarts the service.
  5. Caddy reloads to pick up new routes.

Architecture Diagram

             ┌───────────────────────┐
             │       GitHub Repo     │
             │  (main & dev branches)│
             └───────────┬───────────┘
                         │
                    Push triggers
                         │
                         ▼
             ┌───────────────────────┐
             │    GitHub Actions     │
             │  (CI/CD Workflows)    │
             └───────────┬───────────┘
                         │
                   SSH Deploys to
                         │
                         ▼
    ┌─────────────────────────────────────────┐
    │                 VPS                     │
    │   (Ubuntu + Docker + Docker Compose)    │
    │                                         │
    │  ┌───────────────────────────────────┐  │
    │  │      Shared Docker Network        │  │
    │  │         `orcta-net`               │  │
    │  │                                   │  │
    │  │  ┌─────────────┐   ┌────────────┐ │  │
    │  │  │   app1      │   │   app2     │ │  │
    │  │  │ dockerized  │   │ dockerized │ │  │
    │  │  │ service     │   │ service    │ │  │
    │  │  └─────┬───────┘   └──────┬─────┘ │  │
    │  │        │                  │       │  │
    │  │        ▼                  ▼       │  │
    │  │   postgres://app1_db   postgres://app2_db
    │  │                                   │  │
    │  │        ┌───────────────────┐      │  │
    │  │        │   PostgreSQL DB   │      │  │
    │  │        │   dockerized      │      │  │
    │  │        │   per-app roles   │      │  │
    │  │        └───────────────────┘      │  │
    │  └───────────────────────────────────┘  │
    │                                         │
    │ Backups → /srv/orcta/postgres/backups/  │
    └─────────────────────────────────────────┘

VPS Directory Layout

/srv/apps
  app1/
    docker-compose.yml
    Caddyfile-snippet
  app2/
    docker-compose.yml
    Caddyfile-snippet

/srv/orcta-postgres
  docker-compose.yml
  init/
    01-init.sql
  backups/
  backup.sh

GitHub Actions (Example)

.github/workflows/deploy.yml

name: CI/CD Pipeline (Direct VPS Build)

on:
  push:
    branches: [dev, main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Deploy on VPS
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            set -e
            APP=orcta-app
            mkdir -p /srv/apps/$APP
            cd /srv/apps/$APP

            if [ ! -d ".git" ]; then
              git clone git@github.com:Orctatech-Engineering-Team/$APP.git .
            else
              git pull origin main
            fi

            docker compose up --build -d

PostgreSQL Setup

/srv/orcta-postgres/docker-compose.yml

version: "3.9"
services:
  postgres:
    image: postgres:16
    container_name: orcta_postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: <set-in-env>
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./init:/docker-entrypoint-initdb.d
    networks:
      - orcta_net

  pgbouncer:
    image: edoburu/pgbouncer
    container_name: orcta_pgbouncer
    restart: always
    ports:
      - "6432:6432"
    environment:
      DB_USER: pgbouncer
      DB_PASSWORD: <set-in-env>
      DB_HOST: postgres
      DB_PORT: 5432
      POOL_MODE: transaction
      MAX_CLIENT_CONN: 200
      DEFAULT_POOL_SIZE: 20
    depends_on:
      - postgres
    networks:
      - orcta_net

networks:
  orcta_net:
    external: true

Example init script (/srv/orcta-postgres/init/01-init.sql):

CREATE USER app1_user WITH PASSWORD '<password>';
CREATE DATABASE app1_db OWNER app1_user;
REVOKE ALL ON DATABASE app1_db FROM PUBLIC;

Caddy Reverse Proxy

Main /etc/caddy/Caddyfile:

{
    email admin@orctatech.com
}

import /srv/apps/*/Caddyfile-snippet

Example snippet:

orcta.example.com {
    reverse_proxy localhost:9999
}

Backups

/srv/orcta-postgres/backup.sh:

#!/bin/bash
TIMESTAMP=$(date +%F_%H-%M-%S)
docker exec -t orcta-postgres pg_dumpall -U postgres > /srv/orcta-postgres/backups/db_$TIMESTAMP.sql

Cron (2 AM daily):

0 2 * * * /srv/orcta-postgres/backup.sh

Monitoring & Troubleshooting

  • App logs: docker logs -f <container>
  • Postgres logs: docker logs -f orcta-postgres
  • Caddy logs: journalctl -u caddy --no-pager -n 100
  • Orcta Runtime dashboard: https://runtime.orctatech.com/login
  • Lookout status: journalctl -u lookout -f
  • LogDeck: https://logdeck.orctatech.com

Orcta Runtime Setup

Orcta Runtime is the deployment automation platform that manages multi-app Docker Compose deploys.

Initial Setup

# On the VPS
git clone git@github.com:Orctatech-Engineering-Team/orcta-runtime.git ~/orcta-runtime
cd ~/orcta-runtime

# Create .env
cp .env.example .env
# Edit .env with:
#   AUTH_TOKEN=<your-admin-token>
#   WEBHOOK_SECRET=<your-github-webhook-secret>

# Build and start
docker compose build
docker compose up -d

Register an App

# Create an app entry
curl -X POST https://runtime.orctatech.com/api/apps \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-app",
    "compose_dir": "/srv/apps/my-app",
    "health_url": "http://localhost:3000/health",
    "timeout_sec": 60
  }'

Configure GitHub Webhook

In your GitHub repo → Settings → Webhooks → Add webhook:

  • Payload URL: https://runtime.orctatech.com/webhook
  • Content type: application/json
  • Secret: Same as WEBHOOK_SECRET in .env
  • Events: Just the push event

Install Monitoring (Lookout)

# Run the bootstrap action via API
curl -X POST https://runtime.orctatech.com/api/execute/bootstrap-monitoring \
  -H "Authorization: Bearer <operator-token>"

Or install manually:

curl -fsSL https://raw.githubusercontent.com/AmoabaKelvin/lookout/main/install.sh | sudo sh
Edit this page
Last modified: 2026-08-03