VM Auto-Deploy (Stable + Idempotent)
This runbook documents the canonical VM layout, deploy flow, rollback behavior, and bootstrap steps.
Canonical VM layout
/opt/lucille/
src/ # git checkout
env/ # backend.env + worker.env (secrets/config)
run/ # SAFE_COMMIT, deploy.lock
logs/ # deploy.log
venv/
backend/ # uv venv for API
worker/ # uv venv for worker
deploy.env # optional deploy config
Deploy flow (what happens on each deploy)
git fetch origin main- Hard reset to origin/main:
git checkout -B main origin/maingit reset --hard origin/maingit clean -fd
- Ensure clean tree (abort if dirty)
- Sync dependencies into
/opt/lucille/venv/* - Run migrations
- Optional smoke test (
DEPLOY_RUN_SMOKE=true) - Stamp
APP_VERSIONwithUTC_TIMESTAMP+SHORT_SHA - Restart
lucille-backendandlucille-worker - Write
SAFE_COMMITonly after success
Rollback:
- On failure, checkout
SAFE_COMMIT, clean tree, restart services.
Deployment config
/opt/lucille/deploy.env (optional):
ENV_BACKEND_FILE=/opt/lucille/env/backend.env
ENV_WORKER_FILE=/opt/lucille/env/worker.env
DEPLOY_RUN_SMOKE=false
REPO_ROOT=/opt/lucille/src
Bootstrap a fresh VM
sudo apt-get update
sudo apt-get install -y git python3 python3-venv curl
curl -Ls https://astral.sh/uv/install.sh | sh
source ~/.profile
sudo useradd --system --create-home --shell /bin/bash lucille
sudo mkdir -p /opt/lucille
sudo chown -R lucille:lucille /opt/lucille
sudo -u lucille git clone <YOUR_REPO_URL> /opt/lucille/src
cd /opt/lucille/src/backend
uv sync --dev
cd /opt/lucille/src/worker
uv sync
sudo bash /opt/lucille/src/scripts/install_vm_autodeploy.sh
Populate env files:
/opt/lucille/env/backend.env/opt/lucille/env/worker.env
Then restart:
sudo systemctl restart lucille-backend lucille-worker
Operational checks
- Deploy log:
tail -f /opt/lucille/logs/deploy.log - Timer status:
systemctl status lucille-deploy.timer --no-pager -l - Current code:
git -C /opt/lucille/src log -1 --onelinegit -C /opt/lucille/src rev-parse origin/main
- Health:
curl -s http://127.0.0.1:8000/health
Common failure modes + fixes
- Repo dirty:
- Check
git statusunder/opt/lucille/srcand remove untracked files.
- Check
- Permission errors in
.venv:- Use external venvs at
/opt/lucille/venv/*owned bylucille.
- Use external venvs at
- Deploy loops:
- Ensure
SAFE_COMMITmatchesorigin/main:cat /opt/lucille/run/SAFE_COMMITand compare togit rev-parse origin/main.
- Ensure
Webhook (optional)
- Configure GitHub webhook to
http://<vm-ip>:9009/webhook/github. - Set
LUCILLE_WEBHOOK_SECRETin/opt/lucille/env/backend.env. - Enable:
sudo systemctl enable --now lucille-deployhook