Compare commits
65
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f53aa55d76 | ||
|
|
92bd920113 | ||
|
|
8007f82d52 | ||
|
|
60b9766449 | ||
|
|
0ebb7264bc | ||
|
|
ce21c60eba | ||
|
|
53638f831d | ||
|
|
4953da2dd7 | ||
|
|
4ac65f743c | ||
|
|
8f2e9d66c8 | ||
|
|
c7d42fb90a | ||
|
|
003b1e5dca | ||
|
|
b3463705c3 | ||
|
|
52e1f50a80 | ||
|
|
cdc2f10fe8 | ||
|
|
89fbdef10e | ||
|
|
ac795feeed | ||
|
|
3af5ecd07f | ||
|
|
77be606912 | ||
|
|
4eab6a43c8 | ||
|
|
6c24d4fb17 | ||
|
|
e36595a045 | ||
|
|
e07537ff8b | ||
|
|
303eaaa71b | ||
|
|
68fb5eb45e | ||
|
|
1c58c78892 | ||
|
|
82124017c0 | ||
|
|
e502f46f43 | ||
|
|
a4f8218b5e | ||
|
|
d162a50bba | ||
|
|
9ca8514a0a | ||
|
|
6fa809a8d2 | ||
|
|
c6d8df2317 | ||
|
|
c28d7323b3 | ||
|
|
25f547ff78 | ||
|
|
50911b4ec1 | ||
|
|
663675f9a0 | ||
|
|
8b28bd24ff | ||
|
|
b5d6f75330 | ||
|
|
f5b5ecaafa | ||
|
|
7799b676ca | ||
|
|
24d3686f60 | ||
|
|
b8b3bba264 | ||
|
|
abfbc04067 | ||
|
|
23ed72826a | ||
|
|
7288058df6 | ||
|
|
6715f9e9af | ||
|
|
ccec1102ef | ||
|
|
360a6fc5dc | ||
|
|
9a806724af | ||
|
|
ed1ddaad5d | ||
|
|
726b3ee544 | ||
|
|
13309b26e0 | ||
|
|
eddc256bed | ||
|
|
52f821cbba | ||
|
|
0dbc2fff13 | ||
|
|
91ec83bc1c | ||
|
|
9fec1dae39 | ||
|
|
8fb12a2176 | ||
|
|
fe0c7067b6 | ||
|
|
d0f0843774 | ||
|
|
81a5b207ac | ||
|
|
e3d5970ae3 | ||
|
|
85f05c26cb | ||
|
|
fcc7b0d611 |
No files matched your search
@@ -61,11 +61,22 @@ jobs:
|
||||
- name: Lint YAML syntax
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -t yaml_files < <(
|
||||
git ls-files '*.yaml' '*.yml' \
|
||||
':!node_modules/**' \
|
||||
':!**/.venv/**'
|
||||
)
|
||||
|
||||
if [ "${#yaml_files[@]}" -eq 0 ]; then
|
||||
echo "No YAML files found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
cytopia/yamllint:latest \
|
||||
-c .yamllint .
|
||||
-c .yamllint "${yaml_files[@]}"
|
||||
|
||||
lint-dockerfiles:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
name: renovate-ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-renovate:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Renovate Compose draft
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
trap 'rm -f renovate/.env' EXIT
|
||||
printf '%s\n' \
|
||||
'RENOVATE_ENDPOINT=https://gitea.example/api/v1' \
|
||||
'RENOVATE_TOKEN=test-token' \
|
||||
'RENOVATE_REPOSITORIES=forust/homelab' \
|
||||
> renovate/.env
|
||||
docker compose -f renovate/renovate-compose.yaml config --quiet
|
||||
|
||||
- name: Validate Kubernetes manifests
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
ghcr.io/yannh/kubeconform:latest \
|
||||
-strict \
|
||||
-ignore-missing-schemas \
|
||||
-summary \
|
||||
renovate/k8s/namespace.yaml \
|
||||
renovate/k8s/configmap.yaml \
|
||||
renovate/k8s/cronjob.yaml
|
||||
|
||||
- name: Validate Renovate repository config
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
renovate/renovate:44.83.2 \
|
||||
renovate-config-validator renovate.json
|
||||
@@ -61,11 +61,22 @@ jobs:
|
||||
- name: Lint YAML syntax
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -t yaml_files < <(
|
||||
git ls-files '*.yaml' '*.yml' \
|
||||
':!node_modules/**' \
|
||||
':!**/.venv/**'
|
||||
)
|
||||
|
||||
if [ "${#yaml_files[@]}" -eq 0 ]; then
|
||||
echo "No YAML files found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
cytopia/yamllint:latest \
|
||||
-c .yamllint .
|
||||
-c .yamllint "${yaml_files[@]}"
|
||||
|
||||
lint-dockerfiles:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -133,6 +130,15 @@ jobs:
|
||||
echo " namespaces first: ${ns_files[*]}"
|
||||
kubectl apply -f "${ns_files[@]}"
|
||||
fi
|
||||
if [ -f "$repo/prometheus-stack/k8s/active" ]; then
|
||||
echo "== Upgrading kube-prometheus-stack =="
|
||||
helm upgrade --install prometheus-stack prometheus-community/kube-prometheus-stack \
|
||||
--namespace prometheus \
|
||||
--version 86.2.3 \
|
||||
--values "$repo/prometheus-stack/k8s/grafana-values.yaml" \
|
||||
--wait
|
||||
fi
|
||||
|
||||
if [ "${#other_files[@]}" -gt 0 ]; then
|
||||
echo " resources: ${other_files[*]}"
|
||||
kubectl apply "${prune_opts[@]}" -f "${other_files[@]}"
|
||||
|
||||
@@ -41,6 +41,7 @@ traefik/dynamic/fileservers.yml
|
||||
traefik/dynamic/*.local.y*ml.*
|
||||
traefik/dynamic/*.external.y*ml
|
||||
traefik/k8s/fileservers.y*ml
|
||||
traefik/k8s/aliasHeadersStrategy.md
|
||||
|
||||
traefik/logs/*
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
adguard:
|
||||
image: adguard/adguardhome:latest
|
||||
image: adguard/adguardhome:v0.107.79
|
||||
container_name: adguardhome
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
|
||||
@@ -65,7 +65,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: adguard
|
||||
image: adguard/adguardhome:latest
|
||||
image: adguard/adguardhome:v0.107.79
|
||||
resources:
|
||||
limits:
|
||||
memory: "1.5Gi"
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: adguard-service
|
||||
port: 3000
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
postgresql:
|
||||
image: docker.io/library/postgres:15-alpine
|
||||
image: docker.io/library/postgres:15.19-alpine
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
@@ -41,7 +41,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: authentik-server
|
||||
image: ghcr.io/goauthentik/server:2025.10.2
|
||||
image: ghcr.io/goauthentik/server:2026.8.3
|
||||
args: ["server"]
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
@@ -75,7 +75,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: authentik-worker
|
||||
image: ghcr.io/goauthentik/server:2025.10.2
|
||||
image: ghcr.io/goauthentik/server:2026.8.3
|
||||
args: ["worker"]
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
|
||||
@@ -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"
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`auth.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: authentik-server-service
|
||||
port: 9000
|
||||
|
||||
@@ -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-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
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
cloudflare-ddns:
|
||||
image: timothyjmiller/cloudflare-ddns:latest
|
||||
image: timothyjmiller/cloudflare-ddns:2.2.0
|
||||
container_name: cloudflare-ddns
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
containers:
|
||||
- name: cloudflare-ddns
|
||||
image: timothyjmiller/cloudflare-ddns:latest
|
||||
image: timothyjmiller/cloudflare-ddns:2.2.0
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
checkmk:
|
||||
image: "checkmk/check-mk-raw:2.4.0-latest"
|
||||
image: "checkmk/check-mk-raw:2.4.0-2026.09.14"
|
||||
container_name: "checkmk"
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
|
||||
@@ -7,8 +7,12 @@ spec:
|
||||
selector:
|
||||
app: checkmk
|
||||
ports:
|
||||
- port: 5000
|
||||
- name: web
|
||||
port: 5000
|
||||
targetPort: 5000
|
||||
- name: agent-receiver
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -27,14 +31,17 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: checkmk
|
||||
image: checkmk/check-mk-raw:2.4.0-latest
|
||||
image: checkmk/check-mk-raw:2.4.0-2026.09.14
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: checkmk-secrets
|
||||
- configMapRef:
|
||||
name: checkmk-config
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
- name: web
|
||||
containerPort: 5000
|
||||
- name: agent-receiver
|
||||
containerPort: 8000
|
||||
volumeMounts:
|
||||
- name: sites
|
||||
mountPath: /omd/sites
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`cmk.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: checkmk-service
|
||||
port: 5000
|
||||
@@ -16,6 +19,22 @@ spec:
|
||||
certResolver: letsencrypt
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRouteTCP
|
||||
metadata:
|
||||
name: checkmk-agent-receiver
|
||||
namespace: checkmk
|
||||
spec:
|
||||
entryPoints:
|
||||
- checkmk-agent
|
||||
routes:
|
||||
- match: HostSNI(`*`)
|
||||
services:
|
||||
- name: checkmk-service
|
||||
port: 8000
|
||||
tls:
|
||||
passthrough: true
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: checkmk-local
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
convertx:
|
||||
container_name: convertx
|
||||
image: ghcr.io/c4illin/convertx:latest
|
||||
image: ghcr.io/c4illin/convertx:v0.18.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9992:3000"
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
|
||||
bentopdf:
|
||||
container_name: bentopdf
|
||||
image: bentopdf/bentopdf:latest
|
||||
image: bentopdf/bentopdf@sha256:4eb4ec8f5030faf87c29a73d3d5a2781f28a597cf440c3ab111eb96aee550871
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
||||
@@ -26,7 +26,7 @@ spec:
|
||||
app: bentopdf
|
||||
spec:
|
||||
containers:
|
||||
- image: bentopdf/bentopdf:latest
|
||||
- image: bentopdf/bentopdf@sha256:4eb4ec8f5030faf87c29a73d3d5a2781f28a597cf440c3ab111eb96aee550871
|
||||
imagePullPolicy: Always
|
||||
name: bentopdf
|
||||
ports:
|
||||
|
||||
@@ -26,7 +26,7 @@ spec:
|
||||
app: convertx
|
||||
spec:
|
||||
containers:
|
||||
- image: ghcr.io/c4illin/convertx:latest
|
||||
- image: ghcr.io/c4illin/convertx:v0.18.0
|
||||
name: convertx
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
spec:
|
||||
plugin:
|
||||
crowdsec-bouncer:
|
||||
enabled: true
|
||||
LogLevel: INFO
|
||||
CrowdsecMode: live
|
||||
CrowdsecLapiScheme: http
|
||||
CrowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
|
||||
CrowdsecLapiKeyFile: "/etc/traefik/secrets/traefik-api-key"
|
||||
@@ -0,0 +1,57 @@
|
||||
container_runtime: containerd
|
||||
agent:
|
||||
env:
|
||||
- name: COLLECTIONS
|
||||
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios"
|
||||
- name: DISABLE_COLLECTIONS
|
||||
value: "crowdsecurity/linux crowdsecurity/sshd"
|
||||
|
||||
acquisition:
|
||||
- namespace: traefik
|
||||
podName: "*traefik*"
|
||||
program: traefik
|
||||
poll_without_inotify: true
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 100Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 500Mi
|
||||
|
||||
lapi:
|
||||
env:
|
||||
- name: COLLECTIONS
|
||||
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios"
|
||||
- name: DISABLE_COLLECTIONS
|
||||
value: "crowdsecurity/linux crowdsecurity/sshd"
|
||||
service:
|
||||
type: ClusterIP
|
||||
persistentVolume:
|
||||
data:
|
||||
enabled: true
|
||||
storageClassName: local-path-retain
|
||||
size: 1Gi
|
||||
config:
|
||||
enabled: true
|
||||
storageClassName: local-path-retain
|
||||
size: 100Mi
|
||||
storeLAPICscliCredentialsInSecret: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 150Mi
|
||||
limits:
|
||||
cpu: 400m
|
||||
memory: 500Mi
|
||||
|
||||
metrics:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
additionalLabels:
|
||||
release: prometheus-stack
|
||||
enabled: true
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
namespace: prometheus
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: crowdsec
|
||||
labels:
|
||||
app.kubernetes.io/part-of: crowdsec
|
||||
@@ -0,0 +1,27 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: crowdsec-lapi
|
||||
namespace: crowdsec
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: crowdsec
|
||||
type: lapi
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: traefik
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: traefik
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
k8s-app: crowdsec
|
||||
type: agent
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
dockmon:
|
||||
image: darthnorse/dockmon:latest
|
||||
image: darthnorse/dockmon:2.4.5
|
||||
container_name: dockmon
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
|
||||
@@ -29,7 +29,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: dockmon
|
||||
image: darthnorse/dockmon:latest
|
||||
image: darthnorse/dockmon:2.4.5
|
||||
ports:
|
||||
- containerPort: 443
|
||||
volumeMounts:
|
||||
|
||||
@@ -18,6 +18,8 @@ spec:
|
||||
- match: Host(`dockmon.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
- name: security-headers@file
|
||||
services:
|
||||
- name: dockmon-service
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
downtify:
|
||||
container_name: downtify
|
||||
image: ghcr.io/henriquesebastiao/downtify:latest
|
||||
image: ghcr.io/henriquesebastiao/downtify:2.13.0
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - '7077:8000'
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: downtify
|
||||
image: ghcr.io/henriquesebastiao/downtify:latest
|
||||
image: ghcr.io/henriquesebastiao/downtify:2.13.0
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
volumeMounts:
|
||||
|
||||
@@ -10,6 +10,8 @@ spec:
|
||||
- match: Host(`downtify.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
- name: security-chain@file
|
||||
services:
|
||||
- name: downtify-service
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
redis:
|
||||
image: redis:alpine
|
||||
image: redis:8.10.1-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
retries: 5
|
||||
|
||||
playwright-service:
|
||||
image: mcr.microsoft.com/playwright:v1.56.0-jammy
|
||||
image: mcr.microsoft.com/playwright:v1.63.0-jammy
|
||||
restart: unless-stopped
|
||||
command: npx -y playwright@1.56.0 run-server --port 3000 --path /ws
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: playwright
|
||||
image: mcr.microsoft.com/playwright:v1.56.0-jammy
|
||||
image: mcr.microsoft.com/playwright:v1.63.0-jammy
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- npx
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: edu-master
|
||||
labels:
|
||||
app: edu-master-redis
|
||||
spec:
|
||||
serviceName: redis
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
@@ -17,7 +18,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:alpine
|
||||
image: redis:8.10.1-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
initContainers:
|
||||
- name: wait-redis
|
||||
image: redis:alpine
|
||||
image: redis:8.10.1-alpine
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
# redis healthy -> session-keeper healthy (EXISTS EDU_PHPSESSID) -> playwright started
|
||||
initContainers:
|
||||
- name: wait-deps
|
||||
image: redis:alpine
|
||||
image: redis:8.10.1-alpine
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
@@ -55,8 +55,8 @@ spec:
|
||||
value: "Europe/Kyiv"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 25m
|
||||
memory: 128Mi
|
||||
cpu: "50m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: 300m
|
||||
memory: 384Mi
|
||||
cpu: "600m"
|
||||
memory: "512Mi"
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
server:
|
||||
image: docker.gitea.com/gitea:1.26
|
||||
image: docker.gitea.com/gitea:1.27.3
|
||||
container_name: gitea
|
||||
restart: always
|
||||
environment:
|
||||
@@ -61,7 +61,7 @@ services:
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: docker.io/library/postgres:14
|
||||
image: docker.io/library/postgres:14.24-alpine
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_USER=gitea
|
||||
|
||||
@@ -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"
|
||||
@@ -31,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: gitea
|
||||
image: docker.gitea.com/gitea:1.26
|
||||
image: docker.gitea.com/gitea:1.27.3
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: gitea-config
|
||||
|
||||
@@ -9,11 +9,17 @@ spec:
|
||||
routes:
|
||||
- match: Host(`gitea.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: gitea-service
|
||||
port: 3000
|
||||
- match: Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: gitea-service
|
||||
port: 3000
|
||||
|
||||
@@ -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
|
||||
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__USER
|
||||
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
|
||||
@@ -7,3 +7,4 @@ type: Opaque
|
||||
stringData:
|
||||
GITEA__database__USER: "gitea"
|
||||
GITEA__database__PASSWD: "gitea"
|
||||
GITEA__database__NAME: "gitea"
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
glance:
|
||||
container_name: glance
|
||||
image: glanceapp/glance
|
||||
image: glanceapp/glance:v0.8.6
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config:ro
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: glance
|
||||
image: glanceapp/glance
|
||||
image: glanceapp/glance:v0.8.6
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: glance-secrets
|
||||
@@ -56,11 +56,11 @@ spec:
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
memory: "30Mi"
|
||||
cpu: "20m"
|
||||
limits:
|
||||
memory: "100Mi"
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
limits:
|
||||
cpu: "200m"
|
||||
memory: "256Mi"
|
||||
volumes:
|
||||
- name: glance-config
|
||||
configMap:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
headscale:
|
||||
image: headscale/headscale:latest
|
||||
image: headscale/headscale:0.29.3
|
||||
restart: unless-stopped
|
||||
container_name: headscale-server
|
||||
command: serve
|
||||
@@ -53,7 +53,7 @@ services:
|
||||
- "traefik.http.routers.headscale-metrics-dev.service=headscale-metrics"
|
||||
- "traefik.http.routers.headscale-metrics-dev.tls=true"
|
||||
headplane:
|
||||
image: ghcr.io/tale/headplane:latest
|
||||
image: ghcr.io/tale/headplane:0.7.1
|
||||
container_name: headplane
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
@@ -100,7 +100,7 @@ services:
|
||||
- "traefik.http.routers.headplane-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.headplane-dev.tls=true"
|
||||
web:
|
||||
image: goodieshq/headscale-admin:latest
|
||||
image: goodieshq/headscale-admin:0.28.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 10080:80
|
||||
|
||||
@@ -23,11 +23,17 @@ spec:
|
||||
port: 8080
|
||||
- match: Host(`hs.forust.xyz`) && PathPrefix(`/admin`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: headscale-ui-external
|
||||
port: 80
|
||||
- match: Host(`hs.forust.xyz`) && PathPrefix(`/metrics`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: headscale-server-external
|
||||
port: 9090
|
||||
@@ -47,6 +53,8 @@ spec:
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: headplane-prefix
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: headplane-external
|
||||
port: 3000
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`forust.xyz`) || Host(`www.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
priority: 10
|
||||
services:
|
||||
- name: forust-homepage-service
|
||||
@@ -43,6 +46,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`xdfnx.cfd`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: xdfnx-homepage-service
|
||||
port: 80
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
kener:
|
||||
image: rajnandan1/kener:4.0.23
|
||||
image: rajnandan1/kener:4.1.5
|
||||
container_name: kener
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
@@ -36,7 +36,7 @@ services:
|
||||
- proxy
|
||||
- kener
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
image: redis:8.10.1-alpine
|
||||
container_name: kener-redis
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`status.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: kener-service
|
||||
port: 3000
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kener
|
||||
image: rajnandan1/kener:4.1.0
|
||||
image: rajnandan1/kener:4.1.5
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: kener-config
|
||||
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7-alpine
|
||||
image: redis:8.10.1-alpine
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
volumeMounts:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
metube:
|
||||
image: ghcr.io/alexta69/metube
|
||||
image: ghcr.io/alexta69/metube:2026.09.15
|
||||
container_name: metube
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: metube
|
||||
image: ghcr.io/alexta69/metube
|
||||
image: ghcr.io/alexta69/metube:2026.09.15
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: metube-config
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n
|
||||
image: docker.n8n.io/n8nio/n8n:2.40.2
|
||||
container_name: n8n
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`n8n.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: n8n-service
|
||||
port: 5678
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: n8n
|
||||
image: docker.n8n.io/n8nio/n8n
|
||||
image: docker.n8n.io/n8nio/n8n:2.40.2
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: n8n-config
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
netronome:
|
||||
image: ghcr.io/autobrr/netronome:latest
|
||||
image: ghcr.io/autobrr/netronome:v0.14.0
|
||||
restart: unless-stopped
|
||||
container_name: netronome
|
||||
ports:
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
container_name: netronome-postgres
|
||||
image: postgres:17-alpine
|
||||
image: postgres:17.11-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=netronome
|
||||
- POSTGRES_PASSWORD=netronome
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`nm.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: netronome-service
|
||||
port: 7575
|
||||
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: netronome
|
||||
image: ghcr.io/autobrr/netronome:latest
|
||||
image: ghcr.io/autobrr/netronome:v0.14.0
|
||||
ports:
|
||||
- name: netronome-port
|
||||
protocol: TCP
|
||||
|
||||
@@ -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-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
|
||||
@@ -12,6 +12,8 @@ spec:
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: nextcloud-chain@file
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: nextcloud-apache
|
||||
port: 11000
|
||||
@@ -30,6 +32,8 @@ spec:
|
||||
- match: Host(`nextcloud.workstation.internal`) || Host(`nextcloud.gigaforust.internal`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
- name: nextcloud-chain@file
|
||||
services:
|
||||
- name: nextcloud-apache
|
||||
|
||||
+5
-5
@@ -84,7 +84,7 @@ services:
|
||||
# - "443:443"
|
||||
|
||||
penpot-frontend:
|
||||
image: "penpotapp/frontend:${PENPOT_VERSION:-latest}"
|
||||
image: "penpotapp/frontend:${PENPOT_VERSION:-2.17.2}"
|
||||
restart: always
|
||||
# ports:
|
||||
# - 9001:8080
|
||||
@@ -119,7 +119,7 @@ services:
|
||||
environment:
|
||||
<<: [*penpot-flags, *penpot-http-body-size]
|
||||
penpot-backend:
|
||||
image: "penpotapp/backend:${PENPOT_VERSION:-latest}"
|
||||
image: "penpotapp/backend:${PENPOT_VERSION:-2.17.2}"
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
@@ -189,7 +189,7 @@ services:
|
||||
# PENPOT_SMTP_SSL: false
|
||||
|
||||
penpot-exporter:
|
||||
image: "penpotapp/exporter:${PENPOT_VERSION:-latest}"
|
||||
image: "penpotapp/exporter:${PENPOT_VERSION:-2.17.2}"
|
||||
restart: always
|
||||
|
||||
depends_on:
|
||||
@@ -209,7 +209,7 @@ services:
|
||||
PENPOT_REDIS_URI: redis://penpot-valkey/0
|
||||
|
||||
penpot-postgres:
|
||||
image: "postgres:15"
|
||||
image: "postgres:15.19-alpine"
|
||||
restart: always
|
||||
stop_signal: SIGINT
|
||||
|
||||
@@ -233,7 +233,7 @@ services:
|
||||
- POSTGRES_PASSWORD=penpot
|
||||
|
||||
penpot-valkey:
|
||||
image: valkey/valkey:8.1
|
||||
image: valkey/valkey:9.1.2
|
||||
restart: always
|
||||
|
||||
healthcheck:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
portainer:
|
||||
image: portainer/portainer-ce:2.41.0
|
||||
image: portainer/portainer-ce:2.45.1
|
||||
container_name: portainer
|
||||
restart: always
|
||||
volumes:
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`portainer.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: portainer-service
|
||||
port: 9000
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: portainer
|
||||
image: portainer/portainer-ce:2.41.0
|
||||
image: portainer/portainer-ce:2.45.1
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
volumeMounts:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
POSTGRES_ADMIN_PASSWORD=
|
||||
AUTHENTIK_DB_PASSWORD=
|
||||
GITEA_DB_PASSWORD=
|
||||
NETRONOME_DB_PASSWORD=
|
||||
PENPOT_DB_PASSWORD=
|
||||
@@ -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.
|
||||
Executable
+27
@@ -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"
|
||||
Whitespace-only changes.
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: database
|
||||
labels:
|
||||
app.kubernetes.io/part-of: homelab-database
|
||||
@@ -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
|
||||
@@ -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.11-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"
|
||||
@@ -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: ""
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana:11.6.0
|
||||
image: grafana/grafana:13.2.2
|
||||
container_name: prometheus-grafana
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
@@ -30,7 +30,7 @@ services:
|
||||
- proxy
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:v3.2.1
|
||||
image: prom/prometheus:v3.14.0
|
||||
container_name: prometheus-prometheus
|
||||
restart: unless-stopped
|
||||
command:
|
||||
@@ -44,7 +44,7 @@ services:
|
||||
- proxy
|
||||
|
||||
alertmanager:
|
||||
image: prom/alertmanager:v0.28.1
|
||||
image: prom/alertmanager:v0.34.1
|
||||
container_name: prometheus-alertmanager
|
||||
restart: unless-stopped
|
||||
command:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
route:
|
||||
receiver: telegram
|
||||
group_by:
|
||||
- alertname
|
||||
- namespace
|
||||
group_wait: 30s
|
||||
group_interval: 5m
|
||||
repeat_interval: 12h
|
||||
routes:
|
||||
- receiver: null
|
||||
matchers:
|
||||
- alertname="InfoInhibitor"
|
||||
- receiver: null
|
||||
matchers:
|
||||
- alertname="Watchdog"
|
||||
|
||||
inhibit_rules:
|
||||
- source_matchers:
|
||||
- severity="critical"
|
||||
target_matchers:
|
||||
- severity=~"warning|info"
|
||||
equal:
|
||||
- namespace
|
||||
- source_matchers:
|
||||
- severity="warning"
|
||||
target_matchers:
|
||||
- severity="info"
|
||||
equal:
|
||||
- namespace
|
||||
|
||||
receivers:
|
||||
- name: telegram
|
||||
telegram_configs:
|
||||
- bot_token: REPLACE_WITH_TELEGRAM_BOT_TOKEN
|
||||
chat_id: REPLACE_WITH_TELEGRAM_CHAT_ID
|
||||
parse_mode: HTML
|
||||
send_resolved: true
|
||||
- name: null
|
||||
@@ -0,0 +1,68 @@
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: homelab-infrastructure
|
||||
namespace: prometheus
|
||||
labels:
|
||||
release: prometheus-stack
|
||||
spec:
|
||||
groups:
|
||||
- name: homelab.infrastructure
|
||||
rules:
|
||||
- alert: TargetDown
|
||||
expr: up == 0
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Prometheus target is down"
|
||||
description: "{{ $labels.job }} target {{ $labels.instance }} has been down for more than 10 minutes."
|
||||
|
||||
- alert: PodCrashLooping
|
||||
expr: max_over_time(kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"}[10m]) >= 1
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Pod is crash looping"
|
||||
description: "Container {{ $labels.container }} in {{ $labels.namespace }}/{{ $labels.pod }} is in CrashLoopBackOff."
|
||||
|
||||
- alert: PersistentVolumeClaimFillingUp
|
||||
expr: kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes < 0.15
|
||||
for: 15m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "PVC has less than 15% free space"
|
||||
description: "{{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} has less than 15% free space."
|
||||
|
||||
- alert: CPUThrottlingHigh
|
||||
expr: |
|
||||
sum without (id, metrics_path, name, image, endpoint, job, node) (
|
||||
topk by (cluster, namespace, pod, container, instance) (1,
|
||||
increase(container_cpu_cfs_throttled_periods_total{container!="", job="kubelet", metrics_path="/metrics/cadvisor", }[5m])
|
||||
)
|
||||
)
|
||||
/ on (cluster, namespace, pod, container, instance) group_left
|
||||
sum without (id, metrics_path, name, image, endpoint, job, node) (
|
||||
topk by (cluster, namespace, pod, container, instance) (1,
|
||||
increase(container_cpu_cfs_periods_total{job="kubelet", metrics_path="/metrics/cadvisor", }[5m])
|
||||
)
|
||||
)
|
||||
> ( 50 / 100 )
|
||||
for: 30m
|
||||
labels:
|
||||
severity: info
|
||||
annotations:
|
||||
summary: "Processes experience elevated CPU throttling"
|
||||
description: "{{ $value | humanizePercentage }} throttling of CPU in namespace {{ $labels.namespace }} for container {{ $labels.container }} in pod {{ $labels.pod }} on cluster {{ $labels.cluster }}."
|
||||
runbook_url: "https://runbooks.prometheus-operator.dev/runbooks/kubernetes/cputhrottlinghigh"
|
||||
|
||||
- alert: NodeMemoryPressure
|
||||
expr: 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 90
|
||||
for: 15m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Node memory pressure"
|
||||
description: "Node {{ $labels.instance }} has used more than 90% of memory for 15 minutes."
|
||||
@@ -14,7 +14,8 @@ grafana:
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 10Gi
|
||||
storageClassName: local-path-retain
|
||||
size: 20Gi
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
@@ -24,10 +25,12 @@ grafana:
|
||||
|
||||
prometheus:
|
||||
prometheusSpec:
|
||||
retention: 60d
|
||||
retentionSize: 32GB
|
||||
storageSpec:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "local-path"
|
||||
storageClassName: "local-path-retain"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
@@ -41,12 +44,17 @@ prometheus:
|
||||
memory: "2Gi"
|
||||
alertmanager:
|
||||
alertmanagerSpec:
|
||||
configSecret: alertmanager-config
|
||||
storage:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: local-path-retain
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
storage: 10Gi
|
||||
|
||||
defaultRules:
|
||||
disabled:
|
||||
CPUThrottlingHigh: true
|
||||
@@ -10,6 +10,8 @@ spec:
|
||||
- match: Host(`grafana.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
- name: "security-chain@file"
|
||||
services:
|
||||
- name: prometheus-stack-grafana
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: traefik
|
||||
namespace: prometheus
|
||||
labels:
|
||||
release: prometheus-stack
|
||||
spec:
|
||||
groups:
|
||||
- name: traefik
|
||||
rules:
|
||||
- alert: TraefikDown
|
||||
expr: absent(up{job="traefik"})
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Traefik metrics are unavailable"
|
||||
description: "Prometheus has no Traefik target."
|
||||
|
||||
- alert: TraefikConfigReloadFailed
|
||||
expr: traefik_config_last_reload_success == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Traefik configuration reload failed"
|
||||
description: "Traefik failed to apply its last configuration reload. Check Traefik logs and the dynamic config sources."
|
||||
|
||||
- alert: TraefikServiceHigh5xxRate
|
||||
expr: |
|
||||
sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service)
|
||||
/ sum(rate(traefik_service_requests_total[5m])) by (service) * 100 > 5
|
||||
and sum(rate(traefik_service_requests_total[5m])) by (service) > 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High 5xx error rate for service {{ $labels.service }}"
|
||||
description: "Service {{ $labels.service }} is returning 5xx errors for more than 5% of requests over the last 5 minutes (current: {{ $value | humanizePercentage }})."
|
||||
|
||||
- alert: TraefikServiceHighLatency
|
||||
expr: |
|
||||
histogram_quantile(0.95,
|
||||
sum(rate(traefik_service_request_duration_seconds_bucket[5m])) by (le, service)) > 2
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High latency for service {{ $labels.service }}"
|
||||
description: "P95 latency of {{ $labels.service }} exceeded 2 seconds over the last 5 minutes (current: {{ $value | humanizeDuration }})."
|
||||
|
||||
- alert: TraefikCertExpiringSoon
|
||||
expr: min(traefik_tls_certs_not_after) - time() < 14 * 24 * 60 * 60
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Traefik TLS certificate expires soon"
|
||||
description: "A TLS certificate managed by Traefik expires in less than 14 days (in {{ $value | humanizeDuration }})."
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:recommended"],
|
||||
"enabledManagers": ["docker-compose", "kubernetes"],
|
||||
"kubernetes": {
|
||||
"managerFilePatterns": ["/k8s/.+\\.ya?ml$/"]
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "Keep private homelab images unchanged",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"description": "Require approval for major upgrades",
|
||||
"matchUpdateTypes": ["major"],
|
||||
"dependencyDashboardApproval": true,
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"description": "Group container patch updates",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchUpdateTypes": ["patch"],
|
||||
"groupName": "container patch updates"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
RENOVATE_ENDPOINT=https://gitea.forust.xyz/api/v1
|
||||
RENOVATE_TOKEN=
|
||||
RENOVATE_REPOSITORIES=forust/homelab
|
||||
LOG_LEVEL=info
|
||||
@@ -0,0 +1,59 @@
|
||||
# Renovate for Gitea
|
||||
|
||||
Renovate runs as a Kubernetes CronJob and creates container image update pull
|
||||
requests in Gitea. It does not deploy changes itself.
|
||||
|
||||
## Kubernetes
|
||||
|
||||
Create a dedicated Gitea user named `renovate-bot`, create a repository access
|
||||
token, and grant it repository read/write plus issue read/write permissions.
|
||||
Add `read:packages` if Renovate must inspect private Gitea registry images.
|
||||
|
||||
Create the ignored Secret locally; never commit the PAT:
|
||||
|
||||
```sh
|
||||
cp renovate/k8s/secrets.yaml.example renovate/k8s/secrets.yaml
|
||||
$EDITOR renovate/k8s/secrets.yaml
|
||||
kubectl apply -f renovate/k8s/namespace.yaml
|
||||
kubectl apply -f renovate/k8s/secrets.yaml
|
||||
kubectl apply -f renovate/k8s/configmap.yaml
|
||||
kubectl apply -f renovate/k8s/cronjob.yaml
|
||||
```
|
||||
|
||||
The `renovate/k8s/active` marker makes the normal deployment workflow include
|
||||
the namespace, ConfigMap, and CronJob. The Secret is intentionally excluded
|
||||
from Git and must be applied separately after every new cluster.
|
||||
|
||||
Run it immediately instead of waiting for the six-hour schedule:
|
||||
|
||||
```sh
|
||||
kubectl create job --from=cronjob/renovate renovate-manual-$(date +%s) -n renovate
|
||||
```
|
||||
|
||||
Inspect runs with:
|
||||
|
||||
```sh
|
||||
kubectl get cronjob,jobs,pods -n renovate
|
||||
kubectl logs -n renovate job/<job-name>
|
||||
```
|
||||
|
||||
`RENOVATE_GITHUB_COM_TOKEN` is optional but recommended for changelogs and
|
||||
GitHub API rate limits. Set it in the Kubernetes Secret if available.
|
||||
|
||||
## Compose
|
||||
|
||||
Copy `.env.example` to `.env`, set the PAT, and run:
|
||||
|
||||
```sh
|
||||
docker compose -f renovate-compose.yaml run --rm renovate
|
||||
```
|
||||
|
||||
The Compose file is intentionally named `renovate-compose.yaml`, so the
|
||||
repository's automatic deployment discovery does not start it accidentally.
|
||||
|
||||
## How updates flow
|
||||
|
||||
Renovate scans both `compose.yaml` files and Kubernetes manifests, opens a
|
||||
branch and PR with image tag changes, and waits for CI. After merge, the
|
||||
existing deployment workflow applies Kubernetes changes or redeploys Compose
|
||||
stacks. Renovate never updates running workloads directly.
|
||||
@@ -0,0 +1,41 @@
|
||||
module.exports = {
|
||||
platform: 'gitea',
|
||||
endpoint: process.env.RENOVATE_ENDPOINT,
|
||||
enabledManagers: ['docker-compose', 'kubernetes'],
|
||||
kubernetes: {
|
||||
managerFilePatterns: ['/k8s/.+\\.ya?ml$/'],
|
||||
},
|
||||
repositories: (process.env.RENOVATE_REPOSITORIES || '')
|
||||
.split(',')
|
||||
.map((repository) => repository.trim())
|
||||
.filter(Boolean),
|
||||
onboarding: false,
|
||||
requireConfig: 'optional',
|
||||
autodiscover: false,
|
||||
dependencyDashboard: true,
|
||||
prCreation: 'immediate',
|
||||
labels: ['dependencies', 'automated'],
|
||||
extends: [
|
||||
'config:recommended',
|
||||
':dependencyDashboard',
|
||||
],
|
||||
packageRules: [
|
||||
{
|
||||
description: 'Do not update private homelab images',
|
||||
matchDatasources: ['docker'],
|
||||
matchPackageNames: ['/gcr\\.forust\\.xyz\\/forust\\/.+/'],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
description: 'Keep major upgrades manual',
|
||||
matchUpdateTypes: ['major'],
|
||||
dependencyDashboardApproval: true,
|
||||
automerge: false,
|
||||
},
|
||||
{
|
||||
description: 'Group patch updates',
|
||||
matchUpdateTypes: ['patch'],
|
||||
groupName: 'container patch updates',
|
||||
},
|
||||
],
|
||||
};
|
||||
Whitespace-only changes.
@@ -0,0 +1,45 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: renovate-config
|
||||
namespace: renovate
|
||||
data:
|
||||
config.js: |
|
||||
module.exports = {
|
||||
platform: 'gitea',
|
||||
endpoint: process.env.RENOVATE_ENDPOINT,
|
||||
enabledManagers: ['docker-compose', 'kubernetes'],
|
||||
kubernetes: {
|
||||
managerFilePatterns: ['/k8s/.+\\.ya?ml$/'],
|
||||
},
|
||||
repositories: (process.env.RENOVATE_REPOSITORIES || '')
|
||||
.split(',')
|
||||
.map((repository) => repository.trim())
|
||||
.filter(Boolean),
|
||||
onboarding: false,
|
||||
requireConfig: 'optional',
|
||||
autodiscover: false,
|
||||
dependencyDashboard: true,
|
||||
prCreation: 'immediate',
|
||||
labels: ['dependencies', 'automated'],
|
||||
extends: ['config:recommended', ':dependencyDashboard'],
|
||||
packageRules: [
|
||||
{
|
||||
description: 'Do not update private homelab images',
|
||||
matchDatasources: ['docker'],
|
||||
matchPackageNames: ['/gcr\\.forust\\.xyz\\/forust\\/.+/'],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
description: 'Keep major upgrades manual',
|
||||
matchUpdateTypes: ['major'],
|
||||
dependencyDashboardApproval: true,
|
||||
automerge: false,
|
||||
},
|
||||
{
|
||||
description: 'Group patch updates',
|
||||
matchUpdateTypes: ['patch'],
|
||||
groupName: 'container patch updates',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: renovate
|
||||
namespace: renovate
|
||||
spec:
|
||||
schedule: "17 */6 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 2
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 1
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: renovate
|
||||
image: renovate/renovate:44.97.5
|
||||
env:
|
||||
- name: RENOVATE_PLATFORM
|
||||
value: gitea
|
||||
- name: RENOVATE_ENDPOINT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: renovate-secrets
|
||||
key: RENOVATE_ENDPOINT
|
||||
- name: RENOVATE_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: renovate-secrets
|
||||
key: RENOVATE_TOKEN
|
||||
- name: RENOVATE_REPOSITORIES
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: renovate-secrets
|
||||
key: RENOVATE_REPOSITORIES
|
||||
- name: RENOVATE_CONFIG_FILE
|
||||
value: /opt/renovate/config.js
|
||||
- name: RENOVATE_BASE_DIR
|
||||
value: /tmp/renovate
|
||||
- name: RENOVATE_GITHUB_COM_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: renovate-secrets
|
||||
key: RENOVATE_GITHUB_COM_TOKEN
|
||||
optional: true
|
||||
- name: LOG_LEVEL
|
||||
value: info
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /opt/renovate/config.js
|
||||
subPath: config.js
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: renovate-config
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: renovate
|
||||
labels:
|
||||
app.kubernetes.io/part-of: renovate
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: renovate-secrets
|
||||
namespace: renovate
|
||||
type: Opaque
|
||||
stringData:
|
||||
RENOVATE_TOKEN: ""
|
||||
RENOVATE_ENDPOINT: "https://gitea.forust.xyz/api/v1"
|
||||
RENOVATE_REPOSITORIES: "forust/homelab"
|
||||
RENOVATE_GITHUB_COM_TOKEN: ""
|
||||
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
renovate:
|
||||
image: renovate/renovate:44.83.2
|
||||
container_name: renovate
|
||||
restart: "no"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
RENOVATE_PLATFORM: gitea
|
||||
RENOVATE_ENDPOINT: ${RENOVATE_ENDPOINT:?set RENOVATE_ENDPOINT}
|
||||
RENOVATE_TOKEN: ${RENOVATE_TOKEN:?set RENOVATE_TOKEN}
|
||||
RENOVATE_REPOSITORIES: ${RENOVATE_REPOSITORIES:?set RENOVATE_REPOSITORIES}
|
||||
RENOVATE_CONFIG_FILE: /opt/renovate/config.js
|
||||
RENOVATE_BASE_DIR: /tmp/renovate
|
||||
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||
volumes:
|
||||
- ./config.js:/opt/renovate/config.js:ro
|
||||
@@ -3,7 +3,7 @@
|
||||
services:
|
||||
core:
|
||||
container_name: searxng-core
|
||||
image: docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}
|
||||
image: docker.io/searxng/searxng:${SEARXNG_VERSION:-2026.09.13-d4ce87c23}
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - ${SEARXNG_PORT:-8080}
|
||||
@@ -31,7 +31,7 @@ services:
|
||||
|
||||
valkey:
|
||||
container_name: searxng-valkey
|
||||
image: docker.io/valkey/valkey:9-alpine
|
||||
image: docker.io/valkey/valkey:9.1.2-alpine
|
||||
command: valkey-server --save 30 1 --loglevel warning
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`s.forust.xyz`) || Host(`search.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: searxng-service
|
||||
port: 8080
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: searxng
|
||||
image: docker.io/searxng/searxng:latest
|
||||
image: docker.io/searxng/searxng:2026.09.13-d4ce87c23
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: searxng-config
|
||||
|
||||
@@ -29,7 +29,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: valkey
|
||||
image: docker.io/valkey/valkey:9-alpine
|
||||
image: docker.io/valkey/valkey:9.1.2-alpine
|
||||
command:
|
||||
- valkey-server
|
||||
- --save
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
image: ghcr.io/lukegus/termix:2.7.1
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
|
||||
@@ -9,6 +9,9 @@ spec:
|
||||
routes:
|
||||
- match: Host(`termix.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
services:
|
||||
- name: termix-service
|
||||
port: 8080
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: termix
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
image: ghcr.io/lukegus/termix:2.7.1
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: termix-config
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.7.4
|
||||
image: traefik:v3.7.13
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
command:
|
||||
|
||||
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user