Files
omo-bot/DEPLOYMENT.md
zwitschi 4d6b80a3b9
CI-CD / Bot Lint Test Build (push) Successful in 17s
CI-CD / Dashboard Lint Build (push) Successful in 13s
CI-CD / Deploy to Coolify (push) Failing after 3s
feat: Add detailed token requirements and troubleshooting steps for Coolify API access in deployment guide
2026-05-17 19:43:44 +02:00

8.7 KiB

Deployment Guide

This document covers production deployment for the omo-bot Coolify project with two separate resources:

  • omo-bot-backend (Node.js bot + Admin API)
  • omo-bot-dashboard (React/Vite static dashboard from admin-dashboard/)

Deployment Topology

Recommended domains:

  • api.openmicodyssey.com -> omo-bot-backend
  • admin.openmicodyssey.com -> omo-bot-dashboard

Recommended edge topology:

  • Internet -> Nginx reverse proxy -> Coolify resources
  • Nginx routes by hostname to backend/dashboard resource domains or internal ports

Optional same-origin pattern:

  • admin.openmicodyssey.com serves dashboard and reverse-proxies /admin/* + /health to backend.

1. Create Coolify Project and Resources

  1. In Coolify, create (or open) project omo-bot.
  2. Add resource omo-bot-backend from this repository:
    • Build context: repository root
    • Start command: npm start
    • Internal port: 8787 (Admin API)
  3. Add resource omo-bot-dashboard from this repository:
    • Base directory: admin-dashboard
    • Build command: npm run build
    • Publish directory: /
    • Internal port: 80 (typical static web port behind Coolify)
  4. Configure domains for each resource and enable TLS certificates.

1.1 Nginx Reverse Proxy in Front of Coolify

Use this pattern when you want one public edge server in front of Coolify-managed services.

Recommended flow:

  1. Public DNS for api.openmicodyssey.com and admin.openmicodyssey.com points to Nginx host.
  2. Nginx terminates TLS and forwards traffic to Coolify resource domains or private IP:port targets.
  3. Coolify resources stay private or restricted to internal network where possible.

Example host-based routing:

server {
  listen 80;
  server_name api.openmicodyssey.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  server_name api.openmicodyssey.com;

  ssl_certificate /etc/letsencrypt/live/api.openmicodyssey.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/api.openmicodyssey.com/privkey.pem;

  location / {
    proxy_pass http://coolify-backend.internal:8787;
    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 https;
  }
}

server {
  listen 80;
  server_name admin.openmicodyssey.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  server_name admin.openmicodyssey.com;

  ssl_certificate /etc/letsencrypt/live/admin.openmicodyssey.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/admin.openmicodyssey.com/privkey.pem;

  location / {
    proxy_pass http://coolify-dashboard.internal:80;
    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 https;
  }
}

If you prefer same-origin routing for dashboard + API through one public domain, proxy location /admin/ and location /health to backend while keeping / on dashboard.

Recommended OAuth callback route:

  • https://admin.openmicodyssey.com/oauth/discord/callback

If you use this callback path, make sure dashboard hosting serves the SPA entrypoint for that route instead of returning 404.

2. Configure Runtime Variables

Backend (omo-bot-backend) minimum:

  • DATABASE_URL
  • CONFIG_DB_ENABLED=true
  • Any temporary legacy vars needed for first-time db:seed

Dashboard (omo-bot-dashboard) minimum:

  • VITE_ADMIN_API_BASE_URL (for split-domain deployment, set to https://api.openmicodyssey.com)
  • VITE_DISCORD_CLIENT_ID
  • VITE_DISCORD_REDIRECT_URI (recommended: https://admin.openmicodyssey.com/oauth/discord/callback)

OAuth alignment:

  • Discord Developer Portal redirect URI must exactly match the dashboard callback URL.
  • Config DB key OAUTH_BRIDGE_REDIRECT_URI must match the same callback URL.
  • Dashboard env VITE_DISCORD_REDIRECT_URI must match the same callback URL.

Discord OAuth authorize URL shape:

https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=https%3A%2F%2Fadmin.openmicodyssey.com%2Foauth%2Fdiscord%2Fcallback&scope=identify%20guilds&prompt=consent

3. Get Coolify API Token and Resource UUIDs

  1. In Coolify, open Keys & Tokens -> API Tokens and create a token.
  2. Copy your Coolify base URL (example: https://coolify.yourdomain.com).
  3. Find resource UUID for:
  • omo-bot-backend
  • omo-bot-dashboard

Deploy API endpoint used by CI:

  • GET /api/v1/deploy?uuid=<resource_uuid>
  • Authorization: Bearer <COOLIFY_API_TOKEN>

3.1 Token Requirements (Important)

If CI fails with Coolify API token rejected during preflight (HTTP 403), token exists but lacks access to API target.

Use this checklist:

  1. Create token in same Coolify instance that hosts omo-bot-backend and omo-bot-dashboard resources.
  2. Ensure token owner has access to project/team containing those resources.
  3. Ensure token is API token (not UI session token/cookie).
  4. Set COOLIFY_BASE_URL to Coolify root URL only (example: https://coolify.yourdomain.com), no extra path like /project/....
  5. Rotate token if unsure, then update Gitea secret COOLIFY_API_TOKEN.

Required permissions to grant (minimum):

  • Project/resource read access for both deploy targets (omo-bot-backend, omo-bot-dashboard)
  • Deploy/trigger permission on both resources
  • Team/project membership that includes those resources (not observer/read-only)

If your Coolify token form shows these permissions:

  • root
  • write
  • deploy
  • read
  • read:sensitive

Use this selection for CI deploy token:

  • Required: read + deploy
  • Optional: write (only if your instance still blocks deploy with read+deploy)
  • Avoid for CI: root
  • Optional and sensitive: read:sensitive (only needed when debugging APIs that must return secrets/log internals)

Tip: Start with read + deploy. If preflight still returns 403, temporarily add write to verify scope mismatch, then reduce to least-privilege that still works.

Preflight check from local shell (should NOT return 401 or 403):

curl -i \
  -H "Authorization: Bearer $COOLIFY_API_TOKEN" \
  "$COOLIFY_BASE_URL/api/v1/deploy"

Resource trigger checks (match CI fallback logic):

# Variant 1: GET + Bearer
curl -i -G \
  -H "Authorization: Bearer $COOLIFY_API_TOKEN" \
  --data-urlencode "uuid=$COOLIFY_RESOURCE_UUID" \
  "$COOLIFY_BASE_URL/api/v1/deploy"

# Variant 2: POST JSON + Bearer
curl -i -X POST \
  -H "Authorization: Bearer $COOLIFY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"uuid\":\"$COOLIFY_RESOURCE_UUID\"}" \
  "$COOLIFY_BASE_URL/api/v1/deploy"

# Variant 3: GET + raw Authorization token
curl -i -G \
  -H "Authorization: $COOLIFY_API_TOKEN" \
  --data-urlencode "uuid=$COOLIFY_RESOURCE_UUID" \
  "$COOLIFY_BASE_URL/api/v1/deploy"

If all variants return 403, issue is permissions/scope in Coolify, not Gitea secret injection.

4. Configure Gitea Action Secrets/Variables

In Gitea repository settings, add Actions secrets:

  • COOLIFY_BASE_URL
  • COOLIFY_API_TOKEN
  • COOLIFY_RESOURCE_UUID_BOT
  • COOLIFY_RESOURCE_UUID_DASHBOARD

Current workflow in .gitea/workflows/ci-cd.yml triggers backend and dashboard deploys separately via the Coolify Deploy API.

5. Domain and DNS Checklist

  1. Create DNS records to Nginx edge host (or directly to Coolify if no edge proxy):
  • A record for root/subdomain targets
  • CNAME where appropriate
  1. Verify certificates issued on Nginx or Coolify, depending on TLS termination point.
  2. Verify Nginx upstreams match deployed ports:
  • backend -> 8787
  • dashboard -> 80
  1. Verify OAuth callback route resolves through dashboard hosting:
  • https://admin.openmicodyssey.com/oauth/discord/callback
  1. Verify dashboard can call API at configured VITE_ADMIN_API_BASE_URL.
  2. Verify API health endpoint over TLS:
    • GET https://api.openmicodyssey.com/health

6. Post-Deploy Verification

  1. npm run register:commands (if bot app/guild IDs changed).
  2. Confirm bot online in Discord and slash commands visible.
  3. Open dashboard URL and run OAuth login flow.
  4. Confirm Discord redirects back to https://admin.openmicodyssey.com/oauth/discord/callback without 404.
  5. Validate Admin API auth behavior with and without bearer token.

7. Coolify 403 Troubleshooting

When CI fails in Validate Coolify API access with HTTP 403:

  1. Confirm token belongs to correct Coolify instance and team/project.
  2. Confirm resource UUIDs are from same instance and still valid.
  3. Re-run local preflight curl from section 3.1.
  4. Recreate token and update Gitea secret.
  5. Re-run pipeline.

Current CI workflow already prints sanitized response snippets for deploy failures and tries auth/method fallback variants in .gitea/workflows/ci-cd.yml.