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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: forgejo
namespace: services
labels:
app: forgejo
spec:
replicas: 1
# Recreate so the old pod fully terminates before the new one starts —
# avoids NFS-lock races on the hostPath-backed PVC during rolling updates.
# This also matters for SQLite: exactly one process may hold the database.
strategy:
type: Recreate
selector:
matchLabels:
app: forgejo
template:
metadata:
labels:
app: forgejo
spec:
securityContext:
# The -rootless image runs entirely as the unprivileged git user (1000).
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
initContainers:
- name: nfs-check-data
image: busybox:stable
command:
- sh
- -c
- |
test -f /mnt/.nfs-sentinel || (
echo "ERROR: NFS sentinel missing at /mnt/.nfs-sentinel"
echo "refusing to start; node likely has NFS unmounted"
echo "pod would otherwise bind-mount the local-XFS shadow"
exit 1
)
volumeMounts:
- name: forgejo-data
mountPath: /mnt
readOnly: true
- name: nfs-check-config
image: busybox:stable
command:
- sh
- -c
- |
test -f /mnt/.nfs-sentinel || (
echo "ERROR: NFS sentinel missing at /mnt/.nfs-sentinel"
echo "refusing to start; node likely has NFS unmounted"
echo "pod would otherwise bind-mount the local-XFS shadow"
exit 1
)
volumeMounts:
- name: forgejo-config
mountPath: /mnt
readOnly: true
containers:
- name: forgejo
image: codeberg.org/forgejo/forgejo:16.0.1-rootless
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http
protocol: TCP
- containerPort: 2222
name: ssh
protocol: TCP
env:
# SQLite rather than a separate PostgreSQL pod: this is a single-writer
# instance (replicas 1 + Recreate), and NFSv4.2 does real byte-range
# locking, so the usual SQLite-on-NFS corruption mode does not apply.
# Revisit if this ever needs more than one replica.
- name: FORGEJO__database__DB_TYPE
value: "sqlite3"
- name: FORGEJO__database__PATH
value: "/var/lib/gitea/data/forgejo.db"
# Public identity. ROOT_URL must match what relayd terminates TLS for,
# otherwise Forgejo generates clone URLs and redirects on the wrong host.
- name: FORGEJO__server__DOMAIN
value: "code.f3s.buetow.org"
- name: FORGEJO__server__ROOT_URL
value: "https://code.f3s.buetow.org/"
- name: FORGEJO__server__HTTP_PORT
value: "3000"
# Built-in SSH server. SSH_LISTEN_PORT is what the container binds;
# SSH_PORT is what Forgejo advertises in clone URLs, i.e. the NodePort
# users actually reach. git-server already owns 30022, so this is 30222.
- name: FORGEJO__server__START_SSH_SERVER
value: "true"
- name: FORGEJO__server__SSH_LISTEN_PORT
value: "2222"
# relayd listens on 2022 on the gateways and TCP-forwards to NodePort
# 30222, so git+ssh works from off-LAN (see relay "forgejo_ssh4" in
# frontends/etc/relayd.conf.tpl). Not 22: that keeps the forge clear of
# the constant scanning on the default port. Not 2222 either -- dserver
# already holds that on the gateways.
#
# SSH_PORT is what Forgejo advertises in clone URLs (2022, via relayd);
# SSH_LISTEN_PORT above is what the container actually binds (2222).
- name: FORGEJO__server__SSH_DOMAIN
value: "code.f3s.buetow.org"
- name: FORGEJO__server__SSH_PORT
value: "2022"
# Behind relayd -> Traefik, Forgejo's default trusts only 127.0.0.0/8,
# so every request would be attributed to the Traefik pod IP: real client
# IPs lost from the audit trail and per-IP rate limiting defeated. That
# matters here because the instance is internet-facing. 10.42.0.0/16 is
# the k3s pod CIDR; Traefik is already configured to pass the correct
# X-Forwarded-For (see f3s/traefik-config).
- name: FORGEJO__security__REVERSE_PROXY_TRUSTED_PROXIES
value: "10.42.0.0/16"
# This instance is reachable from the public internet through relayd.
# Lock the installer (otherwise the first visitor gets the setup wizard)
# and keep signups closed; create the admin with the CLI, see README.
- name: FORGEJO__security__INSTALL_LOCK
value: "true"
- name: FORGEJO__service__DISABLE_REGISTRATION
value: "true"
# Catches stale NFS file handles (ESTALE) after an NFS server restart,
# which a plain HTTP probe would not notice until a request touched disk.
livenessProbe:
exec:
command: ["test", "-f", "/var/lib/gitea/.nfs-sentinel"]
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 3
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /api/healthz
port: 3000
initialDelaySeconds: 20
periodSeconds: 15
failureThreshold: 3
timeoutSeconds: 5
volumeMounts:
- name: forgejo-data
mountPath: /var/lib/gitea
- name: forgejo-config
mountPath: /etc/gitea
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
volumes:
- name: forgejo-data
persistentVolumeClaim:
claimName: forgejo-data-pvc
- name: forgejo-config
persistentVolumeClaim:
claimName: forgejo-config-pvc
|