feat(deploy): Caddy HTTPS reverse proxy + live VPS bring-up + usage guide - #14
Conversation
- Caddy reverse proxy (deploy/caddy/Caddyfile): single-origin HTTPS, /api/* -> api, everything else -> frontend; automatic Let's Encrypt cert via <ip>.sslip.io (no domain). - Fix prometheus/grafana localhost-only ports with compose !override (base + override port lists concatenate by default, causing a double-bind -> address-already-in-use that left 4 services stuck in Created). - gitignore the box-specific docker-compose.vps.yml (carries the public host/IP); commit a docker-compose.vps.yml.example template instead, mirroring the .env.prod convention. - docs/USAGE.md operating guide; record the live deploy in PROGRESS + implementation-notes; correct the deploy-checklist briefing cron to use -p second-brain + both compose files. - scrub the public IP from all committed docs (YOUR_VPS_IP placeholder). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR adds a Caddy HTTPS reverse-proxy, a VPS-specific docker-compose override example, updates compose invocation comments/env examples, and expands operational documentation and runbooks for running the full stack on a single VPS. ChangesVPS Live Deployment: Caddy HTTPS, Docker Compose Override, and Operational Guide
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/runbooks/deploy-checklist.md`:
- Around line 10-11: The runbook's deploy command contract is inconsistent:
update the command blocks in Steps 4, 5, and 8 to use the canonical compose
invocation shown in the diff (include -p second-brain, both compose files
deploy/docker-compose.prod.yml and deploy/docker-compose.vps.yml, and --env-file
deploy/.env.prod) so they match the new contract and ensure VPS overrides are
applied; scan the document for any other occurrences of docker-compose.prod.yml
alone and replace them with the full canonical command to avoid recreating the
duplicate-project issue.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6f0b88c7-dca1-4fc3-b9a6-f2f2b6c71717
⛔ Files ignored due to path filters (3)
docs/screenshots/ui-chat-answer.pngis excluded by!**/*.pngdocs/screenshots/ui-chat.pngis excluded by!**/*.pngdocs/screenshots/ui-home.pngis excluded by!**/*.png
📒 Files selected for processing (8)
.gitignoredeploy/caddy/Caddyfiledeploy/docker-compose.vps.yml.exampledocs/PROGRESS.mddocs/USAGE.mddocs/adr/0011-vps-provider.mddocs/implementation-notes.mddocs/runbooks/deploy-checklist.md
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
deploy/docker-compose.vps.yml.example (1)
51-52:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winCaddyfile volume mount path is incorrect for the documented invocation.
The volume mount
./caddy/Caddyfileis relative to the working directory. According to the header comment (line 5) and the deploy-checklist runbook, docker compose is invoked from the repository root with-f deploy/docker-compose.prod.yml -f deploy/docker-compose.vps.yml. From that context,./caddy/Caddyfilewould resolve to<repo-root>/caddy/Caddyfile, but the Caddyfile is actually located atdeploy/caddy/Caddyfile(per the review stack context).🔧 Proposed fix
volumes: - - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@deploy/docker-compose.vps.yml.example` around lines 51 - 52, The Caddyfile volume mount in deploy/docker-compose.vps.yml.example is wrong for the documented invocation: replace the relative path string "./caddy/Caddyfile" with the repository-root-correct path "./deploy/caddy/Caddyfile" so the compose stack (when run with -f deploy/docker-compose.prod.yml -f deploy/docker-compose.vps.yml) mounts the actual Caddyfile; keep the existing :ro flag intact.
🧹 Nitpick comments (1)
docs/runbooks/deploy-checklist.md (1)
81-81: 💤 Low valueConsider extracting the cron command to a wrapper script for maintainability.
The cron command is ~280 characters long and hardcodes the full docker compose invocation with both files, project name, and env file. While this works, any changes to the compose invocation pattern would require updating this cron line.
An alternative would be a small wrapper script
/usr/local/bin/second-brain-enqueue-briefingthat contains the docker compose command, making the cron line much shorter and the command easier to maintain.However, the current approach is explicit and doesn't hide complexity, which is valuable for operational transparency. This is a trade-off decision.
Alternative wrapper script approach
Create
/usr/local/bin/second-brain-enqueue-briefing:#!/bin/bash set -euo pipefail cd /root/second-brain docker compose -p second-brain \ -f deploy/docker-compose.prod.yml \ -f deploy/docker-compose.vps.yml \ --env-file deploy/.env.prod \ exec -T worker python -m app.jobs.enqueue briefingThen simplify the cron line:
0 7 * * * root /usr/local/bin/second-brain-enqueue-briefing >> /var/log/second-brain-briefing.log 2>&1🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/runbooks/deploy-checklist.md` at line 81, Extract the long cron docker-compose invocation into a wrapper script named second-brain-enqueue-briefing: create an executable script that sets strict shell options (e.g., set -euo pipefail), cds to the repository, runs the same docker compose invocation currently in the cron line (including the project name, both compose files, env-file and the exec -T worker python -m app.jobs.enqueue briefing invocation), and then replace the cron entry with a shorter call to second-brain-enqueue-briefing that redirects stdout/stderr to the existing log file; ensure the script is executable and owned appropriately.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@deploy/docker-compose.vps.yml.example`:
- Around line 51-52: The Caddyfile volume mount in
deploy/docker-compose.vps.yml.example is wrong for the documented invocation:
replace the relative path string "./caddy/Caddyfile" with the
repository-root-correct path "./deploy/caddy/Caddyfile" so the compose stack
(when run with -f deploy/docker-compose.prod.yml -f
deploy/docker-compose.vps.yml) mounts the actual Caddyfile; keep the existing
:ro flag intact.
---
Nitpick comments:
In `@docs/runbooks/deploy-checklist.md`:
- Line 81: Extract the long cron docker-compose invocation into a wrapper script
named second-brain-enqueue-briefing: create an executable script that sets
strict shell options (e.g., set -euo pipefail), cds to the repository, runs the
same docker compose invocation currently in the cron line (including the project
name, both compose files, env-file and the exec -T worker python -m
app.jobs.enqueue briefing invocation), and then replace the cron entry with a
shorter call to second-brain-enqueue-briefing that redirects stdout/stderr to
the existing log file; ensure the script is executable and owned appropriately.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 95a34e96-77b8-4ac2-981f-875f880ad2f5
📒 Files selected for processing (9)
deploy/.env.prod.exampledeploy/docker-compose.prod.ymldeploy/docker-compose.vps.yml.exampledocs/PROGRESS.mddocs/USAGE.mddocs/implementation-notes.mddocs/runbooks/backup-restore.mddocs/runbooks/deploy-checklist.mddocs/runbooks/incident-response.md
✅ Files skipped from review due to trivial changes (4)
- deploy/docker-compose.prod.yml
- deploy/.env.prod.example
- docs/PROGRESS.md
- docs/USAGE.md
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/implementation-notes.md
- status badges -> live / 7-of-7 / Caddy auto-HTTPS / eval-gated CI - hero screenshot + live callout; new "What's New" (PRs #12/#13/#14/#16) - tech stack: gemini-2.5-flash, dual embedding providers (both 384-dim), Caddy reverse proxy, PgBouncer - production architecture diagram (9 services); reworked quickstart; new "Deploy to a VPS" -> docs/USAGE.md; updated layout + ADR links Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Brings the production stack fully live on the single VPS and adds an operating guide.
deploy/caddy/Caddyfile): single-origin HTTPS —/api/*→ api, everything else → frontend; automatic Let's Encrypt cert via an<ip>.sslip.iohost (no domain needed). HTTP→HTTPS redirect.127.0.0.1binding concatenated with the base0.0.0.0binding (compose merges sequences), double-binding the port → "address already in use" left 4 services stuck inCreated. Fixed with the compose!overridetag (localhost-only).docker-compose.vps.yml(carries the public host) is now git-ignored; adocker-compose.vps.yml.exampletemplate is committed instead (mirrors the.env.prodconvention).docs/USAGE.mdoperating guide; live deploy recorded inPROGRESS.md+implementation-notes.md;deploy-checklist.mdbriefing cron corrected to use-p second-brain+ both compose files.YOUR_VPS_IPplaceholder).Verified
All 9 services up; end-to-end over HTTPS with a valid LE cert:
/api/health, ingest→embed (Gemini)→search→citedgemini-2.5-flashchat, and a generated daily briefing.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests
Chores