diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx index ac3e6a9888..1a868046bf 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx @@ -1,7 +1,7 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import { DocsPage } from "@/components/site/docs-page"; -import { CodeBlock, FeatureRow } from "@/components/site/primitives"; +import { Callout, CodeBlock, FeatureRow } from "@/components/site/primitives"; export const Route = createFileRoute("/docs/self-hosting-operations")({ head: () => ({ @@ -10,13 +10,13 @@ export const Route = createFileRoute("/docs/self-hosting-operations")({ { name: "description", content: - "Operate the self-hosted Gittensory review service: readiness, metrics, logs, dashboards, jobs, queues, and routine checks.", + "Operate the self-hosted Gittensory review service: readiness, metrics, logs, dashboards, jobs, queues, routine checks, and safe updates/rollback.", }, { property: "og:title", content: "Self-host operations — Gittensory docs" }, { property: "og:description", content: - "Operate the self-hosted Gittensory review service: readiness, metrics, logs, dashboards, jobs, queues, and routine checks.", + "Operate the self-hosted Gittensory review service: readiness, metrics, logs, dashboards, jobs, queues, routine checks, and safe updates/rollback.", }, { property: "og:url", content: "/docs/self-hosting-operations" }, ], @@ -275,6 +275,131 @@ volumes:
  • Backups are recent and restore-tested.
  • +

    Updating and rolling back

    +

    + Both update paths below only ever restart the gittensory app service ( + --no-deps) — they never touch other compose-profile services or their state + (Postgres, Redis, Qdrant, and Grafana's own grafana-data volume), and they + never touch .env keys other than the one they persist for next time. That means{" "} + .env, the gittensory-config/ mount, every data volume — including + the app's own /data volume where Codex/Claude Code auth material lives — + and any docker-compose.override.yml are preserved automatically across an + update. You don't need to back those up or re-supply them just to run either script, + and you only need to recreate a profile service yourself if you're deliberately + upgrading that service (its own image tag in docker-compose.yml, or a + Postgres/Redis/Qdrant major-version bump) rather than the app. +

    + +

    Path 1: pull a published image

    +

    + scripts/deploy-selfhost-image.sh pulls a tag or digest, restarts only the{" "} + gittensory service, waits for it to report healthy via{" "} + docker inspect's health status (configurable timeout, default 180s), and + then persists the resolved image reference back to GITTENSORY_IMAGE in{" "} + .env so the next plain invocation reuses it. +

    + +

    + The pull always runs with --policy always, so re-running the script against an + unchanged tag is safe: if the registry has nothing new, it just restarts the same image and + the health-check wait passes immediately. +

    + +

    Path 2: build from the current git checkout

    +

    + scripts/deploy-selfhost-prebuilt.sh is for a source-based deploy (this is how{" "} + GITTENSORY_VERSION ends up as a short git SHA instead of an image tag). It + builds the bundle inside a Dockerized Node container — the host itself never needs Node or + npm installed — then restarts only the gittensory service the same way as the + image path. +

    + +

    + SENTRY_RELEASE defaults to{" "} + gittensory-selfhost@<short git SHA of the current HEAD> unless you + override it, so each deploy from a new commit gets a distinct release id automatically. When{" "} + SENTRY_AUTH_TOKEN, SENTRY_ORG, and SENTRY_PROJECT are + all configured, the script also injects and uploads Sentry source maps for that release + before restarting the service (set SELFHOST_SKIP_SENTRY_UPLOAD=1 to skip this + even when those three are present). +

    + +

    Rollback: no dedicated command today

    +

    + There is no rollback script. Rolling back means re-running one of the two + scripts above pointed at an older target: +

    + + + This repo has no down-migration convention — scripts/check-migrations.mjs only + enforces a contiguous, non-colliding numbering, not a reverse path. If a migration has + already run forward against the live database, rolling back the app code is{" "} + not safe in general: older code can break against a newer schema (a + dropped/renamed column, a NOT NULL column it never writes, a changed constraint), even + though the migration itself succeeded. Before rolling back across a migration boundary, + check whether everything the newer migration(s) did is purely additive (new nullable column, + new table, new index) and, specifically, whether the code you're rolling back to actually + still runs against that schema — additive is usually fine; anything the old code can't + tolerate is not. Take a fresh backup first regardless — see{" "} + Backup and scaling — and if in doubt, + restore that backup to a scratch database and boot the older code against it before doing + the same on the live instance. + + +

    Before and after any update

    +

    Before updating:

    + +

    + After updating, work through the same checks as any other health pass — see{" "} + Health endpoints and Useful commands above: confirm{" "} + /ready returns 200, docker compose ps shows the service{" "} + healthy, and tail recent logs for startup errors or an unexpected absence of{" "} + selfhost_listening / selfhost_migrations_applied. +

    +

    + Neither /health nor /ready reports a version, so confirm the + deployed release directly — GITTENSORY_IMAGE or SENTRY_RELEASE in{" "} + .env records what the deploy script just resolved, and{" "} + docker inspect confirms what the running container actually has: +

    + +

    If an operating check fails, go to{" "} Self-host troubleshooting.