Overview
Gitea is a lightweight self-hosted Git service — repositories, pull requests, issues and a built-in package registry. This template deploys one Gitea server (the rootless image) on a persistent volume, a bundled PostgreSQL database from the PostgreSQL template, a public HTTPS endpoint serving the web UI, Git-over-HTTPS and the package registry, and optional Git-over-SSH. The site administrator’s login and Gitea’s three long-lived signing keys are not template values. They come from one dictionary secret you create before installing, so they never pass through Helm and never land in the release. The bundled database’s credentials come from a second dictionary secret you create before installing.What Gets Created
Prerequisites
Two secrets must exist before you install. Secrets are org-level, so no GVC flag is involved.Create the database credentials secret
postgres.config.credentialsSecretName to this name. Secret names are org-wide, so give each Gitea release its own. PostgreSQL reads it only when its data directory is first initialized — see Bundled Database for changing the password later.Create the auth secret
admin* keys create the first site-admin account on first boot; the other three are Gitea’s signing keys:gitea.auth.secretName to this name. Read it back later with cpln secret reveal GITEA_AUTH_SECRET_NAME -o yaml — a bare cpln secret reveal prints only a summary table, not the values.Installation
Install the chart from the marketplace registry, pointing it at your two secrets:UI
CLI
Terraform
Pulumi
Configuration
Each block below is the shipped default for that part ofvalues.yaml.
Image and Resources
maxCpu to minCpu may not exceed 4:1. Raising maxCpu without raising minCpu can cross that limit and is rejected when the workload is applied.Storage
Gitea
gitea.auth.secretName names the prerequisite dictionary secret from Prerequisites. Its admin* keys are consumed once, at first boot: editing the secret later does not change the existing administrator account — see Rotating the Admin Password.
Public Access
git clone and git push from laptops and CI are the point of the service, self-registration is closed by default, and the admin login is a credential you created rather than a published default. Set publicAccess.enabled: false to keep Gitea inside the GVC and reach the UI through cpln port-forward (see Connecting). A change to publicAccess can take several minutes to take effect — re-test before concluding it did not apply.
Git-over-SSH
*.cpln.app canonical endpoint. With ssh.enabled: true that endpoint serves Git-over-SSH on ssh.externalPort instead of HTTPS, so the public web UI and Git-over-HTTPS are no longer reachable at it. SSH is therefore off by default, and Git-over-HTTPS supports full clone, push and pull, so most installs never need it.
When SSH is enabled, Gitea’s built-in SSH server listens on container port 2222 behind a direct TCP load balancer on ssh.externalPort, and the SSH user is git. Clone URLs advertise ssh.domain, or the web host when it is empty — with the default of 22 the repository page shows the familiar form:
ssh.domain to a custom domain that points at the SSH endpoint if you want clean git@ URLs that do not change with the endpoint.
ssh.enabled: false, Gitea’s REST API still reports a placeholder ssh://git@localhost:2222/... clone URL on repository objects. It is cosmetic — the web UI’s clone widget offers only the HTTPS URL — and affects only scripts that read ssh_url from the API.Internal Access
Bundled Database
The database serves Gitea only and is unreachable from outside the GVC. Its credentials come from your prerequisite dictionary secret (username, password and database — see Prerequisites), named by postgres.config.credentialsSecretName.
ALTER ROLE first, then update the secret (export it with cpln secret reveal SECRET_NAME -o yaml > secret.yaml, change only password, and cpln apply -f secret.yaml), and right away force a redeployment of RELEASE_NAME-gitea with cpln workload force-redeployment RELEASE_NAME-gitea --gvc GVC_NAME. Do not redeploy the database: PostgreSQL reads the secret only when it is first initialized, so restarting it only adds downtime.Database Backup
Every knob of the PostgreSQL template is available underpostgres.*, including its scheduled backup. It is off by default; enabling it adds one cron workload that runs pg_dumpall and uploads a gzip-compressed dump to your bucket. The job authenticates with your database credentials secret, and the database identity picks up the bucket-scoped policy or cloud account automatically.
postgres.backup. A database dump alone is not a full Gitea backup: repositories, LFS objects and attachments live on the Gitea volume. See Backing Up.
Connecting
http://localhost:3000 in place of the canonical endpoint:
First Run
There is no web install wizard — the chart locks installation and creates the site administrator from your auth secret on first boot.- Open the canonical endpoint (or
http://localhost:3000through the tunnel) and sign in withadminUsernameandadminPasswordfrom the auth secret. - Self-registration is closed by default (
gitea.disableRegistration: true). Create accounts under Site Administration → Identity & Access → User Accounts, or setgitea.disableRegistration: falseand upgrade the release to allow open sign-up. - Create a repository and push to it over HTTPS. The clone widget gives the URL; authenticate with your Gitea username and password, or generate an access token under Settings → Applications.
Operations
Backing Up
The chart schedules no backups of its own. A complete Gitea backup has two halves: the database (users, issues, pull requests, settings) and the Gitea volume (repositories, LFS objects, attachments, packages). Database. Enable the bundled PostgreSQL’s scheduledpg_dumpall with postgres.backup.enabled: true as described under Database Backup, or take a one-off dump through the database container. The dump is base64-encoded in transit because cpln workload exec is not binary-safe:
gitea dump bundles the database and the volume contents into one archive. The commands below are built from the image’s paths (GITEA_APP_INI is set in the container; the container user owns /var/lib/gitea) but have not been exercised against this template — rehearse on a throwaway release first. Write the archive, then stream it out base64-encoded:
secretKey is the only way to read the 2FA secrets, access tokens and mirror credentials in any database dump.
Restoring a Backup
Restore into a fresh release that points at the same auth secret the backup was taken under — a differentsecretKey makes the restored tokens and 2FA secrets unreadable. Then:
-
Database. For a scheduled
pg_dumpallobject, follow the PostgreSQL template’s Restoring a Backup againstRELEASE_NAME-postgres.GVC_NAME.cpln.local. For apg_dumparchive taken as above, load it through the database container (--stdinis required): -
Gitea volume. Either restore the volume set snapshot taken earlier (
cpln volumeset snapshot restore— check--help; not exercised here), or unpack agitea dumparchive and copy itsrepos/contents to/var/lib/gitea/git/repositoriesand itsdata/contents to/var/lib/gitea/datain the Gitea container, as described in the upstream backup and restore guide. -
Regenerate hooks and restart. After repositories are in place, run the upstream post-restore step and restart Gitea so it re-reads the database:
Rotating the Admin Password
The bootstrap script creates the administrator once; on later starts the account already exists and the secret’sadminPassword is not re-applied. Change the password in the Gitea UI: sign in as the administrator and use Settings → Account.
Updating adminPassword in the secret afterwards keeps your records consistent but has no effect on the live account. Never change secretKey or jwtSecret — the first makes every stored 2FA secret, access token and mirror credential permanently unreadable, the second invalidates every issued OAuth2 token.
Upgrading from 1.0.0
Template version 1.0.0 took the admin login and all three security values as plain Helm values and shipped published defaults for every one of them. Version 1.1.0 moved them into the prerequisite auth secret and rejects each old key at render with a message naming its replacement, so the upgrade fails before anything is applied and the running release is left untouched. There are no compatibility fallbacks.Create the auth secret from your current values
gitea.admin.* and gitea.security.* values your 1.0.0 release was installed with.Remove the six old keys from your values
gitea.admin.* and gitea.security.*, and set gitea.auth.secretName to the secret’s name.Apply the 1.1.0 database rename too
Upgrading from 1.1.0
postgres.config.username, postgres.config.password and postgres.config.database were removed. The database keeps the credentials it was initialized with, so put the same three values into the database credentials secret under Prerequisites (keys username, password and database), set postgres.config.credentialsSecretName to its name, and remove the three keys. The auth secret is unchanged.
Upgrading from 1.2.0
postgres.credentials was removed in 1.3.0: the database credentials are now a prerequisite secret, the same one the postgres template requires on its own. Carrying the old key forward fails the render.
1.2.0 created the database secret itself, so this upgrade deletes it. Before upgrading, create a secret under a new name holding the values your database was initialized with — not new ones, or Gitea loses its database connection:
postgres.config.credentialsSecretName set to that name and postgres.credentials removed. Your current values are in postgres.credentials of the 1.2.0 release, and also in the old secret (cpln secret reveal my-gitea-db-credentials -o yaml, or the name your release set in credentialsSecretName) until the upgrade deletes it.
The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so Gitea can briefly lose its database; it reconnects by itself. The new version of a workload can also wait several minutes with is not allowed to reveal the secret in get-deployments while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.
Scaling and Availability
- Gitea is pinned to one replica. Do not raise it — each replica would get its own repository volume, and there is no shared storage between them.
- The first
cpln helm upgradeafter an install can restart the bundled database, so expect Gitea to be briefly unreachable during it. Treat every upgrade as a short planned outage. - A node failure reschedules the server and reattaches the same volume. The exposure is minutes of downtime, not data loss. The Gitea volume set takes one final snapshot when it is deleted, kept for seven days; it is not snapshotted on a schedule, so use the backup steps above for point-in-time copies.
Troubleshooting
Workload never becomes ready and cpln logs returns nothing
Workload never becomes ready and cpln logs returns nothing
RELEASE_NAME-gitea stays not ready indefinitely, and cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-gitea"}' --limit 50 --since 10m returns zero lines.Cause: gitea.auth.secretName names a secret that does not exist, so the container is never started. The reason is visible only in status.versions[].message:cpln workload force-redeployment RELEASE_NAME-gitea --gvc GVC_NAME.Gitea restarts at startup complaining about the JWT secret
Gitea restarts at startup complaining about the JWT secret
cpln logs shows Gitea failing while decoding the OAuth2 JWT_SECRET.Cause: the jwtSecret key in your auth secret is not base64url of exactly 32 bytes — a hex string or passphrase was used.Fix: this key can be changed safely before any OAuth2 token has been issued. Regenerate it with openssl rand -base64 32 | tr '+/' '-_' | tr -d '=', update the secret, and run cpln workload force-redeployment RELEASE_NAME-gitea --gvc GVC_NAME — a running replica does not pick up a changed secret on its own.Upgrade fails with 'gitea.admin.username was removed in 1.1.0'
Upgrade fails with 'gitea.admin.username was removed in 1.1.0'
cpln helm upgrade stops before touching any resource:gitea.admin.password, gitea.admin.email, gitea.security.secretKey, gitea.security.internalToken and gitea.security.jwtSecret each produce the same kind of message under their own name.Cause: your values still carry keys that were removed in 1.1.0.Fix: follow Upgrading from 1.0.0. Your running release is unaffected until the upgrade succeeds.Upgrade fails with 'config.username was REMOVED in postgres 3.4.0'
Upgrade fails with 'config.username was REMOVED in postgres 3.4.0'
cpln helm upgrade to 1.2.0 stops at render with a message from the bundled PostgreSQL chart:postgres.config.username, postgres.config.password or postgres.config.database, which were removed.Fix: create the database credentials secret with those three values and remove the keys, as in Upgrading from 1.1.0.Admin password changed in the secret but the old one still works
Admin password changed in the secret but the old one still works
adminPassword in the auth secret, but Gitea still accepts the previous password.Cause: the bootstrap script creates the administrator only once; afterwards the password lives in the database, not the secret.Fix: change it in the Gitea UI — see Rotating the Admin Password.Enabled SSH and the public web UI stopped responding
Enabled SSH and the public web UI stopped responding
ssh.enabled: true, HTTPS requests to the *.cpln.app endpoint no longer reach Gitea, while git clone over SSH works.Cause: by design — the single canonical endpoint now serves SSH instead of HTTPS. See Git-over-SSH.Fix: serve the web UI through a custom domain, reach it through cpln port-forward RELEASE_NAME-gitea 3000:3000 --gvc GVC_NAME, or set ssh.enabled: false again to get the public web UI back. Allow several minutes for the change to take effect.Important Notes
- Create both secrets before installing. A reference to a secret that does not exist wedges the workload with no log output; see Prerequisites for the one command that shows the reason.
- Rotating the database password needs
ALTER ROLEplus a forced redeployment ofRELEASE_NAME-gitea— a running workload keeps the values it started with; see Bundled Database. - Never rotate
secretKeyorjwtSecretafter install. The first makes stored 2FA secrets, tokens and mirror credentials permanently unreadable; the second invalidates issued OAuth2 tokens. jwtSecretmust be base64url of exactly 32 bytes — use the command in Prerequisites.- Give each Gitea release its own database credentials secret. Secret names are org-wide.
- Public SSH and the public web UI cannot share the canonical endpoint — SSH is off by default. See Git-over-SSH.
- Do not scale past one replica. Replicas would each get a separate repository volume.
- Editing the auth secret after first boot does not change the administrator account. Change the password in Gitea — see Rotating the Admin Password.
cpln helm uninstalldeletes both volume sets — repositories and database alike. Your auth and database credentials secrets are yours and are left alone.