weebo-forgejo

Install

Install, upgrade and uninstall the operator and its CRDs with Helm.

The operator ships as a Helm chart that installs the CRDs, the operator Deployment and its admission webhook. Forgejo itself is not part of it.

Before you begin

  • Kubernetes 1.30+ (the chart refuses older clusters), with kubectl and helm 3.8+ (for OCI charts).
  • cert-manager, which issues the admission webhook's TLS certificate. You can do without it and bring your own certificate.
  • Forgejo v16 or later. Older versions lack the admin token API the operator relies on, and their ForgejoInstance reports UnsupportedForgejoVersion.

Install

helm install weebo-forgejo oci://ghcr.io/batleforc/charts/weebo-forgejo \
  --namespace weebo-forgejo --create-namespace --version X.Y.Z

Versions are listed on the releases page. Every image and chart is signed; see Verify a release to check them before installing.

Check the install

kubectl -n weebo-forgejo get pods
kubectl get crd | grep forgejo.weebo.io

Two Running pods and eleven CRDs mean you're ready to connect a Forgejo.

Use your own certificate issuer

By default the chart creates a self-signed cert-manager Issuer for the webhook. To use an existing issuer instead:

helm install weebo-forgejo oci://ghcr.io/batleforc/charts/weebo-forgejo \
  --namespace weebo-forgejo --create-namespace \
  --set certManager.createIssuer=false \
  --set certManager.issuerRef.name=my-issuer \
  --set certManager.issuerRef.kind=ClusterIssuer

Without cert-manager

Set webhook.certManager.enabled=false and provide the certificate yourself:

  1. A kubernetes.io/tls Secret in the release namespace, valid for <release>-weebo-forgejo.<namespace>.svc (or weebo-forgejo.<namespace>.svc when the release is called weebo-forgejo).
  2. The CA that signed it, base64-encoded, as webhook.caBundle. The API server uses it to trust the webhook.
# Example with a throwaway CA; use your PKI in production.
SVC=weebo-forgejo.weebo-forgejo.svc
openssl req -x509 -newkey rsa:2048 -nodes -days 365 -subj "/CN=weebo-ca" \
  -keyout ca.key -out ca.crt
openssl req -newkey rsa:2048 -nodes -subj "/CN=$SVC" -keyout tls.key -out tls.csr
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 \
  -extfile <(printf "subjectAltName=DNS:%s,DNS:%s.cluster.local" "$SVC" "$SVC") -out tls.crt
kubectl -n weebo-forgejo create secret tls weebo-forgejo-webhook-tls --cert tls.crt --key tls.key

helm install weebo-forgejo oci://ghcr.io/batleforc/charts/weebo-forgejo \
  --namespace weebo-forgejo \
  --set webhook.certManager.enabled=false \
  --set webhook.tls.secretName=weebo-forgejo-webhook-tls \
  --set webhook.caBundle="$(base64 -w0 < ca.crt)"

The operator reloads the certificate every 60 s: renew the Secret in place before it expires, with the same CA (or update webhook.caBundle first).

What gets deployed

ObjectWhy
CRDs (11)The API. Templated, so upgrades update them; never deleted by Helm (see CRDs)
Deployment (2 replicas)One replica is elected leader and does the work; every replica answers the webhook. See High availability
PodDisruptionBudgetKeeps a webhook replica up during node drains
ValidatingWebhookConfigurationChecks the allow-list when namespaced kinds are created or updated (never on delete)
Certificate (+ Issuer)Webhook TLS, rotated by cert-manager and reloaded every 60 s
ClusterRole (+ Binding), Role (+ Binding)The operator's own permissions; the Role covers its leader-election Lease. See Security model
Services weebo-forgejo, weebo-forgejo-metricsThe webhook (HTTPS), and Prometheus metrics (HTTP, when enabled)
ConfigMap weebo-forgejo-argocd-healthA health check for ArgoCD; see ArgoCD
Optional: ServiceMonitor, PrometheusRule, NetworkPolicySee Monitoring and Security model

Main values

Every value is validated against the chart's values.schema.json: a typo or an invalid enum fails the install instead of being ignored. helm show values oci://ghcr.io/batleforc/charts/weebo-forgejo prints them all with their comments.

ValueDefaultDescription
replicaCount2Operator replicas. Keep at least 2
image.tagv + chart appVersionAdd -static for the statically linked image, e.g. vX.Y.Z-static
image.digestsha256:…: pull repository@digest, ignoring tag
crds.installtrueInstall and upgrade the CRDs with the chart
leaderElection.leaseNameweebo-forgejo-operator-leaderThe leader-election Lease (distinct per release sharing a namespace)
webhook.failurePolicyFailKeep Fail: the allow-list is a security boundary, and Ignore would skip it while the webhook is down
webhook.timeoutSeconds5How long the API server waits for an admission answer
webhook.namespaceSelector / objectSelector{}Restrict which objects the webhook sees (the reconcilers still enforce the allow-list)
webhook.certManager.enabledtruefalse: set webhook.tls.secretName and webhook.caBundle
certManager.createIssuertrueCreate a self-signed Issuer for the webhook
certManager.issuerRef.*The issuer to use when createIssuer is false
metrics.prometheus.enabledtruePrometheus endpoint on port 9090
serviceMonitor.enabled / prometheusRule.enabledfalseprometheus-operator objects
resourcesrequests 50m/64Mi, limit 256MiSee sizing
priorityClassNamee.g. a class above your workloads': the webhook gates CR writes
defaultTopologySpread / topologySpreadConstraintssoft spread per nodeReplace with your own constraints (zones, …)
probes.*Startup, readiness (/readyz) and liveness (/livez) probe timings
shutdown.terminationGracePeriodSeconds960Lets an in-flight repository migration finish
shutdown.drainSeconds5The webhook keeps answering this long after SIGTERM
shutdown.preStopSleepSeconds0A native preStop sleep before SIGTERM (the images have no shell)
networkPolicy.enabledfalseSee Network policy
rbac.namespacedExample.*disabledAn example Role letting a team manage the namespaced kinds in its own namespace
extraEnv[]Extra environment variables, e.g. OTEL_EXPORTER_OTLP_ENDPOINT or RUST_LOG

Release artifacts

ArtifactWhere
Helm chartoci://ghcr.io/batleforc/charts/weebo-forgejo, version X.Y.Z (also attached to the GitHub Release)
Operator image (Red Hat Hummingbird)ghcr.io/batleforc/weebo-forgejo-operator:vX.Y.Z (and latest), linux/amd64
Operator image (static, on Hummingbird's static base)ghcr.io/batleforc/weebo-forgejo-operator:vX.Y.Z-static (and static), same platforms, when its scan passed
CRD bundleweebo-forgejo-crds.yaml on the GitHub Release

Both images also contain weebo-forgejo-importer, used to migrate an existing Forgejo. They carry an SBOM and SLSA provenance, and are signed with cosign.

CRDs

The chart renders the CRDs as regular templates (templates/crds/), not from Helm's crds/ directory, which Helm installs once and never upgrades. So helm upgrade upgrades the CRDs too. Each CRD carries helm.sh/resource-policy: keep: helm uninstall, or switching crds.install off, never deletes them, and with them every CR.

If you manage CRDs separately (GitOps with a dedicated CRD stage, or a cluster where only admins may write CRDs), install with crds.install=false and apply the release's bundle yourself, before each chart upgrade:

kubectl apply --server-side \
  -f https://github.com/batleforc/weebo-forgejo/releases/download/vX.Y.Z/weebo-forgejo-crds.yaml

Upgrade

helm upgrade weebo-forgejo oci://ghcr.io/batleforc/charts/weebo-forgejo \
  --namespace weebo-forgejo --version X.Y.Z

The CRDs are upgraded with the chart. Read the release notes first: they list any CRD or value change.

Upgrading to a release on weebo-operator-kit v0.14

The token Secret of every ForgejoLocalInstance must carry the label forgejo.weebo.io/instance-token: "true"; without it the instance is Ready: False (TokenSecretNotAllowed) and its CRs stop reconciling. Label them before upgrading:

kubectl get forgejolocalinstances -A \
  -o jsonpath='{range .items[*]}{.metadata.namespace} {.spec.tokenSecretRef.name}{"\n"}{end}' |
  while read -r ns secret; do
    kubectl -n "$ns" label secret "$secret" forgejo.weebo.io/instance-token=true --overwrite
  done

ForgejoInstance (cluster-scoped, created by cluster admins) needs no label.

Likewise, the Secrets a CR hands to Forgejo — migrate.authSecretRef of a ForgejoRepository, secretRef and authorizationHeaderSecretRef of a ForgejoWebhook — must carry forgejo.weebo.io/credential: "true" (SecretNotAllowed otherwise; webhooks re-read theirs on every reconcile):

kubectl get forgejowebhooks -A -o json | jq -r '.items[] | .metadata.namespace as $ns |
  (.spec.secretRef.name, .spec.authorizationHeaderSecretRef.name) // empty | "\($ns) \(.)"' |
  sort -u | while read -r ns secret; do
    kubectl -n "$ns" label secret "$secret" forgejo.weebo.io/credential=true --overwrite
  done

Upgrading from chart 0.1.0

Chart 0.1.0 installed the CRDs from crds/, outside the Helm release. The first upgrade to a chart that templates them fails with invalid ownership metadata unless the existing CRDs are handed over to the release first (nothing is deleted or recreated):

RELEASE=weebo-forgejo NAMESPACE=weebo-forgejo
for crd in $(kubectl get crd -o name | grep '\.forgejo\.weebo\.io$'); do
  kubectl label "$crd" app.kubernetes.io/managed-by=Helm --overwrite
  kubectl annotate "$crd" meta.helm.sh/release-name="$RELEASE" \
    meta.helm.sh/release-namespace="$NAMESPACE" --overwrite
done

The leader-election Lease keeps its name, weebo-forgejo-operator-leader: old and new replicas compete for the same Lease during the rolling update, so there is never more than one leader. Only a second release in the same namespace needs its own (--set leaderElection.leaseName=…).

Uninstall

helm uninstall weebo-forgejo --namespace weebo-forgejo

This removes the operator but keeps the CRDs and your CRs, so nothing in Forgejo is touched. Deleting the CRDs deletes every CR. Only do that after the operator has cleaned up whatever it should (see Deletion): delete your CRs first, then the ForgejoInstances — an instance's finalizer holds it while CRs are still bound to it, since their own cleanup needs it.

A CR whose cleanup can't succeed any more (its instance or token already gone) stays in Terminating with a Warning Event of action Cleanup. Release it without touching Forgejo or its Secrets:

kubectl annotate forgejorepository app forgejo.weebo.io/force-orphan=true

Next

Connect a Forgejo.

On this page