Page:
CannaManage 09 Deployment
Pages
01 Charter
02 UserStories
03 Architecture
04 Flowcharts
05 API
06 Wireframes
07 CodingStandards
08 TestPlan
09 Deployment
10 Retrospective
11 Features
CannaManage 01 Charter
CannaManage 02 UserStories
CannaManage 03 Architecture
CannaManage 04 Flowcharts
CannaManage 05 API
CannaManage 06 Wireframes
CannaManage 07 CodingStandards
CannaManage 08 TestPlan
CannaManage 09 Deployment
CannaManage 10 Retrospective
CannaManage 11 Features
CannaManage Home
Charter
Home
Clone
3
CannaManage 09 Deployment
Patrick Plate edited this page 2026-06-19 16:43:56 +02:00
09 — Deployment Guide
Project: CannaManage — B2B SaaS for German Cannabis Social Clubs Version: 14.0 (Sprint 14) Date: 2026-06-19 Target environment: TrueNAS Docker — Docker Compose (Production) | Gitea Actions (CI/CD)
1. Infrastructure Overview
graph TB
Dev["👨💻 Dev Workstation\n(macOS)"]
Gitea["🏠 Gitea\n(TrueNAS :30008)"]
Runner["⚙️ Gitea Actions Runner\n(TrueNAS Docker)"]
Dev -->|"git push"| Gitea
Gitea -->|"triggers CI"| Runner
Runner -->|"build + test + deploy"| Prod
subgraph Prod ["🖧 TrueNAS — Production (Docker Compose)"]
Nginx["🔒 Nginx\nreverse proxy + TLS\n:443 → :8080/:3000"]
Backend["☕ cannamanage-app\nSpring Boot 4.0.6\n:8080"]
Frontend["⚛️ cannamanage-frontend\nNext.js 15\n:3000"]
DB["🐘 PostgreSQL 16\n:5432\n(persistent volume)"]
Nginx --> Backend
Nginx --> Frontend
Backend --> DB
end
Internet["🌍 Internet\nhttps://cannamanage.plate-software.de"] -->|":443"| Nginx
Environment Summary
| Environment | Host | Purpose |
|---|---|---|
| Development | macOS (local) | Feature development, unit tests, docker-compose.yml |
| CI/CD | TrueNAS Gitea Actions Runner | Automated build, test (PostgreSQL service container), deploy |
| Production | TrueNAS Docker Compose | Live application at cannamanage.plate-software.de |
Note: The project previously planned Hetzner VPS hosting but migrated to TrueNAS Docker for cost savings and network locality. The production URL is forwarded to the TrueNAS instance via port forwarding.
2. Prerequisites
TrueNAS Docker Host
| Resource | Value |
|---|---|
| Platform | TrueNAS Scale (Docker) |
| Docker Engine | 24+ |
| Docker Compose | v2 (compose plugin) |
| RAM allocated | 4 GB+ for all containers |
| Storage | Persistent volumes on ZFS pools |
DNS / Networking
| Record | Value |
|---|---|
| Domain | cannamanage.plate-software.de |
| TLS | Let's Encrypt via Nginx (certbot) |
| Port forwarding | Router :443 → TrueNAS :443 |
Required Software (on TrueNAS)
- Docker Engine 24+
- Docker Compose v2
- Gitea (self-hosted, port 30008)
- Gitea Actions Runner (registered)
- Certbot / Nginx for TLS
3. Docker Compose — Production
File: docker-compose.truenas.yml
networks:
cannamanage_net:
driver: bridge
volumes:
pgdata:
driver: local
services:
db:
image: postgres:16-alpine
container_name: cannamanage-db
environment:
POSTGRES_DB: cannamanage
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- cannamanage_net
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: .
dockerfile: Dockerfile.backend
container_name: cannamanage-app
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/cannamanage
SPRING_DATASOURCE_USERNAME: ${DB_USER}
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
SPRING_PROFILES_ACTIVE: production
JWT_SECRET: ${JWT_SECRET}
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET}
MAIL_HOST: ${MAIL_HOST}
MAIL_USERNAME: ${MAIL_USERNAME}
MAIL_PASSWORD: ${MAIL_PASSWORD}
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
networks:
- cannamanage_net
restart: unless-stopped
frontend:
build:
context: ./cannamanage-frontend
dockerfile: Dockerfile
container_name: cannamanage-frontend
environment:
NEXTAUTH_URL: https://cannamanage.plate-software.de
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
NEXT_PUBLIC_API_URL: https://cannamanage.plate-software.de/api
ports:
- "3000:3000"
depends_on:
- backend
networks:
- cannamanage_net
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: cannamanage-nginx
ports:
- "443:443"
- "80:80"
volumes:
- ./deploy/nginx/cannamanage.conf:/etc/nginx/conf.d/default.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- backend
- frontend
networks:
- cannamanage_net
restart: unless-stopped
4. Nginx Configuration
File: deploy/nginx/cannamanage.conf
server {
listen 80;
server_name cannamanage.plate-software.de;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name cannamanage.plate-software.de;
ssl_certificate /etc/letsencrypt/live/cannamanage.plate-software.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cannamanage.plate-software.de/privkey.pem;
# Backend API
location /api/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Swagger UI
location /swagger-ui/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
location /v3/api-docs {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
# Frontend (everything else)
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
5. CI/CD — Gitea Actions
File: .gitea/workflows/ci.yml
The CI pipeline runs on every push to main:
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: cannamanage_test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build & Test Backend
run: |
mvn clean verify -B \
-Dspring.datasource.url=jdbc:postgresql://localhost:5432/cannamanage_test \
-Dspring.datasource.username=test \
-Dspring.datasource.password=test
env:
SPRING_PROFILES_ACTIVE: test
- name: Frontend Tests
working-directory: cannamanage-frontend
run: |
npm install -g pnpm
pnpm install --frozen-lockfile
pnpm test
Pipeline Stages
| Stage | What | Fails on |
|---|---|---|
| Checkout | Clone repo | — |
| Java Setup | JDK 21 Temurin | — |
| Maven Build | Compile + unit tests + integration tests | Test failure, compilation error |
| JaCoCo | Coverage report (80% gate) | Coverage below threshold |
| Frontend | pnpm install + Vitest | Test failure |
| Deploy | docker-compose up (on main merge) | Build failure |
6. Environment Variables
File: .env.production (on TrueNAS host, NOT committed to git)
| Variable | Purpose | Example |
|---|---|---|
DB_USER |
PostgreSQL username | cannamanage |
DB_PASSWORD |
PostgreSQL password | (generated) |
JWT_SECRET |
JWT signing key (256-bit) | (generated) |
NEXTAUTH_SECRET |
NextAuth session encryption | (generated) |
STRIPE_SECRET_KEY |
Stripe API secret | sk_live_... |
STRIPE_WEBHOOK_SECRET |
Stripe webhook signing | whsec_... |
MAIL_HOST |
SMTP server | smtp.example.com |
MAIL_USERNAME |
SMTP user | noreply@cannamanage.de |
MAIL_PASSWORD |
SMTP password | (secret) |
7. Backup Strategy
#!/bin/bash
# deploy/backup.sh — runs daily via cron
DATE=$(date +%Y-%m-%d_%H%M)
BACKUP_DIR="/mnt/pool/backups/cannamanage"
# PostgreSQL dump
docker exec cannamanage-db pg_dumpall -U ${DB_USER} | gzip > "${BACKUP_DIR}/db_${DATE}.sql.gz"
# Retain last 30 days
find "${BACKUP_DIR}" -name "db_*.sql.gz" -mtime +30 -delete
| What | Frequency | Retention |
|---|---|---|
| PostgreSQL full dump | Daily (cron) | 30 days |
| ZFS snapshots | Hourly (TrueNAS) | 7 days |
| Git repository | Every push (Gitea) | Permanent |
8. Deployment Commands
Initial Setup
# Clone on TrueNAS
git clone http://truenas.local:30008/pplate/cannamanage.git /opt/cannamanage
cd /opt/cannamanage
# Create .env.production
cp .env.example .env.production
# Edit with real values...
# Start all services
docker compose -f docker-compose.truenas.yml --env-file .env.production up -d
# Verify
docker compose -f docker-compose.truenas.yml ps
curl -k https://cannamanage.plate-software.de/api/v1/health
Redeploy After Push
cd /opt/cannamanage
git pull origin main
docker compose -f docker-compose.truenas.yml --env-file .env.production up -d --build
Logs
# All services
docker compose -f docker-compose.truenas.yml logs -f
# Backend only
docker logs -f cannamanage-app
# Database
docker logs -f cannamanage-db
9. Monitoring
| Check | Method | Alert |
|---|---|---|
| Application health | GET /api/v1/health (Spring Actuator) |
HTTP 200 expected |
| Database connectivity | Docker healthcheck (pg_isready) | Container restart |
| Disk usage | ZFS pool monitoring (TrueNAS) | >80% alert |
| TLS certificate | Certbot auto-renewal (cron) | 30-day warning |
| Container status | docker compose ps |
Any "unhealthy" or "exited" |
10. Troubleshooting
| Issue | Diagnosis | Fix |
|---|---|---|
| 502 Bad Gateway | Backend not started | docker compose logs backend — check for OOM or startup errors |
| Database connection refused | DB container unhealthy | docker compose restart db — check pgdata volume |
| TLS cert expired | Certbot renewal failed | certbot renew --nginx |
| Port 443 not reachable | Router forwarding lost | Re-add port forward rule on router |
| Out of disk | ZFS pool full | docker system prune -a + check backup retention |
| Frontend 500 | NEXTAUTH_SECRET mismatch | Verify .env.production matches container env |
🌿 CannaManage
📋 Planning
🏗️ Architecture
🎨 Design
💻 Development
🌟 Product
📊 Sprint Status
| Sprint | Theme | Status |
|---|---|---|
| 1 | Domain Foundation | ✅ |
| 2 | REST API | ✅ |
| 3 | Staff & Portal | ✅ |
| 4 | Frontend MVP | ✅ |
| 5 | API Integration | ✅ |
| 6 | Production Readiness | ✅ |
| 7 | Communication | ✅ |
| 8 | Vereinsverwaltung | ✅ |
| 9 | Berichtszentrale | ✅ |
| 10 | Payment Import | ✅ |
| 11 | Test Coverage | ✅ |
| 12 | Golden Tests | ✅ |
| 13 | Prod Hardening | ✅ |
| 14 | Marketing | ✅ |
📈 Metrics
| Metric | Value |
|---|---|
| Entities | 57 |
| Controllers | 33 |
| Migrations | V1–V36 |
| Tests | 500+ |
| Coverage | 80% |