Skip to content

Gitea

Deploy Gitea on Kubernetes — a lightweight self-hosted Git service with a full web UI, issue tracking, CI/CD (Gitea Actions), package registry, and SSH access. The chart uses the official rootless image (UID 1000) for improved security.

gitea.rootUrl is required — wrong value breaks all clone URLs and webhooks

Gitea uses gitea.rootUrl to construct all clone URLs, webhook callback addresses, and OAuth redirect URIs. If this value is wrong or missing, users will see incorrect clone URLs and webhooks will fail silently. Always set it to the full public URL (e.g. https://git.example.com/).

Key Features

  • Rootless official image — runs as UID 1000 for improved pod security
  • Dual service — separate HTTP (3000) and SSH (2222) services
  • Three database backends — SQLite (default), PostgreSQL, MySQL with auto-detection
  • Admin bootstrap Job — optional first-run Job to create the admin user
  • GITEA__ env configuration — all app.ini keys configurable via environment variables
  • Database-aware backuptar for SQLite, pg_dump/mysqldump for SQL databases
  • External Secrets guard — admin credentials can be reconciled by ESO without chart-managed drift

Chart Documentation

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install gitea helmforge/gitea -f values.yaml

OCI registry:

helm install gitea oci://ghcr.io/helmforgedev/helm/gitea -f values.yaml

Deployment Examples

# values.yaml — Gitea with SQLite (zero database configuration)
gitea:
  rootUrl: 'https://git.example.com/' # required; wrong value breaks all clone URLs
  sshDomain: git.example.com
  sshPort: 2222
  disableRegistration: true # private instance
  requireSignIn: true # require login to view any page

admin:
  existingSecret: gitea-admin-credentials
  # secret keys: admin-username, admin-password, admin-email

persistence:
  enabled: true
  size: 50Gi # repositories + LFS objects + config + SQLite

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: git.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: gitea-tls
      hosts:
        - git.example.com
# values.yaml — Gitea with bundled PostgreSQL (recommended for production)
gitea:
  rootUrl: 'https://git.example.com/'
  sshDomain: git.example.com
  disableRegistration: true
  requireSignIn: true

admin:
  existingSecret: gitea-admin-credentials

postgresql:
  enabled: true
  auth:
    database: gitea
    username: gitea
    password: 'strong-db-password'
  primary:
    persistence:
      enabled: true
      size: 20Gi

persistence:
  enabled: true
  size: 100Gi # repositories + LFS objects (no SQLite with PostgreSQL active)

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: gitea-backups
    existingSecret: gitea-s3-credentials

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: git.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Gitea with SSH exposed via NodePort for external Git access
gitea:
  rootUrl: 'https://git.example.com/'
  sshDomain: k8s-node.example.com # public hostname or IP of the Kubernetes node
  sshPort: 30022 # must match service.ssh.nodePort

admin:
  existingSecret: gitea-admin-credentials

service:
  http:
    type: ClusterIP
    port: 3000
  ssh:
    enabled: true
    type: NodePort
    port: 2222
    nodePort: 30022 # expose SSH on node port 30022

postgresql:
  enabled: true
  auth:
    password: 'strong-db-password'

persistence:
  enabled: true
  size: 100Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: git.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Gitea with external PostgreSQL and SMTP via GITEA__ env
gitea:
  rootUrl: 'https://git.example.com/'
  sshDomain: git.example.com
  disableRegistration: true
  requireSignIn: true
  extraEnv:
    - name: GITEA__mailer__ENABLED
      value: 'true'
    - name: GITEA__mailer__FROM
      value: '[email protected]'
    - name: GITEA__mailer__SMTP_ADDR
      value: smtp.example.com
    - name: GITEA__mailer__SMTP_PORT
      value: '587'
    - name: GITEA__mailer__USER
      valueFrom:
        secretKeyRef:
          name: gitea-smtp-credentials
          key: username
    - name: GITEA__mailer__PASSWD
      valueFrom:
        secretKeyRef:
          name: gitea-smtp-credentials
          key: password

admin:
  existingSecret: gitea-admin-credentials

database:
  mode: external
  external:
    vendor: postgres
    host: postgres.database.svc.cluster.local
    name: gitea
    username: gitea
    existingSecret: gitea-db-credentials
    existingSecretPasswordKey: database-password

persistence:
  enabled: true
  size: 100Gi

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: gitea-backups
    existingSecret: gitea-s3-credentials

Configuration Reference

Core

Parameter Type Default Description
replicaCount integer 1 Pod replicas. SQLite supports 1 only.
nameOverride string "" Override the chart name.
fullnameOverride string "" Override the full release name.

Image

Parameter Type Default Description
image.repository string docker.io/gitea/gitea Gitea image.
image.tag string "1.26.2-rootless" Rootless image tag (UID 1000).

Gitea Configuration

Parameter Type Default Description
gitea.appName string "Gitea: ..." Application display name.
gitea.runMode string prod Run mode: dev, prod, test.
gitea.rootUrl string "" Required. Full public URL (e.g. https://git.example.com/).
gitea.sshDomain string "" SSH domain shown in clone URLs. Defaults to localhost unless set.
gitea.sshPort integer 2222 SSH listen port (rootless image default).
gitea.lfsEnabled boolean true Enable Git Large File Storage.
gitea.disableRegistration boolean false Disable user self-registration. Set true for private instances.
gitea.requireSignIn boolean false Require login to view any page.
gitea.extraEnv array [] Extra env vars in GITEA__SECTION__KEY format for full app.ini control.
Configure any app.ini setting via GITEA__SECTION__KEY environment variables

Gitea supports overriding any app.ini configuration key via environment variables using the pattern GITEA__<SECTION>__<KEY>. For example, to enable SMTP:

GITEA__mailer__ENABLED=true
GITEA__mailer__SMTP_ADDR=smtp.example.com

Pass these via gitea.extraEnv; for sensitive values, use valueFrom.secretKeyRef.

Admin User

Parameter Type Default Description
admin.username string "" Admin username. Triggers post-install Job to create user if set.
admin.password string "" Admin password. Ignored when existingSecret is set.
admin.email string "" Admin email address.
admin.existingSecret string "" Existing secret. Keys: admin-username, admin-password, admin-email.

Database

Auto-detection precedence (database.mode: auto):

Priority Condition Result
1 database.external.host or external.existingSecret External DB
2 postgresql.enabled: true PostgreSQL subchart
3 mysql.enabled: true MySQL subchart
4 None of the above SQLite (default)
Parameter Type Default Description
database.mode string auto Mode: auto, sqlite, external, postgresql, mysql.
database.sqlite.file string /var/lib/gitea/data/gitea.db SQLite file path inside the data volume.
database.external.vendor string postgres External DB vendor: postgres or mysql.
database.external.host string "" External database hostname.
database.external.existingSecret string "" Existing secret with database password.

Subcharts

Parameter Type Default Description
postgresql.enabled boolean false Deploy the bundled PostgreSQL subchart.
postgresql.auth.password string "" Password. Auto-generated if empty.
postgresql.primary.persistence.size string 8Gi PVC size for PostgreSQL.
mysql.enabled boolean false Deploy the bundled MySQL subchart.
mysql.primary.persistence.size string 8Gi PVC size for MySQL.

Persistence

Parameter Type Default Description
persistence.enabled boolean true Enable PVC for /var/lib/gitea (repositories, LFS, config, SQLite).
persistence.size string 10Gi PVC size. Size based on repository count and LFS usage.
persistence.storageClass string "" StorageClass for the PVC.
persistence.existingClaim string "" Use an existing PVC.

Service

Parameter Type Default Description
service.http.type string ClusterIP HTTP service type.
service.http.port integer 3000 HTTP service port.
service.ssh.enabled boolean true Enable a separate SSH service.
service.ssh.type string ClusterIP SSH service type (ClusterIP, NodePort, LoadBalancer).
service.ssh.port integer 2222 SSH service port.
service.ssh.nodePort string "" NodePort for SSH (only when type: NodePort).

Ingress and Probes

Parameter Type Default Description
ingress.enabled boolean false Enable an Ingress for HTTP traffic.
ingress.ingressClassName string "" Ingress class name.
startupProbe.enabled boolean true Startup probe on /api/healthz.
livenessProbe.enabled boolean true Liveness probe on /api/healthz.
readinessProbe.enabled boolean true Readiness probe on /api/healthz.
resources object {} CPU and memory requests/limits.
extraManifests array [] Extra Kubernetes manifests.

Gateway API

Parameter Type Default Description
gateway.enabled boolean false Render a Gateway API HTTPRoute for HTTP traffic.
gateway.parentRefs array [] Required parent Gateway references.
gateway.hostnames array [] Optional HTTPRoute hostnames.
gateway.path string / HTTPRoute path value.
gateway.pathType string PathPrefix HTTPRoute path match type.

External Secrets

External Secrets integration reconciles admin credentials only. Set admin.existingSecret so the ExternalSecret owns the target Secret and the chart does not create a competing admin Secret.

admin:
  username: gitea_admin
  existingSecret: gitea-admin
externalSecrets:
  enabled: true
  secretStoreRef:
    name: platform-secrets
    kind: SecretStore
  data:
    - secretKey: admin-username
      remoteRef:
        key: gitea/admin
        property: username
    - secretKey: admin-password
      remoteRef:
        key: gitea/admin
        property: password
    - secretKey: admin-email
      remoteRef:
        key: gitea/admin
        property: email

Backup

Database-aware backup: SQLite archives /var/lib/gitea (repositories + LFS + SQLite). PostgreSQL uses pg_dump. MySQL uses mysqldump.

Backup covers the database only for PostgreSQL/MySQL — Git repositories reside in the PVC

When using PostgreSQL or MySQL, the backup CronJob only captures the database via pg_dump or mysqldump. Git repository data and LFS objects stored in the /var/lib/gitea PVC are not included. Back up the PVC separately using Velero, NFS snapshots, or storage provider snapshots. SQLite mode uses tar to archive the full /var/lib/gitea, covering repositories and the database.

Parameter Type Default Description
backup.enabled boolean false Enable scheduled S3 backup CronJob.
backup.schedule string "0 3 * * *" Cron schedule.
backup.s3.endpoint string "" S3-compatible endpoint URL.
backup.s3.bucket string "" Target bucket name.
backup.s3.existingSecret string "" Existing secret with S3 credentials.
backup.database.postgresDumpArgs string "" Extra arguments for pg_dump.
backup.database.mysqlDumpArgs string --single-transaction ... Extra mysqldump arguments.

More Information