Upgrading

GitBlixt upgrades are designed to be safe and fast. Database migrations run automatically on startup.

Standard Upgrade

    docker service update --image willc0de4food/gitblixt:latest gitblixt

Swarm starts the new task alongside the old one and switches over once it is healthy. The database keeps running. Migrations run automatically on startup.

Upgrading to a Specific Version

docker service update --image willc0de4food/gitblixt:1.2.0 gitblixt

Before Upgrading

  1. Read the release notes for the version you're upgrading to — especially any breaking changes or manual migration steps.
  2. Take a backup before every upgrade. See Backup & Restore.

Upgrade notes

Upgrading to this release changes a few things existing installs rely on:

  • Protect your deploy branch. The trusted CI repositories under Admin → Settings (by default repositories named gitblixt owned by root) get the instance's secrets and the Docker socket only for a maintainer's push to a protected branch. Protect the branch your self-deploy pipeline runs on under the repository's Settings → Protected branches, or its pipelines run without that access. The card warns about each trusted repository whose default branch isn't protected, and only root can change the list. Commits an AI job or a pull mirror pushed never get that access; auto-mode instance updates are pushed as the root who owns the local GitBlixt repository.
  • Setup token. When the service has a GITBLIXT_SETUP_TOKEN, the first registration on an empty instance must present it.
  • Approvals go stale once. An approval now counts only for the merge request's head commit and target branch it was given on. Approvals given before the upgrade record neither, so open merge requests need approving again.
  • Token-authenticated mirrors. Mirrors that signed in with a stored personal access token stop syncing and show an error; recreate them with HTTP basic auth or an OAuth connection.
  • GitBlixt's proxies. GitBlixt takes the client IP from X-Forwarded-For only from loopback and the hosts listed under Admin → Infrastructure → Caddy → GitBlixt's proxies (Caddy's service names by default). If your reverse proxy runs under another name, root adds it there, or login throttling, rate limits and the audit log see the proxy's address.
  • /metrics needs an admin token. It used to answer anyone; it now answers 401 without a token and 403 for a non-admin. Give your scraper an admin's personal access token (read_api is enough) in an Authorization: Bearer or PRIVATE-TOKEN header.
  • No PUBLIC connect on GitBlixt's database. At every start GitBlixt revokes PUBLIC's CONNECT on its own database, so the roles it provisions for apps can't open it. A role of your own that reads GitBlixt's database (a reporting or backup user) needs GRANT CONNECT ON DATABASE <name> TO <role>.
  • deploy: job names are reserved. A CI job whose name starts with deploy: is now rejected: GitBlixt names its own deploy jobs that way. Rename such jobs in your CI configuration.
  • Checkouts of other repositories on pull mirrors. On a pull-mirrored repository, a GitHub Actions actions/checkout of another repository is no longer cloned from this instance unless the user recorded as pushing the commit can read that repository. Give the step a token: or ssh-key: to clone it from GitHub instead.
  • AI auto-merges and fork syncs run untrusted CI. Commits an AI auto-merge landed or a fork sync brought in run their pipelines without the trusted CI repositories' secrets and Docker socket, even on a trusted repository's protected branch, so a self-deploy of GitBlixt doesn't run from them. A maintainer's own push or merge to the protected branch deploys as before.

Rollback

If something goes wrong after an upgrade:

  1. Stop the current container
  2. Start the previous image version (you'll need the old image tag)
  3. Restore your database from the pre-upgrade backup

The PostGIS and pgvector Postgres Image

The postgres service in docker-compose.yml runs willc0de4food/gitblixt-postgres, Postgres 18 with PostGIS and pgvector added (built from postgis/postgis:18-3.6-alpine), so managed databases can enable them. It is the same major version, so the data directory carries over without a dump and restore. The service mounts priv/postgres/10_postgis.sh, which keeps the image from installing PostGIS into GitBlixt's own database, and Swarm won't start the service while that file is missing: put it on the host (it is served at /10_postgis.sh) before running docker stack deploy with the new docker-compose.yml. Re-running install.sh fetches both.

An install whose Postgres runs a Postgres 18 Alpine image without pgvector gets it with one click: Admin → Infrastructure → Switch to the pgvector image (Docker Swarm only; Postgres restarts for a few seconds and keeps its data). Then enable vector per database from the environment's Database page; the app's own database user can't create it. GitBlixt's updates never redeploy the Postgres service, so nothing switches it back, unless you redeploy the stack by hand from a docker-compose.yml older than this image (fetch the current one with install.sh first). Postgres and PostGIS patch releases arrive with new GitBlixt releases of this image.

Caddy's Delay Before Closing Websockets

Every route change GitBlixt makes reloads Caddy's config, which closes the websockets (LiveView included) of every site it proxies. GitBlixt's routes for deployed apps set stream_close_delay to 5 minutes, which defers that close: a socket that ends on its own meanwhile isn't cut, but the rest are still closed together when the delay runs out. Caddy runs with --resume, so an existing install keeps the config it already has, and an edited Caddyfile alone changes nothing: GitBlixt's own route has the delay only on a new install, or once Caddy's stored config is rebuilt from the shipped Caddyfile.

Upgrading the Bundled Postgres (major version)

New installs run Postgres 18. A major version bump (e.g. 16 → 18) is NOT a plain image update — Postgres refuses to start on a data directory created by an older major version. You must dump on the old version and restore into a fresh new one. (Minor updates within the same major are safe.)

Postgres runs as the gitblixt_postgres service of the gitblixt Swarm stack (check with docker service ls --filter label=com.docker.stack.namespace=gitblixt), so it is managed with docker service and docker stack deploy, not docker compose.

docker stack deploy applies exactly the docker-compose.yml on disk and reverts anything changed live with docker service update. Before deploying, diff /opt/gitblixt/docker-compose.yml against the current one — a stale copy loses the PGDATA pin (Postgres then initializes an empty cluster outside your data dir) and the POSTGRES_CPUS/POSTGRES_MEMORY_LIMIT settings. It also resets a Postgres memory limit set in Admin → Infrastructure to POSTGRES_MEMORY_LIMIT, so set that to match first, and a /dev/shm size set there to the file's 1 GiB (the shared_buffers and max_connections saved there are kept).

Take a backup first, then, on the host where GitBlixt is installed:

    cd /opt/gitblixt
set -a; . ./.env; set +a    # docker stack deploy doesn't read .env itself
pg() { docker ps -q -f label=com.docker.swarm.service.name=gitblixt_postgres; }

# 1. stop GitBlixt AND every managed app, so nothing writes during the dump.
#    Record replica counts first so step 7 can restore them.
docker service scale gitblixt=0
docker service ls --filter label=gitblixt.managed=true --format '{{.Name}} {{.Replicas}}' > app-services.txt
docker ps -q --filter label=gitblixt.managed=true > app-containers.txt
cut -d' ' -f1 app-services.txt | xargs -r -I{} docker service scale -d {}=0
xargs -r docker stop < app-containers.txt

# 2. dump ALL databases + roles with the OLD server's own pg_dumpall.
#    Managed app databases and their roles live in this same cluster, so a
#    single-database pg_dump would silently leave them behind.
docker exec "$(pg)" pg_dumpall -U gitblixt > /opt/gitblixt/pg-upgrade.sql

# 3. stop postgres and set its data dir aside (don't delete yet)
docker service scale gitblixt_postgres=0
sudo mv /opt/gitblixt/data/postgres /opt/gitblixt/data/postgres-old.bak

# 4. bump the postgres image tag in docker-compose.yml, then redeploy the
#    stack — the new major initializes a fresh cluster in data/postgres
docker stack deploy -c docker-compose.yml gitblixt
until docker exec "$(pg)" pg_isready -U gitblixt; do sleep 2; done

# 5. restore every database + role, then rebuild planner statistics
docker exec -i "$(pg)" psql -U gitblixt -d postgres < /opt/gitblixt/pg-upgrade.sql
docker exec "$(pg)" vacuumdb -U gitblixt --all --analyze-in-stages

# 6. tell GitBlixt the new major is intended (see Boot guard below), then
#    bring it back; migrations run on boot
echo 18 | sudo tee /opt/gitblixt/data/config/postgres_major
docker service scale gitblixt=1
docker service ps gitblixt

# 7. bring the managed apps back at their recorded replica counts
while read -r name replicas; do docker service scale -d "$name=${replicas#*/}"; done < app-services.txt
xargs -r docker start < app-containers.txt

Verify the app and a few managed apps work on the new version, then clean up: shred -u /opt/gitblixt/pg-upgrade.sql and sudo rm -rf /opt/gitblixt/data/postgres-old.bak. Keep the .bak until you're confident — it's your rollback.

Boot guard

Before migrating, GitBlixt checks the database it connected to and refuses to start when Postgres reports a different major version than the one it last migrated (recorded in /data/config/postgres_major, or pinned with the POSTGRES_MAJOR env var), or when the database has no GitBlixt schema while /data/repos already holds repositories — the signature of a freshly initialized cluster mounted in place of the real one. Fix the mount or restore the data; set GITBLIXT_SKIP_DB_GUARD=true on the gitblixt service only if you are certain the database is right.