gitshark

Clone repository

git clone https://gitshark.de/git/workaround/Gitshark.git
git clone git@gitshark.de:workaround/Gitshark.git

← Commits

๐Ÿ“ (docs): Document persistent stores and avatar-volume retrofit

1b869ef28e954b26c0dbcd86d832bce72def9cae ยท Michael Hainz ยท 2026-07-09T12:09:53Z

Changes

3 files changed, +83 -2

MODIFY docs/README.md +3 -0
diff --git a/docs/README.md b/docs/README.md
index ff7a3e5..94d6bed 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -23,6 +23,9 @@
23 23 - **[Getting Started](admins/getting-started.md)** โ€” deploy git-shark with Docker
24 24 Compose, from zero to a running instance behind TLS with OIDC login and SSH git
25 25 access.
26 +- **[Persistent data](admins/persistent-data.md)** โ€” every store that must survive
27 + container recreation (database, repositories, avatars, SSH host key), what breaks
28 + when each is lost, and how to retrofit older deployments.
26 29 - **[Federation](admins/federation.md)** โ€” enable and operate ForgeFed federation:
27 30 configuration, allowlist, reverse-proxy requirements, delivery queue, monitoring.
28 31 - **[Push mirrors](admins/mirrors.md)** โ€” the secret key, outbound network and
MODIFY docs/admins/getting-started.md +6 -2
diff --git a/docs/admins/getting-started.md b/docs/admins/getting-started.md
index 848856d..dae35ba 100644
--- a/docs/admins/getting-started.md
+++ b/docs/admins/getting-started.md
@@ -193,7 +193,9 @@
193 193 - **Separate volumes for `/data/repositories`, `/data/avatars`, and `/data/ssh`** so
194 194 Docker creates each mount point with the right ownership โ€” no init container or
195 195 `mkdir` needed. The SSH host key is generated on first boot and persists across
196 - restarts (so client `known_hosts` entries stay valid).
196 + restarts (so client `known_hosts` entries stay valid). All three mounts (plus the
197 + database) are mandatory for a stateful deployment โ€” see
198 + [Persistent data](persistent-data.md) for what each holds and what breaks without it.
197 199 - **HTTP port 8080 is not published** โ€” it's reached through the reverse proxy on the
198 200 Compose network (Step 6). Only SSH (2222) is exposed directly.
199 201 - **Single app replica.** git-shark keeps git state on a `ReadWriteOnce`-style filesystem
@@ -337,7 +339,8 @@
337 339
338 340 ## Operations
339 341
340 -**Backups** โ€” three things hold state:
342 +**Backups** โ€” three things hold state (full inventory, including what breaks when each
343 +is lost, in [Persistent data](persistent-data.md)):
341 344 - The `db-data` volume (metadata: users, repo records, issues, MRs, comments;
342 345 also each avatar's content type and update timestamp โ€” the bytes are not
343 346 here).
@@ -381,5 +384,6 @@
381 384 | Boot fails on OIDC discovery | `QUARKUS_OIDC_AUTH_SERVER_URL` wrong/unreachable, or IdP demands HTTPS the app can't reach. |
382 385 | App exits complaining about secret length | `*_STATE_SECRET` shorter than 32 chars. Regenerate with `openssl rand -hex 16`. |
383 386 | SSH host key changed after redeploy | The `ssh` volume wasn't persisted โ€” confirm it's a named volume, not a throwaway mount. |
387 +| Profile pictures disappear after redeploy / render as broken images | The `avatars` volume wasn't mounted, so uploads landed in the container layer. Add the volume and `GITSHARK_AVATAR_ROOT` as in Step 5 โ€” retrofit steps in [Persistent data](persistent-data.md#upgrading-a-deployment-created-before-profile-pictures). |
384 388 | `git push` over HTTP rejected | Use a personal access token (from *Access tokens*) as the Basic-auth password, not your OIDC password. |
385 389 | Schema validation error at start | DB not empty / migrated by a different tool. git-shark's Flyway owns the schema; start from an empty database. |
ADD docs/admins/persistent-data.md +74 -0
diff --git a/docs/admins/persistent-data.md b/docs/admins/persistent-data.md
new file mode 100644
index 0000000..88346a1
--- /dev/null
+++ b/docs/admins/persistent-data.md
@@ -0,0 +1,74 @@
1 +# Persistent data: what your deployment must store
2 +
3 +git-shark keeps state in **one database and three filesystem locations**. Every one of
4 +them must live on a persistent volume โ€” anything written to the container's own
5 +filesystem is lost when the container is recreated (every `docker compose up` after an
6 +image update, config change, or host reboot).
7 +
8 +Use this page as the checklist when setting up volumes, writing backup jobs, or
9 +migrating a deployment to a new host.
10 +
11 +## The four stores
12 +
13 +| Store | Configured by | Default (in container) | Contents | If you lose it |
14 +|---|---|---|---|---|
15 +| PostgreSQL | `QUARKUS_DATASOURCE_*` | external service | Users, repository records, issues, merge requests, comments, SSH public keys, access-token hashes, push-mirror and federation state. Also each avatar's content type and update timestamp โ€” but **not** the image bytes. | Everything except the raw git objects and images. Total loss. |
16 +| Repositories | `GITSHARK_STORAGE_ROOT` | `data/repositories` | The bare git repositories (all commits, branches, tags). | All hosted code. The DB rows survive but point at nothing. |
17 +| Avatars | `GITSHARK_AVATAR_ROOT` | `data/avatars` | Uploaded profile pictures, one file per user, named by user UUID. | Profile pictures render as broken images (the DB still says the user has one, but `GET /users/{username}/avatar` returns 404). Users must re-upload. |
18 +| SSH host key | `GITSHARK_SSH_HOST_KEY` | `data/ssh/host-key` | The server's SSH host key, generated on first boot. | A new key is generated; every git client sees a host-key-changed warning and refuses to connect until `known_hosts` is fixed. |
19 +
20 +If you front git-shark with Caddy as in the [Getting Started](getting-started.md) guide,
21 +also persist Caddy's `/data` volume (`caddy-data`) โ€” it holds the TLS certificates and
22 +Let's Encrypt account.
23 +
24 +In the reference Compose file all of these are named volumes: `db-data`, `repos`,
25 +`avatars`, and `ssh`. The defaults above are *relative* paths โ€” inside a container they
26 +resolve to a directory that vanishes with the container, so production deployments must
27 +set the `GITSHARK_*` variables to absolute paths on mounted volumes, exactly as the
28 +reference Compose file does.
29 +
30 +## Checking a running deployment
31 +
32 +```bash
33 +docker compose exec app sh -c 'ls /data/repositories /data/avatars /data/ssh'
34 +docker inspect --format '{{range .Mounts}}{{.Destination}} <- {{.Source}}{{println}}{{end}}' \
35 + "$(docker compose ps -q app)"
36 +```
37 +
38 +Every path from the table must appear as a mount backed by a named volume (or a host
39 +path) โ€” not by the container's writable layer.
40 +
41 +## Upgrading a deployment created before profile pictures
42 +
43 +Deployments set up from a Getting Started guide older than the avatar feature have no
44 +`avatars` volume, so uploaded profile pictures land in the container layer and disappear
45 +on the next `docker compose up`. The symptom: avatars work until the app container is
46 +recreated, then render as broken images.
47 +
48 +Fix by aligning with the current reference Compose file:
49 +
50 +1. Add the environment variable to the `app` service:
51 +
52 + ```yaml
53 + GITSHARK_AVATAR_ROOT: /data/avatars
54 + ```
55 +
56 +2. Add the volume mount to the `app` service and the named volume to the top-level
57 + `volumes:` block:
58 +
59 + ```yaml
60 + volumes:
61 + - avatars:/data/avatars
62 + ```
63 +
64 +3. Recreate the app: `docker compose up -d app`.
65 +
66 +Pictures uploaded before the fix are gone (they lived in the discarded container
67 +layer); affected users re-upload under *Settings โ†’ Profile*, or remove the picture
68 +there to fall back to the initials badge.
69 +
70 +## Backups
71 +
72 +Back up all four stores together and consistently โ€” a DB dump that references git
73 +objects or avatar files from a different point in time is only crash-consistent. See
74 +[Getting Started โ†’ Operations](getting-started.md#operations) for the commands.

Keyboard shortcuts

?Show this help
g hGo home
EscClose dialog