summaryrefslogtreecommitdiff
path: root/f3s/forgejo/README.md
blob: ecc35a62357a7d7dff0c4b9573b7a37e4f5625b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# Forgejo

Self-hosted git forge at `https://code.f3s.buetow.org`, running in the `services`
namespace of the f3s k3s cluster.

## Relationship to the cgit git-server

**These two installs are deliberately independent.** Nothing here touches
`f3s/git-server/`:

| | cgit / git-server | Forgejo |
|---|---|---|
| Web UI | `c-git.f3s.buetow.org` | `code.f3s.buetow.org` |
| Namespace | `cicd` | `services` |
| Repo storage | `/data/nfs/k3svolumes/git-server/repos` (80 bare repos) | `/data/nfs/k3svolumes/forgejo/data` (starts empty) |
| SSH NodePort | 30022 | 30222 |

ArgoCD keeps reading `conf.git` from the existing git-server
(`http://git-server.cicd.svc.cluster.local/conf.git`). Forgejo has no consumers,
so if it breaks, cluster deploys are unaffected. Repositories are migrated by
hand, one at a time, whenever you feel like it — there is no bulk import.

## Architecture

```
Internet -> relayd (OpenBSD GW, TLS) -> WireGuard -> Traefik -> forgejo svc:80 -> pod:3000
git+ssh  -> NodePort 30222 -----------------------------------> pod:2222
                                                                  |
                                        /var/lib/gitea (repos, SQLite)  NFS -> ZFS
                                        /etc/gitea     (app.ini, secrets)
```

- Image `codeberg.org/forgejo/forgejo:16.0.1-rootless` — the rootless variant, so
  the whole pod runs as UID/GID 1000 with all capabilities dropped.
- SQLite, not PostgreSQL. Single writer (`replicas: 1` + `Recreate`), and NFSv4.2
  does real byte-range locking, so the classic SQLite-on-NFS corruption mode does
  not apply. Revisit if this ever needs to scale out.
- Both volumes carry the standard `.nfs-sentinel` guard so the pod refuses to
  start against the local-XFS shadow if a node has NFS unmounted.

## Initial setup

### 1. Create the storage directories

On the current CARP storage master (check with `ifconfig | grep MASTER` on f0/f1):

```sh
doas mkdir -p /data/nfs/k3svolumes/forgejo/data /data/nfs/k3svolumes/forgejo/config
doas touch    /data/nfs/k3svolumes/forgejo/data/.nfs-sentinel \
              /data/nfs/k3svolumes/forgejo/config/.nfs-sentinel
doas chown -R 1000:1000 /data/nfs/k3svolumes/forgejo
doas chmod -R 0750      /data/nfs/k3svolumes/forgejo
```

The PVs use `type: Directory`, so the pod will not schedule until these exist.

### 2. Publish the hostname

`code.f3s.buetow.org` must be added to `@f3s_hosts` in `frontends/Rexfile`. That
one array drives the DNS zone, the relayd routing rule, the ACME certificate and
the gogios monitoring checks.

Deploy order matters — relayd loads `tls keypair code.f3s.buetow.org` at startup,
so the certificate has to exist first:

```sh
cd frontends
rex -H blowfish.buetow.org:2 nsd httpd acme acme_invoke relayd
rex -H fishfinger.buetow.org:2 nsd httpd acme acme_invoke relayd
```

`acme.sh` copies the `foo.zone` cert as a placeholder for any host that has none
yet, so relayd will still start on the first pass; the real certificate arrives
on the same run. Deploying one gateway at a time avoids restarting both public
frontends simultaneously.

### 3. Deploy

```sh
kubectl apply -f ../argocd-apps/services/forgejo.yaml
```

Or just push — ArgoCD picks it up automatically.

### 4. Create the admin user

The web installer is locked (`INSTALL_LOCK=true`) and registration is disabled,
because this instance is reachable from the public internet. Create the first
account from the CLI:

```sh
just create-admin
```

## Repository URLs

```sh
# HTTPS
git clone https://code.f3s.buetow.org/<user>/<repo>.git

# SSH (NodePort; LAN only unless you forward it)
git clone ssh://git@r0.lan.buetow.org:30222/<user>/<repo>.git
```

## Operations

```sh
just status          # pods, services, ingress, PVCs, ArgoCD sync
just logs            # follow logs
just restart         # rollout restart
just shell           # shell inside the pod
just port-forward    # reach the UI on localhost:3000
```

## Backup

Covered by the ZFS snapshots and zrepl replication of `/data/nfs`. The SQLite
database and `app.ini` both live on that volume. To restore: roll back the ZFS
snapshot and restart the deployment.

Note that `/etc/gitea/app.ini` holds `SECRET_KEY` and `INTERNAL_TOKEN`, generated
on first start. Restoring the data volume without the matching config volume
invalidates sessions and stored credentials.