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
- Read the release notes for the version you're upgrading to — especially any breaking changes or manual migration steps.
- 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
gitblixtowned 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-Foronly 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. -
/metricsneeds 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_apiis enough) in anAuthorization: BearerorPRIVATE-TOKENheader. -
No PUBLIC connect on GitBlixt's database.
At every start GitBlixt revokes PUBLIC's
CONNECTon 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) needsGRANT CONNECT ON DATABASE <name> TO <role>. -
deploy:job names are reserved. A CI job whose name starts withdeploy: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/checkoutof another repository is no longer cloned from this instance unless the user recorded as pushing the commit can read that repository. Give the step atoken:orssh-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:
- Stop the current container
- Start the previous image version (you'll need the old image tag)
- 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.