217 lines
7.9 KiB
YAML
217 lines
7.9 KiB
YAML
# Forgejo Helm Values — Single Source of Truth
|
|||
|
|
# Chart: https://codeberg.org/forgejo-contrib/forgejo-helm
|
||
|
|
|
||
# Recreate (not RollingUpdate): the gitea data volume is a single RWO PVC. With
|
|||
|
|
# RollingUpdate the new pod tries to attach the PVC while the old pod still holds
|
||
|
|
# it -> "Multi-Attach error", new pod stuck Init forever, rollout wedged. Recreate
|
||
|
|
# terminates the old pod first so the PVC detaches before the new one starts.
|
||
|
|
strategy:
|
||
|
|
type: Recreate
|
||
|
|
|
||
# Disable bundled dependencies (use external CNPG + Redis instead)
|
|||
|
|
postgresql-ha:
|
||
|
|
enabled: false
|
||
|
|
|
||
|
|
valkey:
|
||
|
|
enabled: false
|
||
|
|
|
||
|
|
valkey-cluster:
|
||
|
|
enabled: false
|
||
|
|
|
||
|
|
redis:
|
||
|
|
enabled: false
|
||
|
|
|
||
# External SSH access for git over the LAN. The chart's ssh Service becomes a
|
|||
|
|
# LoadBalancer with a stable IP from the Cilium homelab-pool (192.168.1.160/28,
|
||
|
|
# L2-announced) so `git clone ssh://[email protected]:2222/...` works from the
|
||
|
|
# LAN. gitea's sshd listens on 2222 in-pod; port 2222 is exposed directly to
|
||
|
|
# avoid needing privileged :22.
|
||
|
|
service:
|
||
|
|
ssh:
|
||
|
|
type: LoadBalancer
|
||
|
|
port: 2222
|
||
|
|
annotations:
|
||
|
|
lbipam.cilium.io/ips: "192.168.1.161"
|
||
|
|
|
||
gitea:
|
|||
podAnnotations:
|
|||
|
|
reloader.stakater.com/auto: "true"
|
||
admin:
|
|||
existingSecret: forgejo-admin
|
|||
|
|||
|
|
config:
|
||
|
|
server:
|
||
|
|
DOMAIN: forgejo.riotpiao.com
|
||
|
|
ROOT_URL: https://forgejo.riotpiao.com
|
||
# SSH clone URLs advertise git.riotpiao.com:2222 (the LoadBalancer above).
|
|||
|
|
SSH_DOMAIN: git.riotpiao.com
|
||
|
|
SSH_PORT: 2222
|
||
|
|
SSH_LISTEN_PORT: 2222
|
||
|
|||
|
|
# Self-signup was open (GET /user/sign_up returned 200), so anyone reaching
|
||
|
|
# forgejo.riotpiao.com could create an account. Accounts are provisioned by
|
||
|
|
# the admin only; admin create-user via UI/API is unaffected by this flag.
|
||
|
|
service:
|
||
|
|
DISABLE_REGISTRATION: true
|
||
|
|
|
||
database:
|
|||
|
|
DB_TYPE: postgres
|
||
HOST: forgejo-db-rw.cicd.svc.cluster.local:5432
|
|||
NAME: forgejo
|
|||
# User/password injected via extraEnv (secretKeyRef doesn't work in config)
|
|||
|
|||
|
|
cache:
|
||
|
|
ADAPTER: redis
|
||
HOST: redis://forgejo-redis.cicd.svc.cluster.local:6379/0
|
|||
|
|||
|
|
session:
|
||
|
|
PROVIDER: redis
|
||
PROVIDER_CONFIG: redis://forgejo-redis.cicd.svc.cluster.local:6379/1
|
|||
|
|||
|
|
queue:
|
||
|
|
TYPE: redis
|
||
CONN_STR: redis://forgejo-redis.cicd.svc.cluster.local:6379/2
|
|||
|
|||
# Actions must be enabled globally, not just per-repo. Without this section
|
|||
|
|
# app.ini carries no [actions] block at all and Forgejo never *creates* a
|
||
|
|
# workflow run — the API returns total_count: 0 for every repo even though
|
||
|
|
# each repo reports has_actions: true, the workflow file is on the default
|
||
|
|
# branch, and forgejo-runner has registered successfully with label
|
||
|
|
# [docker]. Registration does not require the dispatcher, so a healthy-
|
||
|
|
# looking runner sitting at "[poller 0] launched" with zero task pickups is
|
||
|
|
# the symptom of this being off, not of a bad workflow or a label mismatch.
|
||
|
|
#
|
||
|
|
# DEFAULT_ACTIONS_URL is left at its default (https://code.forgejo.org),
|
||
|
|
# which is where `uses: actions/checkout@v4` and friends resolve from. That
|
||
|
|
# requires egress from the runner; if it is ever blocked, pin the actions to
|
||
|
|
# local copies rather than turning this off.
|
||
|
|
actions:
|
||
|
|
ENABLED: true
|
||
|
|
|
||
mailer:
|
|||
|
|
ENABLED: true
|
||
|
|
PROTOCOL: smtp+starttls
|
||
|
|
SMTP_ADDR: smtp.gmail.com
|
||
|
|
SMTP_PORT: 587
|
||
|
|
FROM: "Forgejo <[email protected]>"
|
||
|
|
# USER and PASSWD injected via env vars below (GITEA__MAILER__USER, GITEA__MAILER__PASSWD)
|
||
|
|
|
||
# Persistence (shared storage for repos)
|
|||
#
|
|||
|
|
# 60Gi, not the original 20Gi. The container registry stores its blobs here,
|
||
|
|
# and a single candle-vllm sm_70 image is 14GB uncompressed. At 20Gi the volume
|
||
|
|
# filled, and every blob upload failed with:
|
||
|
|
# Package registry API internal error: 500 write /data/tmp/package-upload/...:
|
||
|
|
# no space left on device
|
||
|
|
# The push client surfaces that as a bare `received unexpected HTTP status: 500`
|
||
|
|
# after retrying every layer, which looks like a network fault and is not one.
|
||
|
|
#
|
||
|
|
# Note for whoever expands this again: Longhorn's admission webhook checks the
|
||
|
|
# PHYSICAL free space of the disk holding each replica, not the thin volume
|
||
|
|
# size. A request for 100Gi was rejected while the disk had 23.5Gi free:
|
||
|
|
# disk ... does not have sufficient physical space for expansion:
|
||
|
|
# physical free space would drop below minimal: left=14596177920 < minimal=49931236556
|
||
|
|
# 60Gi fit. Raise it in steps and watch the webhook message.
|
||
persistence:
|
|||
|
|
enabled: true
|
||
|
|
storageClass: longhorn
|
||
size: 60Gi
|
|||
accessModes:
|
|||
|
|
- ReadWriteOnce
|
||
|
|
|
||
|
|
# Ingress
|
||
|
|
ingress:
|
||
|
|
enabled: true
|
||
|
|
className: nginx
|
||
|
|
annotations:
|
||
# No cert-manager annotation. forgejo.riotpiao.com is covered by the
|
|||
|
|
# *.riotpiao.com wildcard, served as nginx's default-ssl-certificate.
|
||
|
|
#
|
||
# This chart Ingress and the hand-written one in
|
|||
|
|
# k8s/bootstrap/ingress/ingress.yaml both claim forgejo.riotpiao.com.
|
||
|
|
# ingress-nginx breaks the tie by oldest creationTimestamp, and the chart's
|
||
|
|
# is older, so it is the one actually serving — the annotations on the other
|
||
|
|
# have never applied. Duplicate should be removed; until then these must
|
||
|
|
# live here or they do nothing.
|
||
|
|
#
|
||
|
|
# proxy-body-size 0 is required for the OCI registry: nginx defaults to 1m,
|
||
|
|
# so any image layer above that fails the push with 413.
|
||
|
|
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||
|
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||
|
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||
hosts:
|
|||
|
|
- host: forgejo.riotpiao.com
|
||
|
|
paths:
|
||
|
|
- path: /
|
||
|
|
pathType: Prefix
|
||
# tls: intentionally omitted. The chart would create a second, single-host
|
|||
|
|
# certificate (forgejo-tls) that expired on 2026-10-23 and competed with the
|
||
|
|
# wildcard. The wildcard covers this host.
|
||
|
|||
|
|
# Resources
|
||
#
|
|||
|
|
# The 1000m ceiling throttled Gitea badly. Measured on a running pod:
|
||
|
|
# nr_throttled 189528 of 1169014 CFS periods (16.2%)
|
||
|
|
# throttled_usec 4424898602 us (~74 minutes lost)
|
||
|
|
# actual usage 951m against the 1000m limit
|
||
|
|
# The visible symptom was slow Actions traffic: FetchTask took 3469.9ms and
|
||
|
|
# repository compare endpoints took 3091-3811ms, so runners timed out and
|
||
|
|
# retried, which added more load to an already saturated process.
|
||
|
|
#
|
||
|
|
# The database was never the bottleneck. A real action_runner UPDATE measured
|
||
|
|
# 2.133ms with zero lock waits, so this is CPU starvation in Gitea itself.
|
||
|
|
#
|
||
|
|
# requests.cpu rises to 750m because a request below real usage earns the pod
|
||
|
|
# little scheduling weight during contention. limits.cpu rises to 3000m
|
||
|
|
# because diff computation is bursty and needs room to run without throttling.
|
||
|
|
#
|
||
|
|
# Caution: this pod runs on talos-cp-3, a control-plane node. 3000m is 25% of
|
||
|
|
# that node's 11950m allocatable CPU. If Gitea sustains near this limit, move
|
||
|
|
# the workload off the control plane rather than raising the limit again.
|
||
resources:
|
|||
|
|
requests:
|
||
cpu: 750m
|
|||
|
|
memory: 768Mi
|
||
limits:
|
|||
cpu: 3000m
|
|||
memory: 2Gi
|
|||
|
|
|
||
|
|
# Tolerations for control-plane
|
||
|
|
tolerations:
|
||
|
|
- key: node-role.kubernetes.io/control-plane
|
||
|
|
operator: Exists
|
||
|
|
effect: NoSchedule
|
||
|
|
|
||
|
|
# ArgoCD adoption labels
|
||
|
|
labels:
|
||
|
|
argocd.argoproj.io/instance: forgejo
|
||
|
|||
|
|
# Inject database credentials via environment variables (overrides app.ini)
|
||
|
|
deployment:
|
||
|
|
env:
|
||
|
|
- name: GITEA__DATABASE__USER
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: forgejo-db-app
|
||
|
|
key: username
|
||
|
|
- name: GITEA__DATABASE__PASSWD
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: forgejo-db-app
|
||
|
|
key: password
|
||
- name: GITEA__MAILER__USER
|
|||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: forgejo-smtp
|
||
|
|
key: user
|
||
|
|
- name: GITEA__MAILER__PASSWD
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: forgejo-smtp
|
||
|
|
key: password
|
||
# Allow webhooks to reach api-gw through the cluster ingress (resolves to
|
|||
|
|
# internal ClusterIP 10.101.x.x which is blocked by default ALLOWED_HOST_LIST).
|
||
|
|
# api.riotpiao.com is our own gateway — safe to allowlist.
|
||
|
|
- name: GITEA__WEBHOOK__ALLOWED_HOST_LIST
|
||
|
|
value: "external,api.riotpiao.com"
|