Compare commits

...
6 Commits
Author SHA1 Message Date
forust 1e66b5f344 lint(postgres): 126:77 error no new line character at the end of file (new-line-at-end-of-file)
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / lint-prettier (pull_request) Successful in 7s
ci / lint-dockerfiles (pull_request) Successful in 4s
ci / validate (pull_request) Successful in 4s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 7s
renovate-ci / validate-renovate (pull_request) Successful in 7s
ci / build (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 11:56:23 +00:00
forust d638a2c1f9 chore(gite): updated deprecated access log config 2026-09-14 11:56:23 +00:00
forust b8512c6033 feat(postgres): migrate gitea to shared postgres database 2026-09-14 11:56:23 +00:00
forust 3e057ea18d feat(postgres): upgrade shared database to PostgreSQL 17
Move the shared postgres service from 15.19 to 17.6 as the postgres17 StatefulSet with its own PVC, extend the initdb and ingress policy with the statuspage database, and drop the now-unused per-app postgres manifests for authentik, gitea and netronome.
2026-09-14 11:56:23 +00:00
forustandCopilot 77113fb629 feat(postgres): migrate apps to shared database
Move Authentik and Netronome to the shared PostgreSQL service after logical dump and restore.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-14 11:56:23 +00:00
forust a3a0ab92b7 feat(postgres): add shared database deployments 2026-09-14 11:56:23 +00:00
15 changed files with 275 additions and 203 deletions

No files matched your search

+1 -1
View File
@@ -6,6 +6,6 @@ metadata:
data:
AUTHENTIK_IMAGE: ghcr.io/goauthentik/server
AUTHENTIK_TAG: "2025.10.2"
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres-service
AUTHENTIK_POSTGRESQL__HOST: postgres.database.svc.cluster.local
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_ERROR_REPORTING__ENABLED: "true"
-66
View File
@@ -1,66 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: authentik-postgres-service
namespace: authentik
spec:
clusterIP: None
selector:
app: authentik-postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: authentik-postgres-statefulset
namespace: authentik
spec:
selector:
matchLabels:
app: authentik-postgres
serviceName: authentik-postgres-service
replicas: 1
template:
metadata:
labels:
app: authentik-postgres
spec:
containers:
- name: postgres
image: docker.io/library/postgres:15.19-alpine
env:
- name: POSTGRES_DB
value: authentik
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: authentik-secrets
key: AUTHENTIK_POSTGRESQL__USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: authentik-secrets
key: AUTHENTIK_POSTGRESQL__PASSWORD
ports:
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "500m"
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
+2 -2
View File
@@ -10,13 +10,13 @@ data:
GITEA__server__SSH_PORT: "2221"
GITEA__database__DB_TYPE: "postgres"
GITEA__database__HOST: "gitea-postgres-service:5432"
GITEA__database__HOST: "postgres.database.svc.cluster.local:5432"
GITEA__database__NAME: "gitea"
GITEA__security__REVERSE_PROXY_LIMIT: "1"
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES: "*"
GITEA__mailer__ENABLED: "false"
GITEA__log__logger__access__MODE: "console, file"
GITEA__log__logger.access.MODE: "console, file"
USER_UID: "1000"
USER_GID: "1000"
-62
View File
@@ -1,62 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: gitea-postgres-service
namespace: gitea
spec:
clusterIP: None
selector:
app: gitea-postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: gitea-postgres-statefulset
namespace: gitea
spec:
selector:
matchLabels:
app: gitea-postgres
serviceName: gitea-postgres-service
replicas: 1
template:
metadata:
labels:
app: gitea-postgres
spec:
containers:
- name: gitea-postgres
image: postgres:14.24-alpine
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: gitea-secrets
key: GITEA__database__USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: gitea-secrets
key: GITEA__database__PASSWD
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: gitea-secrets
key: GITEA__database__NAME
ports:
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
+1 -1
View File
@@ -5,7 +5,7 @@ metadata:
namespace: netronome
data:
NETRONOME__DB_TYPE: "postgres"
NETRONOME__DB_HOST: "netronome-postgres-service"
NETRONOME__DB_HOST: "postgres.database.svc.cluster.local"
NETRONOME__DB_PORT: "5432"
NETRONOME__DB_NAME: "netronome"
NETRONOME__DB_SSLMODE: "disable"
-71
View File
@@ -1,71 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: netronome-postgres-service
namespace: netronome
spec:
selector:
app: netronome-postgres
ports:
- protocol: TCP
port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: netronome-postgres
namespace: netronome
spec:
serviceName: "netronome-postgres-service"
replicas: 1
selector:
matchLabels:
app: netronome-postgres
template:
metadata:
labels:
app: netronome-postgres
spec:
containers:
- name: netronome-postgres
image: postgres:17.11-alpine
ports:
- name: postgres-port
protocol: TCP
containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: netronome-secrets
key: NETRONOME__DB_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: netronome-secrets
key: NETRONOME__DB_PASSWORD
- name: POSTGRES_DB
valueFrom:
configMapKeyRef:
name: netronome-config
key: NETRONOME__DB_NAME
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
volumeMounts:
- name: netronome-pg-data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: netronome-pg-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
+5
View File
@@ -0,0 +1,5 @@
POSTGRES_ADMIN_PASSWORD=
AUTHENTIK_DB_PASSWORD=
GITEA_DB_PASSWORD=
NETRONOME_DB_PASSWORD=
PENPOT_DB_PASSWORD=
+35
View File
@@ -0,0 +1,35 @@
# Shared PostgreSQL
This directory contains a PostgreSQL 15 deployment draft for Authentik, Gitea,
Netronome, and Penpot. It creates one database and one login role per service;
it does not migrate existing data or change application connection settings.
## Compatibility baseline
| Service | Current application | Current standalone PostgreSQL | Common PostgreSQL 15 |
| --------- | ------------------- | ----------------------------: | ------------------------------------------------------------------------------------ |
| Authentik | 2025.10.2 | 15 | Supported (Authentik requires 14+) |
| Gitea | 1.27.3 | 14 | Supported (Gitea requires 12+) |
| Netronome | 0.14.0 | 17 | Validate in staging; upstream's example uses 17 but no 17-only feature is documented |
| Penpot | 2.17.2 | 15 | Supported by the official deployment |
PostgreSQL 15 is the conservative common major. A major-version downgrade or
change must use a logical dump/restore; changing only the image tag while
keeping a data directory is not supported. Back up and migrate one application
at a time, starting with Netronome because its current standalone deployment
uses PostgreSQL 17.
For Compose, copy `.env.example` to `.env`, set all passwords, and start it with
`docker compose -f shared-compose.yaml up -d`. This file is intentionally not
named `compose.yaml`, so the repository deploy workflow does not start a second
database accidentally.
Applications that use this database must also join that external network and use
`homelab-postgres:5432`.
For Kubernetes, create `k8s/secrets.yaml` from the example before applying the
manifests. The `k8s/active` marker makes the normal deploy workflow include the
namespace, StatefulSet, ConfigMap, and NetworkPolicy. Applications use
`postgres.database.svc.cluster.local:5432`.
Migrate each existing database with a tested logical dump/restore before
switching an application. Do not reuse a PostgreSQL 14 or 17 data directory
with PostgreSQL 15.
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
: "${AUTHENTIK_DB_PASSWORD:?AUTHENTIK_DB_PASSWORD is required}"
: "${GITEA_DB_PASSWORD:?GITEA_DB_PASSWORD is required}"
: "${NETRONOME_DB_PASSWORD:?NETRONOME_DB_PASSWORD is required}"
: "${PENPOT_DB_PASSWORD:?PENPOT_DB_PASSWORD is required}"
create_role_and_database() {
local role="$1"
local database="$2"
local password="$3"
psql --username "$POSTGRES_USER" --dbname postgres \
-v role="$role" -v database="$database" -v password="$password" \
<<'SQL'
SELECT format('CREATE ROLE %I LOGIN PASSWORD %L', :'role', :'password')
WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = :'role')\gexec
SELECT format('CREATE DATABASE %I OWNER %I', :'database', :'role')
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = :'database')\gexec
SQL
}
create_role_and_database authentik authentik "$AUTHENTIK_DB_PASSWORD"
create_role_and_database gitea gitea "$GITEA_DB_PASSWORD"
create_role_and_database netronome netronome "$NETRONOME_DB_PASSWORD"
create_role_and_database penpot penpot "$PENPOT_DB_PASSWORD"
View File
Whitespace-only changes.
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: database
labels:
app.kubernetes.io/part-of: homelab-database
+31
View File
@@ -0,0 +1,31 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-ingress
namespace: database
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: postgres17
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: authentik
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: gitea
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: netronome
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: penpot
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: statuspage
ports:
- protocol: TCP
port: 5432
+126
View File
@@ -0,0 +1,126 @@
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: database
labels:
app.kubernetes.io/name: postgres17
app.kubernetes.io/part-of: homelab-database
spec:
selector:
app.kubernetes.io/name: postgres17
ports:
- name: postgres
port: 5432
targetPort: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres17
namespace: database
spec:
serviceName: postgres17
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: postgres17
template:
metadata:
labels:
app.kubernetes.io/name: postgres17
spec:
containers:
- name: postgres
image: postgres:17.6-alpine
ports:
- name: postgres
containerPort: 5432
env:
- name: POSTGRES_DB
value: postgres
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-shared-secrets
key: POSTGRES_ADMIN_PASSWORD
envFrom:
- secretRef:
name: postgres-shared-secrets
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
- name: initdb
mountPath: /docker-entrypoint-initdb.d/01-create-databases.sh
subPath: 01-create-databases.sh
readinessProbe:
exec:
command: ["pg_isready", "-U", "postgres", "-d", "postgres"]
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
exec:
command: ["pg_isready", "-U", "postgres", "-d", "postgres"]
initialDelaySeconds: 30
periodSeconds: 20
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres17-data
- name: initdb
configMap:
name: postgres-initdb
defaultMode: 0755
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres17-data
namespace: database
labels:
app.kubernetes.io/name: postgres17
app.kubernetes.io/part-of: homelab-database
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: local-path-retain
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-initdb
namespace: database
data:
01-create-databases.sh: |
#!/usr/bin/env bash
set -euo pipefail
: "${AUTHENTIK_DB_PASSWORD:?AUTHENTIK_DB_PASSWORD is required}"
: "${GITEA_DB_PASSWORD:?GITEA_DB_PASSWORD is required}"
: "${NETRONOME_DB_PASSWORD:?NETRONOME_DB_PASSWORD is required}"
: "${PENPOT_DB_PASSWORD:?PENPOT_DB_PASSWORD is required}"
: "${STATUSPAGE_DB_PASSWORD:?STATUSPAGE_DB_PASSWORD is required}"
create_role_and_database() {
local role="$1"
local database="$2"
local password="$3"
psql --username "$POSTGRES_USER" --dbname postgres \
-v role="$role" -v database="$database" -v password="$password" \
<<'SQL'
SELECT format('CREATE ROLE %I LOGIN PASSWORD %L', :'role', :'password')
WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = :'role')\gexec
SELECT format('CREATE DATABASE %I OWNER %I', :'database', :'role')
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = :'database')\gexec
SQL
}
create_role_and_database authentik authentik "$AUTHENTIK_DB_PASSWORD"
create_role_and_database gitea gitea "$GITEA_DB_PASSWORD"
create_role_and_database netronome netronome "$NETRONOME_DB_PASSWORD"
create_role_and_database penpot penpot "$PENPOT_DB_PASSWORD"
create_role_and_database statuspage statuspage "$STATUSPAGE_DB_PASSWORD"
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Secret
metadata:
name: postgres-shared-secrets
namespace: database
type: Opaque
stringData:
POSTGRES_ADMIN_PASSWORD: ""
AUTHENTIK_DB_PASSWORD: ""
GITEA_DB_PASSWORD: ""
NETRONOME_DB_PASSWORD: ""
PENPOT_DB_PASSWORD: ""
STATUSPAGE_DB_PASSWORD: ""
+28
View File
@@ -0,0 +1,28 @@
services:
postgres:
image: postgres:15.19-alpine
container_name: homelab-postgres
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_ADMIN_PASSWORD:?set POSTGRES_ADMIN_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./initdb:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- database
volumes:
postgres-data:
networks:
database:
name: homelab-database