Dokploy deploy
Lumen is deployed to VPS jaeger via Dokploy — a self-hosted PaaS that manages Docker Compose stacks and Traefik routing.
Prod setup snapshot
- Dokploy instance:
https://dokploy.zenlabsai.com(admin-only) - Project: "Lumen" (composeId
-lHrFrWxG8415d10zZ3j0) - Repo:
codename-zen/ai-knowledge-base(master branch watched) - GitHub App:
dokploy-zenlabsai— fires redeploy on push - SSH host alias:
jaeger→89.21.85.125 - DB container:
lumen-postgres(Postgres 16 + pgvector) - Web URL:
https://ai-kb.zenmail.my.id - API URL:
https://lumen-api.zenmail.my.id - Docs URL:
https://lumen-docs.zenmail.my.id
Auto-deploy flow
git push origin master
↓ webhook
Dokploy pulls latest + docker compose build
↓
docker compose up -d with new images
↓
Traefik reloads routes
Typical deploy takes 3–6 minutes. Web rebuild is the bottleneck (Next typecheck + build).
Manual redeploy
Sometimes webhook doesn't fire, or you changed env vars and need a fresh pull. Trigger redeploy via Dokploy tRPC API:
# 1. Log in to Dokploy (once — cookie cached)
ssh jaeger "curl -s -c /tmp/dk_cookie -X POST \
http://localhost:3000/api/auth/sign-in/email \
-H 'Content-Type: application/json' \
-d '{\"email\":\"ganysigit1@gmail.com\",\"password\":\"<pw>\"}'"
# 2. Trigger compose redeploy
ssh jaeger "curl -s -b /tmp/dk_cookie -X POST \
http://localhost:3000/api/trpc/compose.redeploy \
-H 'Content-Type: application/json' \
-d '{\"json\":{\"composeId\":\"-lHrFrWxG8415d10zZ3j0\"}}'"
Expected response: {"result":{"data":{"json":{"success":true,"message":"Redeployment queued"...}}}}.
Watching a deploy
# Live container status
watch -n 5 "ssh jaeger 'docker ps --filter name=lumen --format \"{{.Names}}\\t{{.Status}}\"'"
# Build logs (last one)
ssh jaeger "ls -t /etc/dokploy/logs/compose-input-wireless-firewall-mjami6/ | head -1"
ssh jaeger "tail -f /etc/dokploy/logs/compose-input-wireless-firewall-mjami6/<filename>"
When lumen-web status flips from Up X minutes to Up X seconds, the new build is live.
Services in the compose stack
| Service | Role | Port |
|---|---|---|
| lumen-web | Next.js frontend | 3000 (internal) |
| lumen-api | Hono API + chat stream | 4000 (internal) |
| lumen-docs | Docs site (this one) | 3000 (internal) |
| lumen-postgres | DB + pgvector | 5432 |
| lumen-redis | BullMQ queue | 6379 |
| lumen-embedder | Embedding + reranker | 8000 |
| lumen-worker | Document processing queue consumer | — |
Traefik routes (auto-configured by Dokploy from labels):
ai-kb.zenmail.my.id → lumen-web:3000
lumen-api.zenmail.my.id → lumen-api:4000
lumen-docs.zenmail.my.id → lumen-docs:3000
Common gotchas
- Migration failures on deploy —
start.shin the API container runsprisma db pushon boot. Schema changes that rename enum values or drop columns with data will fail. Apply manual migrations before merging to master (see Runbooks). - Enum + same-TX usage — Postgres rejects adding a new enum value and using it in the same transaction. Always split into two migrations:
part1_enums.sql(add values) →part2_schema.sql(use them). - Embedder cold start — First deploy downloads model weights (~200MB). Container shows
unhealthyfor 2–3 minutes until first healthcheck passes. Patience. - Docker volume
upload_data— Survivesdocker compose downbut NOTdocker volume rm. Doc files live here.
Rollback
# Find the bad commit
git log --oneline -10
# Revert it (keeps history, creates new commit)
git revert <sha> --no-edit
# Push → Dokploy auto-deploys the revert
git push origin master
If the DB schema is the problem, restore from the pre-change backup:
ssh jaeger "docker exec -i lumen-postgres \
psql -U lumen -d lumen < /tmp/lumen-backup-<timestamp>.sql"
See Backup & restore.