How to Deploy Forgejo in Kubernetes

In this short tutorial I will explain how to deploy Forgejo into a Kubernetes Cluster. Forgejo is a self-hosted lightweight source code management solution based on Git. Forgejo is a good alternative to Github if you want to host your own Git Repository server.

Forgejo was founded in 2022 as a fork of the Gitea project. In the meantime it becomes a noteable an relevant Open Source alternative to Github. The project is community driven and maintained by the Codeberg e.v. If you have an open source project you can host your project directly on codeberg.org . For private repositories you can install your own instance of Forgejo.

There are several ways how you can install Forgejo. As it is provided as a Docker Image you can easily start the system with docker compose. See the official documentation.

Read more: How to Deploy Forgejo in Kubernetes

Kubernetes

If you have a Kubernets cluster it is easy to setup a production ready deployment of Forgejo. I assume that you are familiar with Kubernetes (see also my Kubernetes Setup Project Imixs-Cloud on Gihub ) and I assume you have some kind of storage solution and ingress solution already installed.

I split the deployment into two parts, each with a separate deyployment .yaml file

  • Database – 010-postgresql.yaml
  • Git Repo – 020-forgejo.yaml

The Database

For productive environments it is recommanded to replace the build in SQLLight database with MySQL or Postgres. For a kubernetes deployment this is the recommanded way.
In the following deployment example I deploy a postgreSQL database for forgejo

---
###################################################
# Deployment PostgreSQL
###################################################
apiVersion: apps/v1
kind: Deployment
metadata:
  name: forgejo-postgres
  namespace: my-git-repo
  labels: 
    app: forgejo-postgres

spec:
  replicas: 1
  selector: 
    matchLabels:
      app: forgejo-postgres
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: forgejo-postgres
    spec:
     
      containers:
      - env:
        - name: POSTGRES_DB
          value: forgejo
        - name: POSTGRES_USER
          value: forgejo
        - name: POSTGRES_PASSWORD
          value: xxxx

        image: postgres:16-alpine
        name: forgejo-postgres

        readinessProbe:
          exec:
            command: ["pg_isready", "-U", "forgejo"]
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          exec:
            command: ["pg_isready", "-U", "forgejo"]
          initialDelaySeconds: 15
          periodSeconds: 20

          
        ports:
          - containerPort: 5432        
        volumeMounts:
        - mountPath: /var/lib/postgresql/data
          name: forgejo-dbdata
          subPath: postgres
      restartPolicy: Always
      volumes:
      - name: forgejo-dbdata
        persistentVolumeClaim:
          claimName: forgejo-dbdata


---
###################################################
# Data Volume
###################################################
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: forgejo-dbdata
  namespace: my-git-repo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  volumeMode: Filesystem
  volumeName: "forgejo-dbdata"
  storageClassName: ""

---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: forgejo-dbdata
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Gi
  csi:
    driver: rbd.csi.ceph.com
    fsType: ext4
    nodeStageSecretRef:
      name: csi-rbd-secret-ceph-abpua
      namespace: ceph-system
    volumeAttributes:
      "clusterID": "xxxxxxxxxxx"
      "pool": "kubernetes"
      "staticVolume": "true"
      "imageFeatures": "layering"
    volumeHandle: "forgejo-dbdata"
  persistentVolumeReclaimPolicy: Retain
  volumeMode: Filesystem


---
###################################################
# Network
###################################################
apiVersion: v1
kind: Service
metadata:
  name: forgejo-postgres
  namespace: my-git-repo
  labels: 
    app: forgejo-postgres
spec:
  clusterIP: None
  ports:
    - name: tcp
      port: 5432
  selector:
    app: forgejo-postgres

This deplyoment is quite easy and assumes that you have a storage solution (in this example a ceph cluster) to provide a data volume for the postgresql service.

The Git Repo

The second part of our deployment is the forgejo service – providing the git repo and the web applicaiton. Also here we need a data volume for the git repository and configuration and also a ingers network to access the application via HTTPS.

---
###################################################
# Deployment Forgejo
###################################################
apiVersion: apps/v1
kind: Deployment
metadata:
  name: forgejo-git
  namespace: my-git-repo
  labels: 
    app: forgejo-git
spec:
  replicas: 1
  selector: 
    matchLabels:
      app: forgejo-git
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: forgejo-git
    spec:

      containers:
      - env:
        - name: FORGEJO__security__INSTALL_LOCK
          value: "true"
        - name: FORGEJO__database__DB_TYPE
          value: postgres
        - name: FORGEJO__database__HOST
          value: forgejo-postgres:5432
        - name: FORGEJO__database__NAME
          value: forgejo
        - name: FORGEJO__database__USER
          value: forgejo
        - name: FORGEJO__database__PASSWD
          value: xxxx
        - name: FORGEJO__server__DOMAIN
          value: git.forgejo.foo.com
        - name: FORGEJO__server__ROOT_URL
          value: https://git.forgejo.foo.com/
        - name: FORGEJO__server__SSH_DOMAIN
          value: git.forgejo.foo.com
        - name: FORGEJO__service__DISABLE_REGISTRATION
          value: "true"
        # Mail
        - name: FORGEJO__mailer__ENABLED
          value: "true"
        - name: FORGEJO__mailer__PROTOCOL
          value: "smtp"
        - name: FORGEJO__mailer__SMTP_ADDR
          value: "mailgateway.my-git-repo"
        - name: FORGEJO__mailer__SMTP_PORT
          value: "25"
        - name: FORGEJO__mailer__FROM
          value: "Webmaster <webmaster@foo.com>"
        # Layout
        - name: FORGEJO____APP_NAME
          value: "Imixs Workflow"
        - name: FORGEJO____APP_SLOGAN
          value: "Git Forgejo Repository"
        - name: FORGEJO__i18n__DEFAULT_LANG
          value: "en-US"
        - name: FORGEJO__ui__DEFAULT_THEME
          value: "forgejo-dark"
          
        image: codeberg.org/forgejo/forgejo:9
        name: forgejo
        startupProbe:
          httpGet:
            path: /api/healthz
            port: 3000
          failureThreshold: 30     
          periodSeconds: 10

        readinessProbe:
          httpGet:
            path: /api/healthz
            port: 3000
          periodSeconds: 10

        livenessProbe:
          httpGet:
            path: /api/healthz
            port: 3000
          periodSeconds: 20
          failureThreshold: 3   

        ports:
          - containerPort: 3000
            name: http

        volumeMounts:
        - mountPath: /data
          name: forgejo-data
          subPath: forgejo
      restartPolicy: Always
      volumes:
      - name: forgejo-data
        persistentVolumeClaim:
          claimName: forgejo-data

---
###################################################
# Data Volume
###################################################
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: forgejo-data
  namespace: my-git-repo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Gi
  volumeMode: Filesystem
  volumeName: "forgejo-repodata"
  storageClassName: ""

---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: forgejo-repodata
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 200Gi
  csi:
    driver: rbd.csi.ceph.com
    fsType: ext4
    nodeStageSecretRef:
      name: csi-rbd-secret-ceph-abpua
      namespace: ceph-system
    volumeAttributes:
      # abpua
      "clusterID": "xxxxxxxxxxxxxxxx"    
      "pool": "kubernetes"
      "staticVolume": "true"
      "imageFeatures": "layering"
    volumeHandle: "forgejo-repodata"
  persistentVolumeReclaimPolicy: Retain
  volumeMode: Filesystem

---
###################################################
# Network
###################################################
apiVersion: v1
kind: Service
metadata:
  name: forgejo-git
  namespace: my-git-repo
  labels: 
    app: forgejo-git
spec:
  ports:
    - name: http
      port: 3000
  selector:
    app: forgejo-git

---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name: forgejo-git-tls
  namespace: my-git-repo
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    # prevent the controller from redirecting (308) to HTTPS
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/proxy-body-size: "512m"
  
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - git.forgejo.foo.com
      secretName: tls-forgejo-git
  rules:
    - host: git.forgejo.foo.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: forgejo-git
                port:
                  number: 3000    

As you can see I added a mail configuration and my internet domain – in this example git.forgejo.foo.com. You can replace these values.

You can also adjust the other environment parameters to your needs. These are just examples.

Deplyoment

For the deployment just save the two files into a deployment directory and run:

kubectl create namespace my-git-repo
kubectl apply -f ./

As you can see from the configuraiton I disabled self-registration. To set the default admin password you can ssh the running forgejo container and run the following shell command:

su git -c "forgejo admin user create --username my-admin --password 'xxxxx' --email admin@foot.com --admin"

This will create you first admin user to login to the dashboard.

Network Access

In my deployment I use only HTTPS and disabled the SSH port 22 that is also exposed by Forgejo. But depending on your Kubernetes architecture you possible may not expose port 22. I think it is not necessary at all as the HTTPS support is very comfortable in Forgejo using API access tokens.

Conclusion

That’s it. As you can see to run Forgejo in a Kubernetes cluster is easy to achiv. If you have comments or additional ideas – let me know!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.