๐ (docs): Document persistent stores and avatar-volume retrofit
Changes
3 files changed, +83 -2
MODIFY
docs/README.md
+3 -0
@@ -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
@@ -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
@@ -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.