Backup & disaster recovery
What to back up, and how to restore CRs without the operator recreating or deleting anything in Forgejo.
The operator keeps no state of its own: everything is in the CRs (spec and status) and in Forgejo. Two things need a backup, and they must be restored together.
| What | Holds | Backed up by |
|---|---|---|
| Forgejo (database, repositories) | The real objects | Forgejo's own backup (forgejo dump, database + data volume snapshots) |
| The CRs, with their status | Which Forgejo object each CR owns (status.forgejoId) | Velero, an etcd snapshot, or a kubectl export |
| The Secrets the CRs reference | Instance token, webhook secrets, migration credentials | Same as the CRs (or your secret manager) |
The generated credentials (passwords, tokens, OAuth2 secrets, SSH keys) don't need a backup: a restored CR whose target Secret is missing re-issues it.
Why the status matters
status.forgejoId links a CR to its Forgejo object. A CR restored
without status is a new CR: the operator tries to create the object,
finds the name taken, and reports ForgejoObjectAlreadyExists. It never
takes over an existing object on its own. GitOps tools and kubectl apply
don't write status, so your Git repository alone is not a backup.
Back up
Velero (recommended) backs up CRs with their status subresource:
velero backup create forgejo-crs \
--include-resources 'forgejo*.forgejo.weebo.io,secrets' \
--include-cluster-resources=truePlain export, e.g. a nightly CronJob:
kinds=forgejoinstances,forgejousers,forgejouserkeys,forgejoorganizations,forgejoteams,forgejonamespacepolicies,forgejorepositories,forgejowebhooks,forgejodeploykeys,forgejooauth2applications,forgejoaccesstokens
kubectl get "$kinds" -A -o yaml > forgejo-crs-$(date +%F).yamlStore it like a secret: it names every managed object, and the CRDs' specs can reference Secrets.
Restore
Restore Forgejo first
From the same point in time as the CRs. A CR whose object no longer exists in Forgejo (restored from an older backup) recreates it; an object newer than the CR backup is simply unmanaged.
Stop the operator
So nothing reconciles a half-restored set:
kubectl -n weebo-forgejo scale deploy weebo-forgejo --replicas=0With the operator down the webhook is down too: namespaced CRs can't be
created through it. Either restore them with Velero (it restores the status
subresource too), or temporarily switch the webhook to Ignore as in
Troubleshooting.
Restore the CRs with their status
- Velero:
velero restore create --from-backup forgejo-crs. It restores the status. - From an export:
kubectl create -fdrops the status. Use the importer instead: it reads the live Forgejo and creates the CRs with their ids already in status. Then re-apply your Git manifests on top, which only touchesspec.
The CRDs must exist first (crds.install=true, or the release's CRD
bundle).
Start the operator again
kubectl -n weebo-forgejo scale deploy weebo-forgejo --replicas=2
kubectl get fji,fjorg,fjteam,fjuser
kubectl get fjrepo,fjhook,fjdkey,fjoauth,fjtoken -AEverything should come back Ready. A CR reporting
ForgejoObjectAlreadyExists lost its status: adopt it with the importer.
Losing the cluster, keeping Forgejo
Rebuild the cluster, install the operator, connect
the instance, then run the importer with --apply
before applying your Git manifests. The importer adopts every existing
object; your manifests then take over their spec.
OAuth2 applications and access tokens can't be imported (Forgejo never returns their secrets): their CRs create new ones, and the old ones stay in Forgejo until you delete them.