Overview
Gitea is a lightweight, self-hosted Git service — repositories, pull requests, issues, and a built-in package registry — backed by PostgreSQL. This template deploys a single Gitea server with persistent storage, an automatically wired PostgreSQL database, a public HTTPS web UI with Git-over-HTTPS, and optional Git-over-SSH. The site administrator’s login and the 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.Architecture
- Gitea — Stateful, single-replica workload running the rootless image. Serves the web UI, Git-over-HTTPS, and the package registry on port 3000, plus the built-in SSH server on 2222.
- PostgreSQL — Backing database provisioned from the postgres template as a subchart and connected to Gitea on startup.
What Gets Created
- Stateful Gitea Workload — (
RELEASE_NAME-gitea): the Gitea server, bootstrapped with the site-admin account from your auth secret on first boot. - Stateful PostgreSQL Workload — Single-replica Postgres, automatically connected to Gitea.
- Volume Sets — A persistent volume set for Gitea (
/var/lib/gitea: repositories, LFS objects, attachments, and SSH host keys) and one for PostgreSQL data, both with optional autoscaling. - Bootstrap Secret — (
RELEASE_NAME-gitea-bootstrap): an opaque secret holding the admin-bootstrap script mounted into the container. The template creates no credential secret of its own. - Identity & Policy — An identity bound to the Gitea workload with
revealscoped to exactly three secrets: the bootstrap script, the PostgreSQL config secret, and the auth secret you created. - Direct Load Balancer (optional) — A raw-TCP port for Git-over-SSH, created only when
ssh.enabledistrue.
This template does not create a GVC. You must deploy it into an existing GVC.
Upgrading From Earlier Versions
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. Those are not merely a web login:secretKey encrypts the 2FA secrets, access tokens and mirror credentials stored in the database.
Each removed key is rejected 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.
Prerequisites
One secret must exist before you install. Its values never pass through Helm, so they never land in the release. Secrets are org-level, so no GVC flag is involved.1
Create the auth secret
A dictionary secret holding exactly six keys:Set
gitea.auth.secretName to the name you used. Nothing else is required for a default install.2
Read the secret back later
The
-o yaml is required — plain cpln secret reveal prints only a summary table, not the values:Installation
To install, follow the instructions for your preferred method:UI
Browse, install, and manage templates visually
CLI
Manage templates from your terminal
Terraform
Declare templates in your Terraform configurations
Pulumi
Declare templates in your Pulumi programs
Configuration
The defaultvalues.yaml for this template:
Image and Resources
image— The Gitea image. The template uses the rootless variant, which runs as UID 1000 and serves SSH on port 2222 inside the container.resources— Reservation and limit for the Gitea container (minCpu/maxCpu/minMemory/maxMemory).
Storage
volumeset.capacity— Initial Gitea volume size in GiB (minimum 10). Holds repositories, LFS objects, attachments, and SSH host keys.volumeset.autoscaling.enabled— Automatically expand the volume as it fills. When enabled:maxCapacity— Maximum volume size in GiB.minFreePercentage— Trigger a scale-up when free space drops below this percentage.scalingFactor— Multiply the current capacity by this factor when scaling up.
Gitea
gitea.auth.secretName— Name of the prerequisite dictionary secret holding the admin login and the three signing keys. It must exist before you install.gitea.disableRegistration—true(default) restricts new accounts to admin invites;falseallows open self-registration.
Access
publicAccess.enabled— Serves the HTTPS web UI, Git-over-HTTPS, and the package registry on the auto-assigned*.cpln.appcanonical endpoint.ssh.enabled— Exposes Git-over-SSH via a direct TCP load balancer. Off by default — see The SSH and HTTPS Endpoint Trade-off.ssh.externalPort— The public SSH port clients connect to; also advertised in SSH clone URLs.ssh.domain— The SSH host advertised in clone URLs. Empty uses the web domain.internalAccess.type— Controls which workloads can reach Gitea over the internal network (none,same-gvc,same-org, orworkload-list).
Public access is on by default, deliberately.
git clone and git push from laptops and CI are the point of the service, and both are proven to work against the public endpoint. Self-registration is closed by default, and after 1.1.0 there is no published default credential to protect against — the admin login is one you created.Set publicAccess.enabled: false to keep Gitea inside the GVC and reach the UI with cpln port-forward RELEASE_NAME-gitea 3000:3000 --gvc GVC_NAME. A firewall change takes roughly 30 seconds to 5 minutes to propagate, so re-test rather than trusting the first response.Backing Database
Bundled plumbing: the database serves Gitea only and is unreachable from outside the GVC, and nobody ever types this password into a client, so it stays a value.postgres.credentials.username/password/database— Credentials for the bundled PostgreSQL, applied on first startup. Change the password before installing — the shippedchange-me-gitea-dbis a published placeholder.postgres.config.credentialsSecretName— Name of the dictionary secret the chart creates from those three values and hands to the bundled PostgreSQL. Secret names are organization-wide, so give each release its own name; a second release left on the default is refused at install and creates nothing, leaving the first release untouched.postgres.resources— Reservation and limit for the PostgreSQL container.postgres.volumeset.capacityandpostgres.volumeset.autoscaling— Initial Postgres volume size in GiB (minimum 10) and its autoscaling settings.postgres.internalAccess.type— Controls which workloads can reach PostgreSQL.
PostgreSQL credentials are only applied on first startup, when the data directory is empty. Changing them afterwards has no effect on the running database — use PostgreSQL’s own commands (for example
ALTER USER) instead.Connecting
Sign in at the canonical endpoint to create repositories, users, and organizations. Clone and push over HTTPS against the same endpoint — full clone, push, and anonymous clone of a public repository all work from outside the GVC:
The SSH and HTTPS Endpoint Trade-off
A Control Plane workload has exactly one*.cpln.app canonical endpoint, and it can serve either the HTTPS web UI (web UI plus Git-over-HTTPS on port 443) or a raw-TCP load balancer for SSH — not both. Enabling ssh.enabled repoints that single endpoint to SSH, which takes the public web UI offline.
Because of this, ssh.enabled is false by default, which keeps the public web UI and Git-over-HTTPS working out of the box. Git-over-HTTPS supports full clone, push, and pull, so SSH is not required for normal use.
With
ssh.enabled: false, Gitea’s REST API still reports a placeholder ssh://git@localhost:2222/... clone URL on a repository object. It is cosmetic: the web UI’s clone widget offers only the HTTPS URL, so only a script reading ssh_url from the API is affected.Important Notes
- The auth secret must exist before you install. Without it the deployment wedges with no log output at all; see Prerequisites for the one diagnostic that names it.
- Never rotate
secretKeyorjwtSecretafter install. A newsecretKeymakes existing 2FA secrets, tokens and mirror credentials permanently unreadable, and a newjwtSecretinvalidates issued OAuth2 tokens. jwtSecretmust be base64url of exactly 32 bytes — any other length is rejected at startup. Use the command in Prerequisites.- Public SSH and the public web UI cannot share one endpoint — SSH is off by default. See The SSH and HTTPS Endpoint Trade-off.
- Single replica only. A rolling restart or upgrade incurs brief downtime. Do not raise the workload scale above 1 — replicas would each get separate repository volumes and corrupt state.
- The first Helm upgrade after an install re-applies the bundled PostgreSQL, so expect Gitea to be briefly unreachable while the database restarts. Later upgrades do not do this.
- Editing the auth secret after first boot does not change the existing administrator account — the data directory keeps it. Change the password in the Gitea UI instead.
- Data lives on the volume set and survives redeploys under the same release name. To fully reset,
helm uninstall(which deletes the volume set) then reinstall.
External References
Gitea Documentation
Official Gitea product documentation
Configuration Cheat Sheet
Full reference of Gitea configuration options
Package Registry
Using Gitea’s built-in package registry
Gitea Template
View the source files, default values, and chart definition