How to deploy Gitea on Kubernetes with Helm
Gitea is a simple and hyper-focused git management solution. Here is how you can install it.
As I've been going through and building a self hosted infrastructure, I was looking for a fast, and well maintained version control hosting system. Gitea quickly came up as a great solution. Gitea is a fork of the Gogs project but is developed much quicker. Gitea provides a lot of the features that big Git hosting services, like Github, Gitlab, provide. Although, Gitea is particularly well suited for self hosting because hyper focused on only Git features. Where as Github, and Gitlab, provide pipeline, hosting features as well. Here we will deploy Gitea the easy way, on Kubernetes.
Helm chart
Gitea actually provides a nice and easy to use helm chart for deployment on Kubernetes. The following are the custom values that I used for my deployment.
# Default values for gitea.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
clusterDomain: cluster.local
image:
repository: gitea/gitea
# Overrides the image tag whose default is the chart appVersion.
tag: ""
pullPolicy: Always
rootless: false # only possible when running 1.14 or later
imagePullSecrets: []
# Security context is only usable with rootless image due to image design
podSecurityContext:
fsGroup: 1000
containerSecurityContext: {}
# allowPrivilegeEscalation: false
# capabilities:
# drop:
# - ALL
# # Add the SYS_CHROOT capability for root and rootless images if you intend to
# # run pods on nodes that use the container runtime cri-o. Otherwise, you will
# # get an error message from the SSH server that it is not possible to read from
# # the repository.
# # https://gitea.com/gitea/helm-chart/issues/161
# add:
# - SYS_CHROOT
# privileged: false
# readOnlyRootFilesystem: true
# runAsGroup: 1000
# runAsNonRoot: true
# runAsUser: 1000
# DEPRECATED. The securityContext variable has been split two:
# - containerSecurityContext
# - podSecurityContext.
securityContext: {}
service:
http:
type: ClusterIP
port: 3000
clusterIP: None
#loadBalancerIP:
#nodePort:
#externalTrafficPolicy:
#externalIPs:
#ipFamilyPolicy:
#ipFamilies:
loadBalancerSourceRanges: []
annotations:
ssh:
type: ClusterIP
port: 22
clusterIP: None
#loadBalancerIP:
#nodePort:
#externalTrafficPolicy:
#externalIPs:
#ipFamilyPolicy:
#ipFamilies:
#hostPort:
loadBalancerSourceRanges: []
annotations:
ingress:
enabled: true
className: nginx
annotations:
{}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: [root-url]
paths:
- path: /
pathType: Prefix
tls: []
# - secretName: chart-example-tls
# hosts:
# - git.example.com
# Mostly for argocd or any other CI that uses `helm template | kubectl apply` or similar
# If helm doesn't correctly detect your ingress API version you can set it here.
# apiVersion: networking.k8s.io/v1
resources:
{}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
## Use an alternate scheduler, e.g. "stork".
## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
##
# schedulerName:
nodeSelector: {}
tolerations: []
affinity: {}
statefulset:
env:
[]
# - name: VARIABLE
# value: my-value
terminationGracePeriodSeconds: 60
labels: {}
annotations: {}
persistence:
enabled: true
# existingClaim:
size: 10Gi
accessModes:
- ReadWriteOnce
labels: {}
annotations: {}
# storageClass:
# subPath:
# additional volumes to add to the Gitea statefulset.
extraVolumes:
# - name: postgres-ssl-vol
# secret:
# secretName: gitea-postgres-ssl
# additional volumes to mount, both to the init container and to the main
# container. As an example, can be used to mount a client cert when connecting
# to an external Postgres server.
extraVolumeMounts:
# - name: postgres-ssl-vol
# readOnly: true
# mountPath: "/pg-ssl"
# bash shell script copied verbatim to the start of the init-container.
initPreScript: ""
#
# initPreScript: |
# mkdir -p /data/git/.postgresql
# cp /pg-ssl/* /data/git/.postgresql/
# chown -R git:git /data/git/.postgresql/
# chmod 400 /data/git/.postgresql/postgresql.key
# Configure commit/action signing prerequisites
signing:
enabled: false
gpgHome: /data/git/.gnupg
gitea:
admin:
#existingSecret: gitea-admin-secret
username: [gitea-admin]
password: [gitea-password]
email: [gitea-email]
metrics:
enabled: false
serviceMonitor:
enabled: false
# additionalLabels:
# prometheus-release: prom1
ldap:
[]
# - name: "LDAP 1"
# existingSecret:
# securityProtocol:
# host:
# port:
# userSearchBase:
# userFilter:
# adminFilter:
# emailAttribute:
# bindDn:
# bindPassword:
# usernameAttribute:
# publicSSHKeyAttribute:
# Either specify inline `key` and `secret` or refer to them via `existingSecret`
oauth:
[]
config:
# APP_NAME: "Gitea: Git with a cup of tea"
# RUN_MODE: dev
#
# server:
# SSH_PORT: 22
#
# security:
# PASSWORD_COMPLEXITY: spec
server:
ROOT_URL: [root-url]
service:
DISABLE_REGISTRATION: false
REQUIRE_SIGNIN_VIEW: true
SHOW_REGISTRATION_BUTTON: false
openid:
ENABLE_OPENID_SIGNIN: false
ENABLE_OPENID_SIGNUP: true
oauth2_client:
ENABLE_AUTO_REGISTRATION: true
additionalConfigSources: []
# - secret:
# secretName: gitea-app-ini-oauth
# - configMap:
# name: gitea-app-ini-plaintext
additionalConfigFromEnvs: []
podAnnotations: {}
# Modify the liveness probe for your needs or completely disable it by commenting out.
livenessProbe:
tcpSocket:
port: http
initialDelaySeconds: 200
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 10
# Modify the readiness probe for your needs or completely disable it by commenting out.
readinessProbe:
tcpSocket:
port: http
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
# # Uncomment the startup probe to enable and modify it for your needs.
# startupProbe:
# tcpSocket:
# port: http
# initialDelaySeconds: 60
# timeoutSeconds: 1
# periodSeconds: 10
# successThreshold: 1
# failureThreshold: 10
memcached:
enabled: true
service:
port: 11211
postgresql:
enabled: true
global:
postgresql:
postgresqlDatabase: gitea
postgresqlUsername: [db-username]
postgresqlPassword: [db-password]
servicePort: 5432
persistence:
size: 10Gi
mysql:
enabled: false
root:
password: gitea
db:
user: gitea
password: gitea
name: gitea
service:
port: 3306
persistence:
size: 10Gi
mariadb:
enabled: false
auth:
database: gitea
username: gitea
password: gitea
rootPassword: gitea
primary:
service:
port: 3306
persistence:
size: 10Gi
# By default, removed or moved settings that still remain in a user defined values.yaml will cause Helm to fail running the install/update.
# Set it to false to skip this basic validation check.
checkDeprecation: true
Pay particular attention to the config
section. That section is how you can change the behaviour of your deployment. For examle, I'm using it to disable registration on my instance.
So it's deployed right? Not yet. I'm using the ingress-nginx, an ingress that's officially maintained by the Kubernetes project. If you are using the ingress as well, I highly recommend using it because it can make a lot of things easier to manage, then you also have to change that ingress' tcp
section. In the values file of you ingress, near the bottom, find the tcp
section and make the following adjustments.
# TCP service key:value pairs
# Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md
##
tcp:
22: "[namespace]/[instance-name]-gitea-ssh:22"
This will allow you to use SSH to pull and push changes to your repos.
Now you're done! 👍