# DEPLOY — dev → prod promotion workflow > Strict order. Skip a step → break prod. ## Pre-flight - [ ] testing/SMOKE-TEST.md passed on dev - [ ] You can name the change in one sentence - [ ] You have a rollback target (the current prod md5 — capture before deploy) ## Step 1 — capture current prod md5 (rollback anchor) ```bash PROD_BEFORE=$(ssh user@nullstone 'md5sum /opt/docker/jellyfin/web-overrides/index.html' | awk '{print $1}') echo "rollback anchor: $PROD_BEFORE" ``` If anything goes wrong: `cp /opt/docker/jellyfin/web-overrides/index.html.bak. /opt/docker/jellyfin/web-overrides/index.html` + restart. ## Step 2 — backup prod overlay + branding ```bash ssh user@nullstone 'set -e TS=$(date +%s) docker run --rm --userns=host -v /opt/docker/jellyfin/web-overrides:/d:rw alpine cp /d/index.html /d/index.html.bak.deploy.$TS docker run --rm --userns=host -v /home/docker/jellyfin/config/config:/d:rw alpine cp /d/branding.xml /d/branding.xml.bak.deploy.$TS ' ``` ## Step 3 — copy dev overlay to prod via docker shim (root-owned dest) ```bash ssh user@nullstone 'docker run --rm --userns=host -v /opt/docker/jellyfin-dev/web-overrides:/dev:ro -v /opt/docker/jellyfin/web-overrides:/prod:rw alpine sh -c "cp /dev/index-dev.html /prod/index.html && chown root:root /prod/index.html && md5sum /prod/index.html"' ``` ## Step 4 — bind-mount inode swap requires restart ```bash ssh user@nullstone 'docker restart jellyfin && sleep 14' ``` ## Step 5 — verify ```bash ssh user@nullstone 'docker exec jellyfin md5sum /jellyfin/jellyfin-web/index.html' # should match dev's md5 ssh user@nullstone 'docker exec jellyfin curl -s http://127.0.0.1:8096/web/index.html | grep -c ARRFLIX-MIDDLE-THEME-BEGIN' # = 1 ssh user@nullstone 'docker exec jellyfin curl -s http://127.0.0.1:8096/Branding/Css.css | wc -c' # ~36000 ssh user@nullstone 'docker ps --format "{{.Names}} {{.Status}}" | grep "^jellyfin "' # healthy ``` ## Step 6 — manual smoke on prod Open arrflix.s8n.ru in incognito. Run testing/SMOKE-TEST.md manual checklist. ## Step 7 — commit + push to repo ```bash cd /tmp/arrflix-recon cp web-overrides/index.html snapshots/2026-05-09-v6-stable/index.html # update snapshot git add bin/inject-middle-theme.py web-overrides/index.html snapshots/2026-05-09-v6-stable/index.html docs/ # whatever changed git -c user.name=s8n -c user.email=admin@s8n.ru commit -m "" git push origin main ``` ## If verify fails Run `testing/ROLLBACK.md` immediately. Don't try to fix forward on prod. ## Common deploy gotchas - Forget `docker restart jellyfin` after `cp` → bind-mount inode swap → container serves stale - Forget `chown root:root` → user can't write but root needs to own per host config - Forget snapshot bump → next "what's deployed" question gets wrong answer - Forget commit → repo drifts from prod (per doc 26 INC1 root cause)