Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff83daed1e | ||
|
|
080ae343e6 | ||
|
|
8d3185f8ab | ||
|
|
ff40a71145 | ||
|
|
cc9c3dea88 | ||
|
|
815cd85b9a | ||
|
|
9021eddbc3 | ||
|
|
2adf17c307 | ||
|
|
1add5b5cd7 | ||
|
|
34f10211ab | ||
|
|
02447f2946 | ||
|
|
8799962b1c | ||
|
|
fb80024fa2 | ||
|
|
0f1a788874 | ||
|
|
39df442e60 | ||
|
|
62a451773e | ||
|
|
f196099491 | ||
|
|
66502b8279 | ||
|
|
9018c091fa | ||
|
|
114608af2f | ||
|
|
51a73fb213 |
No files matched your search
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
# Local regressions only: kubectl is mocked and Docker is used for config parsing.
|
||||
set -euo pipefail
|
||||
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
scratch="$(mktemp -d)"
|
||||
trap 'rm -rf "$scratch"' EXIT
|
||||
|
||||
mkdir -p "$scratch/repo/app" "$scratch/repo/postgres" "$scratch/repo/netbird" "$scratch/repo/renovate"
|
||||
git -C "$scratch/repo" init -q
|
||||
for file in app/compose.yaml postgres/shared-compose.yaml netbird/client.compose.yaml renovate/renovate-compose.yaml; do
|
||||
touch "$scratch/repo/$file"
|
||||
done
|
||||
git -C "$scratch/repo" add .
|
||||
# shellcheck source=../workflows/compose-lint.sh
|
||||
source "$repo/.gitea/workflows/compose-lint.sh"
|
||||
actual="$(cd "$scratch/repo" && compose_files)"
|
||||
expected=$'app/compose.yaml\nnetbird/client.compose.yaml\npostgres/shared-compose.yaml\nrenovate/renovate-compose.yaml'
|
||||
[ "$actual" = "$expected" ] || { echo 'Compose discovery missed a file' >&2; exit 1; }
|
||||
|
||||
cat >"$scratch/compose.yaml" <<'YAML'
|
||||
services:
|
||||
example:
|
||||
image: busybox:1.37.0
|
||||
environment:
|
||||
REQUIRED: ${HOMELAB_TEST_REQUIRED:?required for this regression}
|
||||
YAML
|
||||
unset HOMELAB_TEST_REQUIRED
|
||||
if validate_compose_file "$scratch/compose.yaml" >"$scratch/config.log" 2>&1; then
|
||||
echo 'Full Compose validation accepted a missing variable' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'required for this regression' "$scratch/config.log"
|
||||
HOMELAB_TEST_REQUIRED=present validate_compose_file "$scratch/compose.yaml"
|
||||
|
||||
cat >"$scratch/resources.json" <<'JSON'
|
||||
{"kind":"List","items":[
|
||||
{"kind":"Deployment","metadata":{"namespace":"app"},"spec":{"template":{"spec":{
|
||||
"containers":[{"envFrom":[{"secretRef":{"name":"credentials"}},{"secretRef":{"name":"optional","optional":true}}],"env":[{"valueFrom":{"secretKeyRef":{"name":"credentials","key":"password"}}}]}],
|
||||
"initContainers":[{"envFrom":[{"secretRef":{"name":"init"}}]}],
|
||||
"imagePullSecrets":[{"name":"registry"}],
|
||||
"volumes":[{"secret":{"secretName":"mounted"}},{"projected":{"sources":[{"secret":{"name":"projected"}},{"secret":{"name":"optional-projected","optional":true}}]}}]
|
||||
}}}},
|
||||
{"kind":"CronJob","metadata":{},"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"envFrom":[{"secretRef":{"name":"cron"}}]}]}}}}}},
|
||||
{"kind":"IngressRoute","metadata":{"namespace":"app"},"spec":{"tls":{"secretName":"controller-issued-tls"}}}
|
||||
]}
|
||||
JSON
|
||||
actual="$(jq -r -f "$repo/.gitea/workflows/secret-references.jq" "$scratch/resources.json" | sort)"
|
||||
expected=$'app credentials\napp init\napp mounted\napp projected\napp registry\ndefault cron'
|
||||
[ "$actual" = "$expected" ] || { echo "Unexpected Secret references: $actual" >&2; exit 1; }
|
||||
|
||||
REPO="$repo"
|
||||
# shellcheck source=../workflows/deploy-lib.sh
|
||||
source "$repo/.gitea/workflows/deploy-lib.sh"
|
||||
K8S_MANIFESTS=("$scratch/resources.json")
|
||||
KUSTOMIZE_APPS=()
|
||||
# No live cluster access. Reject credentials in app even if they exist elsewhere.
|
||||
kubectl() {
|
||||
case "$1" in
|
||||
create) cat "$scratch/resources.json" ;;
|
||||
get)
|
||||
if [ "$3" = credentials ] && [ "$5" = app ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
*) echo "Unexpected kubectl invocation: $*" >&2; return 1 ;;
|
||||
esac
|
||||
}
|
||||
if check_referenced_secrets >"$scratch/secrets.log"; then
|
||||
echo 'Namespace-scoped Secret check accepted a missing Secret' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'MISSING OR UNREADABLE: app/credentials' "$scratch/secrets.log"
|
||||
# API/rendering errors must not produce an empty reference list and pass.
|
||||
kubectl() { return 1; }
|
||||
if check_referenced_secrets >"$scratch/secrets.log"; then
|
||||
echo 'Secret check accepted a failed manifest render' >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' 'Deploy validation regressions passed.'
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh shellcheck)"
|
||||
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh shellcheck jq)"
|
||||
export PATH="$tools_dir:$PATH"
|
||||
mapfile -t scripts < <(
|
||||
git ls-files '*.sh' ':(glob)**/*.bash'
|
||||
@@ -98,6 +98,7 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
shellcheck --external-sources --source-path=SCRIPTDIR --severity=style "${scripts[@]}"
|
||||
bash .gitea/tests/deploy-validation.sh
|
||||
|
||||
lint-prettier:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
# All committed Compose files, including the ones deploy never starts.
|
||||
compose_files() {
|
||||
git ls-files \
|
||||
'*/compose.yaml' '*/compose.yml' 'compose.yaml' 'compose.yml' \
|
||||
'*/docker-compose.yaml' '*/docker-compose.yml'
|
||||
'*compose.yaml' '*compose.yml'
|
||||
}
|
||||
|
||||
# Prints the flags that turn `docker compose config` into the general check.
|
||||
|
||||
@@ -686,27 +686,52 @@ stage_preflight() {
|
||||
git -C "$REPO" reset --hard "$target"
|
||||
}
|
||||
|
||||
# Required pod Secrets, scoped to the resource namespace. TLS route Secrets are
|
||||
# created by cert-manager and are not prerequisites for applying a Certificate.
|
||||
check_referenced_secrets() {
|
||||
local m k objects refs extracted ns name
|
||||
local missing=()
|
||||
refs=""
|
||||
for m in ${K8S_MANIFESTS[@]+"${K8S_MANIFESTS[@]}"}; do
|
||||
objects="$(kubectl create --dry-run=client --validate=false -f "$m" -o json)" || return 1
|
||||
extracted="$(printf '%s' "$objects" | jq -r -f "$REPO/.gitea/workflows/secret-references.jq")" || return 1
|
||||
refs+="$extracted"$'\n'
|
||||
done
|
||||
for k in ${KUSTOMIZE_APPS[@]+"${KUSTOMIZE_APPS[@]}"}; do
|
||||
objects="$(kubectl kustomize "$k" | kubectl create --dry-run=client --validate=false -f - -o json)" || return 1
|
||||
extracted="$(printf '%s' "$objects" | jq -r -f "$REPO/.gitea/workflows/secret-references.jq")" || return 1
|
||||
refs+="$extracted"$'\n'
|
||||
done
|
||||
while read -r ns name; do
|
||||
[ -n "${name:-}" ] || continue
|
||||
if kubectl get secret "$name" -n "$ns" -o name >/dev/null 2>&1; then
|
||||
echo " ok: $ns/$name"
|
||||
else
|
||||
echo " MISSING OR UNREADABLE: $ns/$name"
|
||||
missing+=("$ns/$name")
|
||||
fi
|
||||
done < <(printf '%s' "$refs" | sort -u)
|
||||
if [ "${#missing[@]}" -gt 0 ]; then
|
||||
echo "ERROR: required pod Secrets are missing or unreadable:"
|
||||
printf ' - %s\n' "${missing[@]}"
|
||||
echo "Create them in the listed namespaces from the service's secret example."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stage_validate() {
|
||||
cd "$REPO"
|
||||
select_manifests
|
||||
local m k cf
|
||||
# Compose .env files and secret files are gitignored by design, so the
|
||||
# workstation never has real values for the inactive stacks. This stage only
|
||||
# runs the full check on active stacks; the general structure check for every
|
||||
# committed Compose file (active or not) lives in the ci workflow, which has no
|
||||
# .env at all.
|
||||
#
|
||||
# Active stacks are still validated with interpolation and env-file resolution
|
||||
# off, so required-variable guards (:?) and missing local files do not fail the
|
||||
# deploy. Normalization and consistency checks stay enabled.
|
||||
# The deploy host has the local .env and secret files. Resolve them here so
|
||||
# missing configuration fails before either apply job changes workloads.
|
||||
# CI keeps the structure-only check for inactive stacks.
|
||||
# shellcheck source=compose-lint.sh
|
||||
source "$REPO/.gitea/workflows/compose-lint.sh"
|
||||
local compose_validate_flags=()
|
||||
mapfile -t compose_validate_flags < <(compose_safe_flags)
|
||||
log "Validate compose stacks"
|
||||
for cf in ${COMPOSE_STACKS[@]+"${COMPOSE_STACKS[@]}"}; do
|
||||
echo " config: $cf"
|
||||
validate_compose_file "$cf" ${compose_validate_flags[@]+"${compose_validate_flags[@]}"}
|
||||
validate_compose_file "$cf"
|
||||
done
|
||||
log "Validate k8s manifests (kubectl dry-run=client)"
|
||||
for m in ${K8S_MANIFESTS[@]+"${K8S_MANIFESTS[@]}"}; do
|
||||
@@ -724,33 +749,7 @@ stage_validate() {
|
||||
done
|
||||
log "Checking referenced Secrets exist"
|
||||
echo " (deploy never applies *secret*.yaml; create missing ones manually)"
|
||||
local ref_secrets=() missing_secrets=() all_secrets s
|
||||
if [ "${#K8S_MANIFESTS[@]}" -gt 0 ]; then
|
||||
while IFS= read -r s; do
|
||||
[ -n "$s" ] && ref_secrets+=("$s")
|
||||
done < <(
|
||||
{
|
||||
grep -h -A1 -E 'secretRef:|secretKeyRef:' "${K8S_MANIFESTS[@]}" 2>/dev/null || true
|
||||
grep -h -E 'secretName:' "${K8S_MANIFESTS[@]}" 2>/dev/null || true
|
||||
} | grep -E 'name:' | sed -E 's/.*name:[[:space:]]*//' | tr -d '"'"'"' "'"'" | sed -E 's/[[:space:]]*#.*//' | awk 'NF' | sort -u || true
|
||||
)
|
||||
fi
|
||||
all_secrets="$(kubectl get secrets -A --no-headers -o custom-columns=:metadata.name 2>/dev/null || true)"
|
||||
for s in ${ref_secrets[@]+"${ref_secrets[@]}"}; do
|
||||
if printf '%s\n' "$all_secrets" | grep -qx "$s"; then
|
||||
echo " ok: $s"
|
||||
else
|
||||
echo " MISSING: $s"
|
||||
missing_secrets+=("$s")
|
||||
fi
|
||||
done
|
||||
if [ "${#missing_secrets[@]}" -gt 0 ]; then
|
||||
echo "ERROR: ${#missing_secrets[@]} referenced Secret(s) not found in the cluster:"
|
||||
printf ' - %s\n' "${missing_secrets[@]}"
|
||||
echo "Create them manually from the laptop, e.g.:"
|
||||
echo " kubectl apply -f SERVICE/k8s/secrets.yaml # see SERVICE/k8s/secrets.yaml.example"
|
||||
exit 1
|
||||
fi
|
||||
check_referenced_secrets
|
||||
}
|
||||
|
||||
stage_apply_k8s() {
|
||||
|
||||
@@ -120,6 +120,15 @@ install_shellcheck() {
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
install_jq() {
|
||||
if at_version jq "${JQ_VERSION}"; then
|
||||
return 0
|
||||
fi
|
||||
fetch "https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-linux-${goarch}" \
|
||||
"$BIN_DIR/jq"
|
||||
chmod 0755 "$BIN_DIR/jq"
|
||||
}
|
||||
|
||||
install_uv() {
|
||||
if at_version uv "${UV_VERSION}"; then
|
||||
return 0
|
||||
@@ -236,6 +245,7 @@ for tool in "${wanted[@]}"; do
|
||||
case "$tool" in
|
||||
kubeconform) install_kubeconform ;;
|
||||
shellcheck) install_shellcheck ;;
|
||||
jq) install_jq ;;
|
||||
actionlint) install_actionlint ;;
|
||||
prettier) install_prettier ;;
|
||||
ruff) install_ruff ;;
|
||||
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
docker run --rm \
|
||||
-v "$PWD/renovate/renovate.json:/opt/renovate/renovate.json:ro" \
|
||||
-e RENOVATE_PLATFORM=gitea \
|
||||
-e RENOVATE_ENDPOINT=https://gitea.forust.xyz/api/v1 \
|
||||
-e RENOVATE_ENDPOINT=https://git.forust.xyz/api/v1 \
|
||||
-e RENOVATE_TOKEN="$RENOVATE_TOKEN" \
|
||||
-e RENOVATE_GITHUB_COM_TOKEN="${RENOVATE_GITHUB_COM_TOKEN:-}" \
|
||||
-e RENOVATE_REPOSITORIES="${RENOVATE_REPOSITORIES:-forust/homelab}" \
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# kubectl emits a List for files containing multiple resources.
|
||||
(if .kind == "List" then .items[] else . end)
|
||||
| (.metadata.namespace // "default") as $ns
|
||||
| [
|
||||
(.. | objects
|
||||
| (.secretRef? // empty), (.secretKeyRef? // empty), (.secret? // empty)
|
||||
| select(.optional != true)
|
||||
| .name // .secretName // empty),
|
||||
(.. | objects | .imagePullSecrets[]?.name)
|
||||
]
|
||||
| unique[]
|
||||
| select(. != null and . != "")
|
||||
| "\($ns) \(.)"
|
||||
@@ -31,3 +31,6 @@ UV_VERSION="0.12.17"
|
||||
# so the tree that gets tested is the tree that gets built. Renovate keeps this
|
||||
# in step with the Dockerfile's node: tag via the "node runtime" group.
|
||||
NODE_VERSION="22.23.3"
|
||||
|
||||
# Secret-reference regression tests parse rendered Kubernetes objects.
|
||||
JQ_VERSION="1.8.1"
|
||||
@@ -51,6 +51,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: adguard-deployment
|
||||
namespace: adguard
|
||||
spec:
|
||||
@@ -64,39 +66,8 @@ spec:
|
||||
metadata:
|
||||
labels:
|
||||
app: adguard
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
spec:
|
||||
containers:
|
||||
- name: netbird
|
||||
image: netbirdio/netbird:0.80.0
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: adguard-config
|
||||
env:
|
||||
- name: NB_SETUP_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: adguard-netbird-secrets
|
||||
key: NB_SETUP_KEY
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- SYS_ADMIN
|
||||
- SYS_RESOURCE
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
volumeMounts:
|
||||
- name: netbird-state
|
||||
mountPath: /var/lib/netbird
|
||||
- name: dev-tun
|
||||
mountPath: /dev/net/tun
|
||||
- name: adguard
|
||||
image: adguard/adguardhome:v0.107.79
|
||||
resources:
|
||||
@@ -113,6 +84,13 @@ spec:
|
||||
name: dns
|
||||
- containerPort: 853
|
||||
name: dot
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: dns
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: adguard-data
|
||||
mountPath: /opt/adguardhome/work
|
||||
@@ -124,12 +102,6 @@ spec:
|
||||
mountPath: /certs
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: netbird-state
|
||||
emptyDir: {}
|
||||
- name: dev-tun
|
||||
hostPath:
|
||||
path: /dev/net/tun
|
||||
type: CharDevice
|
||||
- name: adguard-data
|
||||
persistentVolumeClaim:
|
||||
claimName: adguard-pvc
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: adguard-config
|
||||
namespace: adguard
|
||||
data:
|
||||
NB_MANAGEMENT_URL: "https://nb.forust.xyz"
|
||||
NB_HOSTNAME: "adguard"
|
||||
NB_LOG_LEVEL: "info"
|
||||
NB_DISABLE_DNS: "true"
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: adguard-netbird-secrets
|
||||
namespace: adguard
|
||||
type: Opaque
|
||||
stringData:
|
||||
NB_SETUP_KEY: "REPLACE_ME"
|
||||
@@ -27,6 +27,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: authentik-server-deployment
|
||||
namespace: authentik
|
||||
spec:
|
||||
@@ -63,6 +65,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: authentik-worker-deployment
|
||||
namespace: authentik
|
||||
spec:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: cfddns
|
||||
labels:
|
||||
app: cfddns
|
||||
|
||||
@@ -17,6 +17,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: checkmk-deployment
|
||||
namespace: checkmk
|
||||
spec:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: cloudflared
|
||||
labels:
|
||||
app: cloudflared
|
||||
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: convertx-deployment
|
||||
namespace: converters
|
||||
spec:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: session-keeper
|
||||
namespace: edu-master
|
||||
labels:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: webinar-checker
|
||||
namespace: edu-master
|
||||
labels:
|
||||
|
||||
+5
-2
@@ -13,9 +13,12 @@ services:
|
||||
- GITEA__database__PASSWD=gitea
|
||||
- GITEA__database__NAME=gitea
|
||||
# Server
|
||||
- GITEA__server__ROOT_URL=https://gitea.forust.xyz
|
||||
- GITEA__server__ROOT_URL=https://git.forust.xyz
|
||||
- GITEA__server__SSH_DOMAIN=gitssh.forust.xyz
|
||||
- GITEA__server__SSH_PORT=2221
|
||||
# Pin 28.0 defaults explicitly (see k8s/config.yaml for rationale)
|
||||
- GITEA__service__DISABLE_REGISTRATION=true
|
||||
- GITEA__actions__RUN_RETENTION_DAYS=90
|
||||
# Mailer
|
||||
- GITEA__mailer__ENABLED=true
|
||||
- GITEA__mailer__FROM=${SERVICE_EMAIL}
|
||||
@@ -34,7 +37,7 @@ services:
|
||||
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.gitea.rule=Host(`gitea.forust.xyz`)"
|
||||
- "traefik.http.routers.gitea.rule=Host(`git.forust.xyz`) || Host(`gitea.forust.xyz`)"
|
||||
- "traefik.http.routers.gitea.entrypoints=websecure"
|
||||
- "traefik.http.routers.gitea.tls.certresolver"
|
||||
# Local Router
|
||||
|
||||
@@ -4,11 +4,14 @@ metadata:
|
||||
name: gitea-config
|
||||
namespace: gitea
|
||||
data:
|
||||
GITEA__server__DOMAIN: "gitea.forust.xyz"
|
||||
GITEA__server__ROOT_URL: "https://gitea.forust.xyz"
|
||||
GITEA__server__ROOT_URL: "https://git.forust.xyz"
|
||||
GITEA__server__SSH_DOMAIN: "gitssh.forust.xyz"
|
||||
GITEA__server__SSH_PORT: "2221"
|
||||
|
||||
GITEA__service__DISABLE_REGISTRATION: "true"
|
||||
|
||||
GITEA__actions__RUN_RETENTION_DAYS: "90"
|
||||
|
||||
GITEA__database__DB_TYPE: "postgres"
|
||||
GITEA__database__HOST: "postgres.database.svc.cluster.local:5432"
|
||||
GITEA__database__NAME: "gitea"
|
||||
|
||||
@@ -17,6 +17,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: gitea-deployment
|
||||
namespace: gitea
|
||||
spec:
|
||||
|
||||
@@ -177,8 +177,8 @@ data:
|
||||
# url: https://gitssh.forust.xyz
|
||||
# - title: gcr.forust.xyz
|
||||
# url: https://gcr.forust.xyz/v2/
|
||||
- title: gitea.forust.xyz
|
||||
url: https://gitea.forust.xyz
|
||||
- title: git.forust.xyz
|
||||
url: https://git.forust.xyz
|
||||
- title: nextcloud.forust.xyz
|
||||
url: https://nextcloud.forust.xyz
|
||||
- title: mc.forust.xyz
|
||||
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: glance-deployment
|
||||
namespace: glance
|
||||
spec:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
SECRET_ENCRYPTION_KEY="REPLACE_ME"
|
||||
TZ="Europe/Bratislava"
|
||||
PUID="1000"
|
||||
PGID="1000"
|
||||
@@ -0,0 +1,38 @@
|
||||
services:
|
||||
homarr:
|
||||
container_name: homarr
|
||||
image: ghcr.io/homarr-labs/homarr:v2.1.2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./appdata:/appdata
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./kubeconfig:/app/config/kubeconfig:ro
|
||||
env_file: .env
|
||||
ports:
|
||||
- 80:7575
|
||||
- 81:3000
|
||||
environment:
|
||||
- TZ=${TZ:-Europe/Bratislava}
|
||||
- TURBO_TELEMETRY_DISABLED=1
|
||||
- KUBECONFIG=/app/config/kubeconfig
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.homarr.loadbalancer.server.port=7575"
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.homarr.rule=Host(`homarr.forust.xyz`)"
|
||||
- "traefik.http.routers.homarr.entrypoints=websecure"
|
||||
- "traefik.http.routers.homarr.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.homarr-local.rule=Host(`homarr.workstation.internal`)"
|
||||
- "traefik.http.routers.homarr-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.homarr-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.homarr-dev.rule=Host(`homarr.gigaforust.internal`)"
|
||||
- "traefik.http.routers.homarr-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.homarr-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
@@ -0,0 +1,28 @@
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: home-prod-tls
|
||||
# namespace: homarr
|
||||
# spec:
|
||||
# secretName: home-prod-tls
|
||||
# dnsNames:
|
||||
# - home.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: internal-wildcard-tls
|
||||
namespace: homarr
|
||||
spec:
|
||||
secretName: internal-wildcard-tls
|
||||
dnsNames:
|
||||
- "*.workstation.internal"
|
||||
- "*.gigaforust.internal"
|
||||
- workstation.internal
|
||||
- gigaforust.internal
|
||||
issuerRef:
|
||||
name: internal-ca
|
||||
kind: ClusterIssuer
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: homarr-config
|
||||
namespace: homarr
|
||||
data:
|
||||
TZ: "Europe/Bratislava"
|
||||
TURBO_TELEMETRY_DISABLED: "1"
|
||||
ENABLE_KUBERNETES: "true"
|
||||
@@ -0,0 +1,83 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: homarr-service
|
||||
namespace: homarr
|
||||
spec:
|
||||
selector:
|
||||
app: homarr
|
||||
ports:
|
||||
- port: 7575
|
||||
targetPort: 7575
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: homarr-deployment
|
||||
namespace: homarr
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: homarr
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: homarr
|
||||
spec:
|
||||
serviceAccountName: homarr
|
||||
containers:
|
||||
- name: homarr
|
||||
image: ghcr.io/homarr-labs/homarr:v2.1.2
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: homarr-config
|
||||
- secretRef:
|
||||
name: homarr-secrets
|
||||
ports:
|
||||
- containerPort: 7575
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 7575
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 7575
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: homarr-data
|
||||
mountPath: /appdata
|
||||
resources:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "350Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "700Mi"
|
||||
volumes:
|
||||
- name: homarr-data
|
||||
persistentVolumeClaim:
|
||||
claimName: homarr-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: homarr-pvc
|
||||
namespace: homarr
|
||||
spec:
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
volumeMode: Filesystem
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
@@ -0,0 +1,33 @@
|
||||
# apiVersion: traefik.io/v1alpha1
|
||||
# kind: IngressRoute
|
||||
# metadata:
|
||||
# name: homarr-prod
|
||||
# namespace: homarr
|
||||
# spec:
|
||||
# entryPoints:
|
||||
# - websecure
|
||||
# routes:
|
||||
# - match: Host(`home.forust.xyz`)
|
||||
# kind: Rule
|
||||
# services:
|
||||
# - name: homarr-service
|
||||
# port: 7575
|
||||
# tls:
|
||||
# secretName: home-prod-tls
|
||||
# ---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: homarr-local
|
||||
namespace: homarr
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`home.workstation.internal`) || Host(`home.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: homarr-service
|
||||
port: 7575
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: homarr
|
||||
@@ -0,0 +1,58 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: homarr
|
||||
namespace: homarr
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: homarr-readonly
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- pods
|
||||
- services
|
||||
- endpoints
|
||||
- namespaces
|
||||
- nodes
|
||||
- configmaps
|
||||
- persistentvolumeclaims
|
||||
- events
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["apps"]
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
- daemonsets
|
||||
- replicasets
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources:
|
||||
- ingresses
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["traefik.io"]
|
||||
resources:
|
||||
- ingressroutes
|
||||
- ingressroutetcps
|
||||
- ingressrouteudps
|
||||
- middlewares
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["metrics.k8s.io"]
|
||||
resources:
|
||||
- pods
|
||||
- nodes
|
||||
verbs: ["get", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: homarr-readonly
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: homarr-readonly
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: homarr
|
||||
namespace: homarr
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: homarr-secrets
|
||||
namespace: homarr
|
||||
type: Opaque
|
||||
stringData:
|
||||
# openssl rand -hex 32
|
||||
SECRET_ENCRYPTION_KEY: "REPLACE_ME"
|
||||
@@ -174,7 +174,7 @@
|
||||
<h2>./projects</h2>
|
||||
<ul class="repo-list">
|
||||
<li>
|
||||
<a href="https://gitea.forust.xyz/forust/gosleep" target="_blank">forust/gosleep</a>
|
||||
<a href="https://git.forust.xyz/forust/gosleep" target="_blank">forust/gosleep</a>
|
||||
<span class="comment">// linux sleep timer written in rust (originally in go)</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -14,6 +14,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: immich-deployment
|
||||
namespace: immich
|
||||
labels:
|
||||
|
||||
@@ -14,6 +14,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: immich-machine-learning-deployment
|
||||
namespace: immich
|
||||
labels:
|
||||
|
||||
@@ -17,6 +17,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: immich-valkey
|
||||
namespace: immich
|
||||
labels:
|
||||
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: kener-deployment
|
||||
namespace: kener
|
||||
spec:
|
||||
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: metube-deployment
|
||||
namespace: metube
|
||||
spec:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.2
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.3
|
||||
container_name: n8n
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
+3
-1
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: n8n-deployment
|
||||
namespace: n8n
|
||||
spec:
|
||||
@@ -29,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: n8n
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.2
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.3
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: n8n-config
|
||||
|
||||
@@ -32,6 +32,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: netbird-server-deployment
|
||||
namespace: netbird
|
||||
spec:
|
||||
@@ -126,6 +128,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: netbird-dashboard-deployment
|
||||
namespace: netbird
|
||||
spec:
|
||||
|
||||
@@ -14,6 +14,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: netbox-deployment
|
||||
namespace: netbox
|
||||
labels:
|
||||
@@ -118,6 +120,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: netbox-worker-deployment
|
||||
namespace: netbox
|
||||
labels:
|
||||
|
||||
@@ -17,6 +17,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: netbox-valkey
|
||||
namespace: netbox
|
||||
labels:
|
||||
|
||||
@@ -14,6 +14,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: netronome-deployment
|
||||
namespace: netronome
|
||||
labels:
|
||||
|
||||
Whitespace-only changes.
@@ -1,11 +1,17 @@
|
||||
# Pinned chart: stakater/reloader 2.2.17 (app v1.4.22).
|
||||
# Deployed by the deploy workflow, namespace reloader.
|
||||
# Restarts pods when a ConfigMap or Secret they consume changes. Opt-in per workload
|
||||
# via the reloader.stakater.com/auto: "true" pod annotation; watchGlobally because
|
||||
# via the reloader.stakater.com/auto: "true" workload annotation; watchGlobally because
|
||||
# the workloads that need it are spread across a few dozen namespaces.
|
||||
|
||||
reloader:
|
||||
watchGlobally: true
|
||||
# Only opted-in workloads are restarted. Keep scheduled jobs on their schedule.
|
||||
autoReloadAll: false
|
||||
ignoreJobs: true
|
||||
ignoreCronJobs: true
|
||||
# Change pod-template annotations rather than injecting STAKATER_* env vars.
|
||||
reloadStrategy: annotations
|
||||
|
||||
deployment:
|
||||
replicas: 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
RENOVATE_ENDPOINT=https://gitea.forust.xyz/api/v1
|
||||
RENOVATE_ENDPOINT=https://git.forust.xyz/api/v1
|
||||
RENOVATE_TOKEN=
|
||||
RENOVATE_REPOSITORIES=forust/homelab
|
||||
LOG_LEVEL=info
|
||||
@@ -218,6 +218,34 @@ data:
|
||||
"matchUpdateTypes": ["patch"],
|
||||
"groupName": "all patch updates",
|
||||
"groupSlug": "all-patch"
|
||||
},
|
||||
{
|
||||
"description": "Python Y-bumps break compat (3.11->3.12->3.13->3.14) - keep the base image out of the shared minor/patch groups, review every bump separately. Placed last so its groupName wins.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["python"],
|
||||
"groupName": "python base image",
|
||||
"groupSlug": "python",
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"description": "Rolling/floating tags (streaming stack, nextcloud beta, kubectl latest) - never automerge, every bump is a manual review. Placed last so automerge:false wins over the shared digest/patch rule.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": [
|
||||
"lscr.io/linuxserver/jellyfin",
|
||||
"lscr.io/linuxserver/qbittorrent",
|
||||
"lscr.io/linuxserver/sonarr",
|
||||
"lscr.io/linuxserver/radarr",
|
||||
"lscr.io/linuxserver/prowlarr",
|
||||
"lscr.io/linuxserver/bazarr",
|
||||
"ghcr.io/seerr-team/seerr",
|
||||
"fallenbagel/jellyseerr",
|
||||
"ghcr.io/lampac-nextgen/lampac",
|
||||
"ghcr.io/nextcloud-releases/all-in-one",
|
||||
"alpine/kubectl"
|
||||
],
|
||||
"groupName": "floating images - manual",
|
||||
"groupSlug": "floating-manual",
|
||||
"automerge": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: renovate
|
||||
image: renovate/renovate:44.132.5
|
||||
image: renovate/renovate:44.136.0
|
||||
env:
|
||||
- name: RENOVATE_PLATFORM
|
||||
value: gitea
|
||||
|
||||
@@ -6,6 +6,6 @@ metadata:
|
||||
type: Opaque
|
||||
stringData:
|
||||
RENOVATE_TOKEN: ""
|
||||
RENOVATE_ENDPOINT: "https://gitea.forust.xyz/api/v1"
|
||||
RENOVATE_ENDPOINT: "https://git.forust.xyz/api/v1"
|
||||
RENOVATE_REPOSITORIES: "forust/homelab"
|
||||
RENOVATE_GITHUB_COM_TOKEN: ""
|
||||
@@ -207,6 +207,34 @@
|
||||
"matchUpdateTypes": ["patch"],
|
||||
"groupName": "all patch updates",
|
||||
"groupSlug": "all-patch"
|
||||
},
|
||||
{
|
||||
"description": "Python Y-bumps break compat (3.11->3.12->3.13->3.14) - keep the base image out of the shared minor/patch groups, review every bump separately. Placed last so its groupName wins.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["python"],
|
||||
"groupName": "python base image",
|
||||
"groupSlug": "python",
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"description": "Rolling/floating tags (streaming stack, nextcloud beta, kubectl latest) - never automerge, every bump is a manual review. Placed last so automerge:false wins over the shared digest/patch rule.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": [
|
||||
"lscr.io/linuxserver/jellyfin",
|
||||
"lscr.io/linuxserver/qbittorrent",
|
||||
"lscr.io/linuxserver/sonarr",
|
||||
"lscr.io/linuxserver/radarr",
|
||||
"lscr.io/linuxserver/prowlarr",
|
||||
"lscr.io/linuxserver/bazarr",
|
||||
"ghcr.io/seerr-team/seerr",
|
||||
"fallenbagel/jellyseerr",
|
||||
"ghcr.io/lampac-nextgen/lampac",
|
||||
"ghcr.io/nextcloud-releases/all-in-one",
|
||||
"alpine/kubectl"
|
||||
],
|
||||
"groupName": "floating images - manual",
|
||||
"groupSlug": "floating-manual",
|
||||
"automerge": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: searxng-deployment
|
||||
namespace: searxng
|
||||
spec:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
PUID=1000
|
||||
PGID=1000
|
||||
TZ=Europe/Berlin
|
||||
Whitespace-only changes.
@@ -0,0 +1,133 @@
|
||||
services:
|
||||
jellyfin:
|
||||
image: lscr.io/linuxserver/jellyfin:version-12.1ubu2604
|
||||
container_name: jellyfin
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
volumes:
|
||||
- jellyfin-cfg:/config
|
||||
- movies:/media/movies
|
||||
- tv:/media/tv
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
ports:
|
||||
- "18096:8096"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
qbittorrent:
|
||||
image: lscr.io/linuxserver/qbittorrent:5.2.4
|
||||
container_name: qbittorrent
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
- WEBUI_PORT=8080
|
||||
volumes:
|
||||
- qbittorrent-cfg:/config
|
||||
- downloads:/downloads
|
||||
ports:
|
||||
- "18180:8080"
|
||||
- "6881:6881"
|
||||
- "6881:6881/udp"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
sonarr:
|
||||
image: lscr.io/linuxserver/sonarr:4.0.20
|
||||
container_name: sonarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
volumes:
|
||||
- sonarr-cfg:/config
|
||||
- downloads:/downloads
|
||||
- tv:/tv
|
||||
ports:
|
||||
- "18989:8989"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
radarr:
|
||||
image: lscr.io/linuxserver/radarr:6.4.4
|
||||
container_name: radarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
volumes:
|
||||
- radarr-cfg:/config
|
||||
- downloads:/downloads
|
||||
- movies:/movies
|
||||
ports:
|
||||
- "17878:7878"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
prowlarr:
|
||||
image: lscr.io/linuxserver/prowlarr:2.6.5
|
||||
container_name: prowlarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
volumes:
|
||||
- prowlarr-cfg:/config
|
||||
ports:
|
||||
- "19696:9696"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
jellyseerr:
|
||||
image: fallenbagel/jellyseerr:latest
|
||||
container_name: jellyseerr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
volumes:
|
||||
- jellyseerr-cfg:/app/config
|
||||
ports:
|
||||
- "15055:5055"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
bazarr:
|
||||
image: lscr.io/linuxserver/bazarr:1.6.2
|
||||
container_name: bazarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Europe/Berlin}
|
||||
volumes:
|
||||
- bazarr-cfg:/config
|
||||
- movies:/movies
|
||||
- tv:/tv
|
||||
ports:
|
||||
- "16767:6767"
|
||||
networks:
|
||||
- streaming
|
||||
|
||||
volumes:
|
||||
jellyfin-cfg:
|
||||
qbittorrent-cfg:
|
||||
sonarr-cfg:
|
||||
radarr-cfg:
|
||||
prowlarr-cfg:
|
||||
jellyseerr-cfg:
|
||||
bazarr-cfg:
|
||||
downloads:
|
||||
movies:
|
||||
tv:
|
||||
|
||||
networks:
|
||||
streaming:
|
||||
name: streaming
|
||||
Whitespace-only changes.
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: streaming
|
||||
@@ -0,0 +1,93 @@
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: internal-wildcard-tls
|
||||
namespace: streaming
|
||||
spec:
|
||||
secretName: internal-wildcard-tls
|
||||
dnsNames:
|
||||
- "*.workstation.internal"
|
||||
- "*.gigaforust.internal"
|
||||
- workstation.internal
|
||||
- gigaforust.internal
|
||||
issuerRef:
|
||||
name: internal-ca
|
||||
kind: ClusterIssuer
|
||||
# ---
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: jellyfin-prod-tls
|
||||
# namespace: streaming
|
||||
# spec:
|
||||
# secretName: jellyfin-prod-tls
|
||||
# dnsNames:
|
||||
# - jellyfin.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: qbittorrent-prod-tls
|
||||
# namespace: streaming
|
||||
# spec:
|
||||
# secretName: qbittorrent-prod-tls
|
||||
# dnsNames:
|
||||
# - qbittorrent.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: sonarr-prod-tls
|
||||
# namespace: streaming
|
||||
# spec:
|
||||
# secretName: sonarr-prod-tls
|
||||
# dnsNames:
|
||||
# - sonarr.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: radarr-prod-tls
|
||||
# namespace: streaming
|
||||
# spec:
|
||||
# secretName: radarr-prod-tls
|
||||
# dnsNames:
|
||||
# - radarr.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: prowlarr-prod-tls
|
||||
# namespace: streaming
|
||||
# spec:
|
||||
# secretName: prowlarr-prod-tls
|
||||
# dnsNames:
|
||||
# - prowlarr.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: jellyseerr-prod-tls
|
||||
# namespace: streaming
|
||||
# spec:
|
||||
# secretName: jellyseerr-prod-tls
|
||||
# dnsNames:
|
||||
# - jellyseerr.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
@@ -0,0 +1,188 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 18096
|
||||
targetPort: 18096
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: jellyfin
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 18096
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: qbittorrent
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 18180
|
||||
targetPort: 18180
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: qbittorrent
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: qbittorrent
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 18180
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sonarr
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 18989
|
||||
targetPort: 18989
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: sonarr
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: sonarr
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 18989
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: radarr
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 17878
|
||||
targetPort: 17878
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: radarr
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: radarr
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 17878
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: prowlarr
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 19696
|
||||
targetPort: 19696
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: prowlarr
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: prowlarr
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 19696
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jellyseerr
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 15055
|
||||
targetPort: 15055
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: jellyseerr
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: jellyseerr
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 15055
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: bazarr
|
||||
namespace: streaming
|
||||
spec:
|
||||
ports:
|
||||
- port: 16767
|
||||
targetPort: 16767
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: bazarr
|
||||
namespace: streaming
|
||||
labels:
|
||||
kubernetes.io/service-name: bazarr
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 16767
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "192.168.88.100"
|
||||
conditions:
|
||||
ready: true
|
||||
@@ -0,0 +1,118 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: jellyfin-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`jellyfin.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: jellyfin
|
||||
port: 18096
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: qbittorrent-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`qbittorrent.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: qbittorrent
|
||||
port: 18180
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: sonarr-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`sonarr.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: sonarr
|
||||
port: 18989
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: radarr-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`radarr.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: radarr
|
||||
port: 17878
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: prowlarr-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`prowlarr.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: prowlarr
|
||||
port: 19696
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: jellyseerr-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`jellyseerr.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: jellyseerr
|
||||
port: 15055
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: bazarr-local
|
||||
namespace: streaming
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`bazarr.workstation.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: bazarr
|
||||
port: 16767
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:2.9.0
|
||||
image: ghcr.io/lukegus/termix:2.9.1
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: termix-deployment
|
||||
namespace: termix
|
||||
spec:
|
||||
@@ -29,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: termix
|
||||
image: ghcr.io/lukegus/termix:2.9.0
|
||||
image: ghcr.io/lukegus/termix:2.9.1
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: termix-config
|
||||
|
||||
@@ -10,6 +10,8 @@ spec:
|
||||
routes:
|
||||
- match: Host(`traefik.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: security-chain@file
|
||||
services:
|
||||
- name: api@internal
|
||||
kind: TraefikService
|
||||
|
||||
@@ -94,7 +94,7 @@ ports:
|
||||
exposedPort: 8080
|
||||
protocol: TCP
|
||||
expose:
|
||||
default: false
|
||||
default: true
|
||||
http:
|
||||
aliasHeadersStrategy: delete
|
||||
ssh:
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: uptime-kuma
|
||||
namespace: uptime-kuma
|
||||
labels:
|
||||
release: prometheus-stack
|
||||
spec:
|
||||
groups:
|
||||
- name: uptime_kuma.monitors
|
||||
rules:
|
||||
- alert: KumaMonitorDown
|
||||
expr: |
|
||||
monitor_status{monitor_type!="group"} == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Uptime Kuma monitor down: {{ $labels.monitor_name }}"
|
||||
description: "Uptime Kuma monitor {{ $labels.monitor_name }} ({{ $labels.monitor_url }}) is down for 5m. Check Uptime Kuma (https://uptime.forust.xyz) and the target service."
|
||||
|
||||
- alert: KumaScrapeDown
|
||||
expr: |
|
||||
absent(monitor_status) == 1
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Uptime Kuma metrics missing"
|
||||
description: "uptime-kuma: no monitor_status series for 10m. Pod may be down, the uk1_ API key may have been rotated without updating uptime-kuma-secrets, or ServiceMonitor/Service broken. All Kuma monitors are unobserved."
|
||||
|
||||
- alert: KumaCertExpiring
|
||||
expr: |
|
||||
monitor_cert_days_remaining < 14
|
||||
for: 1h
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "TLS cert expiring: {{ $labels.monitor_name }} ({{ $value }}d left)"
|
||||
description: "Uptime Kuma monitor {{ $labels.monitor_name }} ({{ $labels.monitor_url }}) reports a TLS certificate with {{ $value }} days remaining. Check cert-manager Certificate for this host."
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: uptime-kuma-secrets
|
||||
namespace: uptime-kuma
|
||||
type: Opaque
|
||||
stringData:
|
||||
metrics-username: "uptime-kuma"
|
||||
metrics-password: "REPLACE_ME"
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: uptime-kuma
|
||||
namespace: uptime-kuma
|
||||
labels:
|
||||
release: prometheus-stack
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: uptime-kuma
|
||||
endpoints:
|
||||
- port: http
|
||||
path: /metrics
|
||||
interval: 60s
|
||||
scrapeTimeout: 15s
|
||||
basicAuth:
|
||||
username:
|
||||
name: uptime-kuma-secrets
|
||||
key: metrics-username
|
||||
password:
|
||||
name: uptime-kuma-secrets
|
||||
key: metrics-password
|
||||
@@ -3,11 +3,14 @@ kind: Service
|
||||
metadata:
|
||||
name: uptime-kuma-service
|
||||
namespace: uptime-kuma
|
||||
labels:
|
||||
app: uptime-kuma
|
||||
spec:
|
||||
selector:
|
||||
app: uptime-kuma
|
||||
ports:
|
||||
- port: 3001
|
||||
name: http
|
||||
targetPort: 3001
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -13,6 +13,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: vaultwarden-deployment
|
||||
namespace: vaultwarden
|
||||
spec:
|
||||
|
||||
@@ -20,6 +20,8 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
name: xui-deployment
|
||||
namespace: xui
|
||||
spec:
|
||||
|
||||
Reference in new issue
Block a user