ci(deploy): auto-deploy to TrueNAS via self-hosted Gitea Actions runner
Deploy to TrueNAS / deploy (push) Failing after 3s

- Replace VPS SSH deploy workflow with a self-contained job that runs on the
  TrueNAS act_runner (host docker socket mounted). Checks out the pushed commit,
  builds, and rolls out the cannamanage compose stack in-place (project=cannamanage),
  then health-checks backend :8081 + frontend :3000.
- Commit docker-compose.truenas.yml (port remap 8081 + AUTH_SECRET) into the repo;
  it was previously host-only, so a fresh checkout could not reproduce the deploy.
  Use the !override tag for the backend ports list.
This commit is contained in:
Patrick Plate
2026-06-16 18:52:18 +02:00
parent 59b785b8ed
commit 3b15d7439d
2 changed files with 94 additions and 42 deletions
+72 -40
View File
@@ -1,51 +1,83 @@
name: Deploy to Production
name: Deploy to TrueNAS
# Auto-deploy on push to main.
# Runs on the self-hosted Gitea Actions runner on TrueNAS.local
# (container: cannamanage-act-runner). The runner mounts the host Docker
# socket into the job container, so `docker compose` commands act on the
# TrueNAS Docker daemon and (re)build/restart the live cannamanage stack.
#
# The job checks the repo out into its own workspace and builds from there,
# so it always deploys exactly the pushed commit — it does NOT depend on the
# old /mnt/VM_SSD_Pool/cannamanage host checkout.
#
# Compose project name is pinned to "cannamanage" so it updates the existing
# containers and reuses the persistent "cannamanage_pgdata" volume on the host.
# Live ports: backend 8081->8080, frontend 3000, db 5432
on:
push:
branches: [main]
# Avoid overlapping deploys if pushes land in quick succession.
concurrency:
group: truenas-deploy
cancel-in-progress: false
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Run backend tests
run: ./mvnw verify -B -q
deploy:
needs: test
runs-on: ubuntu-latest
env:
COMPOSE: docker compose -f docker-compose.yml -f docker-compose.truenas.yml -p cannamanage
steps:
- name: Deploy to production
uses: appleboy/ssh-action@v1
with:
host: plate-software.de
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/cannamanage
git pull origin main
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d
- name: Check out pushed commit
uses: actions/checkout@v4
# Wait for backend health
sleep 15
for i in 1 2 3 4 5; do
if curl -sf http://127.0.0.1:8080/actuator/health > /dev/null 2>&1; then
echo "✅ Deploy successful at $(date)"
exit 0
fi
echo "Waiting... attempt $i/5"
sleep 5
done
- name: Show toolchain
run: |
set -euo pipefail
docker version --format 'docker {{.Server.Version}}'
docker compose version
echo "❌ Deploy failed — backend unhealthy"
docker compose -f docker-compose.prod.yml logs --tail=30 backend
exit 1
- name: Build images
run: |
set -euo pipefail
$COMPOSE build
- name: Roll out stack
run: |
set -euo pipefail
$COMPOSE up -d --remove-orphans
- name: Wait for backend health
run: |
set -euo pipefail
echo "Waiting for backend health on :8081 ..."
for i in $(seq 1 20); do
if wget -q -O /dev/null http://192.168.188.119:8081/actuator/health; then
echo "✅ Backend healthy after ${i} attempt(s)"
exit 0
fi
echo " attempt $i/20 — waiting 6s"
sleep 6
done
echo "❌ Backend did not become healthy — recent logs:"
$COMPOSE logs --tail=40 backend
exit 1
- name: Verify frontend
run: |
if wget -q -O /dev/null http://192.168.188.119:3000; then
echo "✅ Frontend responding on :3000"
else
echo "⚠️ Frontend not responding yet (may still be starting)"
fi
- name: Prune dangling images
run: docker image prune -f || true
- name: Deployment summary
run: |
echo "=== CannaManage deployed to TrueNAS ==="
echo "Commit: ${GITHUB_SHA}"
echo "Backend: http://192.168.188.119:8081"
echo "Frontend: http://192.168.188.119:3000"
+20
View File
@@ -0,0 +1,20 @@
# TrueNAS homelab override — replaces localhost with 192.168.188.119
# Applied on top of docker-compose.yml for the homelab deployment on TrueNAS.local.
# Usage:
# docker compose -f docker-compose.yml -f docker-compose.truenas.yml up -d --build
services:
backend:
# Host port 8080 is taken by odysseus-searxng-1; remap to 8081.
# !override replaces the inherited ports list (compose merges lists by concat otherwise).
# Internal container port stays 8080 so frontend's BACKEND_URL=http://backend:8080 is unaffected.
ports: !override
- "8081:8080"
frontend:
environment:
NEXTAUTH_URL: http://192.168.188.119:3000
AUTH_URL: http://192.168.188.119:3000
# NextAuth v5 (Auth.js) reads AUTH_SECRET, not NEXTAUTH_SECRET. Without it at
# runtime, signIn throws MissingSecret -> the app error boundary shows 'Oops'.
AUTH_SECRET: docker-dev-nextauth-secret-minimum-32chars
AUTH_TRUST_HOST: "true"