Development vs production¶
Jellybot uses a two-tree layout on the operator host:
| Path | Role |
|---|---|
~/coding/jellybot-dev |
Git checkout — edit code, run tests, make dev-refresh with local image build |
~/docker/jellybot |
Production deploy — GHCR image only, Watchtower, persistent data |
The GitHub repo stays introVRt-Lounge/jellybot. The -dev suffix is local only so prod compose never shares a working directory with bind-mounted source.
Development (~/coding/jellybot-dev)¶
git clone https://github.com/introVRt-Lounge/jellybot.git ~/coding/jellybot-dev
cd ~/coding/jellybot-dev
cp .env.example .env
bun install
make test
make dev-refresh # builds jellybot:runtime locally; container name jellybot-dev optional
make health
Optional docker-compose.override.yml (gitignored) for host-specific mounts:
- Subtitle DB:
JELLYBOT_DATA_HOST_DIR=/home/heavygee/docker/jellybot/data(shared with prod data) - Jellyfin on Docker network:
JELLYFIN_URL=http://jellyfin:8096,traefik_netexternal network - Clips: SMB or local bind under
/var/lib/jellybot/clips
Do not run a long-lived jellybot container from the dev checkout in production. Use make test / short-lived compose profiles for parity checks.
Live smoke (dev bot + real Jellyfin)¶
Self-hosted runner gate: SMOKE.md (make smoke-ci). In-container checks exercise Jellyfin, subtitle index, and handler logic; host-side checks exercise Discord gateway via user token.
Legacy single-command Discord smokes: Discord smoke testing.
Production (~/docker/jellybot)¶
Only one container named jellybot should run on the host.
cd ~/docker/jellybot
docker compose pull
docker compose up -d --force-recreate
curl -fsS http://127.0.0.1:8080/healthz | jq .
Compose uses:
image: ghcr.io/introvrt-lounge/jellybot:latestcom.centurylinklabs.watchtower.enable=truecom.centurylinklabs.watchtower.scope=minutely- Bind mounts:
./data(subtitle index + bot state), clips path on host .envwith secrets (never commit)
Automagic deploy chain¶
- Conventional commit merged to
main - CI builds and pushes GHCR (
:main,:sha-*;:lateston major/minor release tags) - Watchtower (minutely scope) recreates
jellybotwhen:latestdigest changes - Bot
ClientReadyone-shot checks GitHub Releases and announces major/minor to the configured announcements channel (NOTIFICATION_CHANNEL_ID)
Patch releases update GitHub but do not move :latest — silent until the next major/minor Watchtower cycle.
First-time prod bootstrap¶
- Create
~/docker/jellybot/data(uid 1001 for SQLite WAL) - Copy
.envfrom dev checkout; addGITHUB_TOKEN,NOTIFICATION_CHANNEL_ID - Copy
deploy/prod/docker-compose.host.example.ymlto~/docker/jellybot/docker-compose.ymland adjust paths docker login ghcr.io(Watchtower uses host credentials for private packages)- Stop any dev-checkout
jellybotcontainer before starting prod
Rollback¶
Pin image in ~/docker/jellybot/docker-compose.yml to a semver or sha-* tag, then:
docker compose pull && docker compose up -d --force-recreate
Restore :latest when ready for Watchtower again.