diff --git a/apps/backup/forgejo-backup-cronjob.yaml b/apps/backup/forgejo-backup-cronjob.yaml new file mode 100644 index 0000000..d9598f9 --- /dev/null +++ b/apps/backup/forgejo-backup-cronjob.yaml @@ -0,0 +1,80 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: forgejo-backup + namespace: backup + labels: + app: forgejo-backup +spec: + schedule: "0 3 * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 7 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: forgejo-backup + image: postgres:16-alpine + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -lc + - | + set -euo pipefail + ts=$(date -u +%Y%m%dT%H%M%SZ) + BACKUP_DIR="/data/forgejo-${ts}" + mkdir -p "${BACKUP_DIR}" + + echo "[${ts}] Starting Forgejo backup..." + + # Backup PostgreSQL database + echo "[$(date -u +%H:%M:%S)] Dumping PostgreSQL database..." + PGPASSWORD="${POSTGRES_PASSWORD}" pg_dump \ + -h forgejo-postgres.forgejo.svc.cluster.local \ + -U "${POSTGRES_USER}" \ + -d "${POSTGRES_DB}" \ + -F c \ + -f "${BACKUP_DIR}/forgejo-db.dump" + + echo "[$(date -u +%H:%M:%S)] Database backup complete: $(ls -lh ${BACKUP_DIR}/forgejo-db.dump | awk '{print $5}')" + + # Create backup manifest + echo "backup_timestamp=${ts}" > "${BACKUP_DIR}/manifest.txt" + echo "type=postgresql_custom_dump" >> "${BACKUP_DIR}/manifest.txt" + echo "database=forgejo" >> "${BACKUP_DIR}/manifest.txt" + echo "restore_cmd=PGPASSWORD= pg_restore -h -U forgejo -d forgejo -F c forgejo-db.dump" >> "${BACKUP_DIR}/manifest.txt" + + # Cleanup old backups (keep 14 days) + find /data -maxdepth 1 -type d -name "forgejo-*" -mtime +14 -exec rm -rf {} + 2>/dev/null || true + + echo "[$(date -u +%H:%M:%S)] Backup complete. Files:" + ls -lh "${BACKUP_DIR}/" + + echo "[$(date -u +%H:%M:%S)] All backups in storage:" + ls -1 /data | grep "forgejo-" + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: forgejo-backup-secret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: forgejo-backup-secret + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: forgejo-backup-secret + key: POSTGRES_DB + volumeMounts: + - name: backup-data + mountPath: /data + volumes: + - name: backup-data + persistentVolumeClaim: + claimName: platform-backup-data diff --git a/apps/forgejo/forgejo-deployment.yaml b/apps/forgejo/forgejo-deployment.yaml new file mode 100644 index 0000000..fc68376 --- /dev/null +++ b/apps/forgejo/forgejo-deployment.yaml @@ -0,0 +1,82 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forgejo + namespace: forgejo +spec: + replicas: 1 + selector: + matchLabels: + app: forgejo + template: + metadata: + labels: + app: forgejo + spec: + initContainers: + - name: init-permissions + image: busybox:latest + command: + - sh + - -c + - chown -R 1000:1000 /var/lib/gitea && chmod -R 755 /var/lib/gitea + volumeMounts: + - name: data + mountPath: /var/lib/gitea + containers: + - name: forgejo + image: codeberg.org/forgejo/forgejo:10-rootless + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 3000 + protocol: TCP + - name: ssh + containerPort: 22 + protocol: TCP + env: + - name: USER_UID + value: "1000" + - name: USER_GID + value: "1000" + - name: FORGEJO__database__DB_TYPE + value: postgres + - name: FORGEJO__database__HOST + value: forgejo-postgres:5432 + - name: FORGEJO__database__NAME + valueFrom: + secretKeyRef: + name: forgejo-postgres-secret + key: POSTGRES_DB + - name: FORGEJO__database__USER + valueFrom: + secretKeyRef: + name: forgejo-postgres-secret + key: POSTGRES_USER + - name: FORGEJO__database__PASSWD + valueFrom: + secretKeyRef: + name: forgejo-postgres-secret + key: POSTGRES_PASSWORD + - name: FORGEJO__server__DOMAIN + value: forgejo.basicstack.de + - name: FORGEJO__server__ROOT_URL + value: https://forgejo.basicstack.de + - name: FORGEJO__server__SSH_DOMAIN + value: forgejo.basicstack.de + - name: FORGEJO__service__DISABLE_REGISTRATION + value: "true" + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 2Gi + volumeMounts: + - name: data + mountPath: /var/lib/gitea + volumes: + - name: data + persistentVolumeClaim: + claimName: forgejo-data diff --git a/apps/forgejo/forgejo-ingress.yaml b/apps/forgejo/forgejo-ingress.yaml new file mode 100644 index 0000000..1e7a709 --- /dev/null +++ b/apps/forgejo/forgejo-ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: forgejo + namespace: forgejo + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" +spec: + ingressClassName: traefik + rules: + - host: forgejo.basicstack.de + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: forgejo + port: + number: 3000 + tls: + - hosts: + - forgejo.basicstack.de + secretName: forgejo-tls diff --git a/apps/forgejo/forgejo-postgres-service.yaml b/apps/forgejo/forgejo-postgres-service.yaml new file mode 100644 index 0000000..09eb546 --- /dev/null +++ b/apps/forgejo/forgejo-postgres-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: forgejo-postgres + namespace: forgejo +spec: + type: ClusterIP + selector: + app: forgejo-postgres + ports: + - port: 5432 + targetPort: 5432 + protocol: TCP diff --git a/apps/forgejo/forgejo-postgres-statefulset.yaml b/apps/forgejo/forgejo-postgres-statefulset.yaml new file mode 100644 index 0000000..2fd55ea --- /dev/null +++ b/apps/forgejo/forgejo-postgres-statefulset.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: forgejo-postgres + namespace: forgejo +spec: + serviceName: forgejo-postgres + replicas: 1 + selector: + matchLabels: + app: forgejo-postgres + template: + metadata: + labels: + app: forgejo-postgres + spec: + containers: + - name: postgres + image: postgres:16-alpine + imagePullPolicy: IfNotPresent + ports: + - name: postgres + containerPort: 5432 + protocol: TCP + envFrom: + - secretRef: + name: forgejo-postgres-secret + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 500m + memory: 1Gi + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + subPath: pgdata + volumeClaimTemplates: + - metadata: + name: postgres-data + spec: + accessModes: + - ReadWriteOnce + storageClassName: hcloud-volumes-encrypted + resources: + requests: + storage: 10Gi diff --git a/apps/forgejo/forgejo-pvc.yaml b/apps/forgejo/forgejo-pvc.yaml new file mode 100644 index 0000000..64c93d7 --- /dev/null +++ b/apps/forgejo/forgejo-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: forgejo-data + namespace: forgejo +spec: + accessModes: + - ReadWriteOnce + storageClassName: hcloud-volumes-encrypted + resources: + requests: + storage: 5Gi diff --git a/apps/forgejo/forgejo-service.yaml b/apps/forgejo/forgejo-service.yaml new file mode 100644 index 0000000..c847266 --- /dev/null +++ b/apps/forgejo/forgejo-service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: forgejo + namespace: forgejo +spec: + type: ClusterIP + selector: + app: forgejo + ports: + - name: http + port: 3000 + targetPort: 3000 + protocol: TCP + - name: ssh + port: 22 + targetPort: 2222 + protocol: TCP