1 Commits

Author SHA1 Message Date
a54e97e51f Update caronc/apprise Docker tag to v1.2.6
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
lint-test-helm / helm-lint (pull_request) Successful in 21s
2025-11-26 03:01:36 +00:00
794 changed files with 18518 additions and 16851 deletions

View File

@@ -0,0 +1,87 @@
name: lint-test-docker
on:
pull_request:
branches:
- main
paths:
- 'hosts/**'
- ! 'hosts/archive'
jobs:
docker-lint:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check Branch Exists
id: check-branch-exists
uses: GuillaumeFalourd/branch-exists@v1.1
with:
branch: "${{ github.base_ref }}"
- name: Branch Does Not Exist
if: steps.check-branch-exists.outputs.exists == 'false'
run: echo "Branch ${{ github.base_ref }} was not found, likely already merged"
- name: Set up Node.js
if: steps.check-branch-exists.outputs.exists == 'true'
uses: actions/setup-node@v6
with:
node-version: '24'
- name: Lint Docker Compose
if: steps.check-branch-exists.outputs.exists == 'true'
run: |
set -e # Exit immediately if a command exits with a non-zero status.
TARGET_BRANCH="origin/${{ github.base_ref }}"
echo ">> Target branch for diff is: $TARGET_BRANCH"
CHANGED_FILES=$(git diff --name-only "$TARGET_BRANCH" -- 'hosts/**')
echo ">> Found changed files:"
echo "$CHANGED_FILES"
# For each changed file, find its parent chart directory (the one with compose.yaml).
# Then, create a unique list of those directories.
CHANGED_COMPOSE=$(echo "$CHANGED_FILES" | while read -r file; do
dir=$(dirname "$file")
while [[ "$dir" != "." && ! -f "$dir/compose.yaml" ]]; do
dir=$(dirname "$dir")
done
if [[ "$dir" != "." ]]; then
echo "$dir"
fi
done | sort -u)
if [[ -z "$CHANGED_COMPOSE" ]]; then
echo ">> Could not determine changed compose files. This will happen if only files outside a compose file were changed."
exit 0
fi
echo ">> Running dclint on changed compose files:"
echo "$CHANGED_COMPOSE"
echo "$CHANGED_COMPOSE" | while read -r compose; do
echo ">> Linting $compose ..."
npx dclint $compose
done
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Test Failure - Infrastructure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Docker linting on Pull Request for Infrastructure has failed!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=lint-test-docker-pull.yaml", "clear": true}]'
image: true

View File

@@ -0,0 +1,75 @@
name: lint-test-docker
on:
push:
branches:
- main
paths:
- 'hosts/**'
- ! 'hosts/archive'
jobs:
docker-lint:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
- name: Lint Docker Compose
run: |
set -e # Exit immediately if a command exits with a non-zero status.
TARGET_BRANCH="origin/main"
echo ">> Target branch for diff is: $TARGET_BRANCH"
CHANGED_FILES=$(git diff --name-only "$TARGET_BRANCH" -- 'hosts/**')
echo ">> Found changed files:"
echo "$CHANGED_FILES"
# For each changed file, find its parent chart directory (the one with compose.yaml).
# Then, create a unique list of those directories.
CHANGED_COMPOSE=$(echo "$CHANGED_FILES" | while read -r file; do
dir=$(dirname "$file")
while [[ "$dir" != "." && ! -f "$dir/compose.yaml" ]]; do
dir=$(dirname "$dir")
done
if [[ "$dir" != "." ]]; then
echo "$dir"
fi
done | sort -u)
if [[ -z "$CHANGED_COMPOSE" ]]; then
echo ">> Could not determine changed compose files. This will happen if only files outside a compose file were changed."
exit 0
fi
echo ">> Running dclint on changed compose files:"
echo "$CHANGED_COMPOSE"
echo "$CHANGED_COMPOSE" | while read -r compose; do
echo ">> Linting $compose ..."
npx dclint $compose
done
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Test Failure - Infrastructure'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Docker linting on Push for Infrastructure has failed!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=lint-test-docker-push.yaml", "clear": true}]'
image: true

View File

@@ -1,134 +0,0 @@
name: lint-test-docker
on:
pull_request:
branches:
- main
paths:
- 'hosts/**'
push:
branches:
- main
paths:
- 'hosts/**'
env:
BASE_BRANCH: "origin/${{ github.base_ref }}"
jobs:
lint-docker-compose:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Check Branch Exists
id: check-branch-exists
if: github.event_name == 'pull_request'
uses: GuillaumeFalourd/branch-exists@650358876c774d6ccbd581b5553eb636dab79a97 # v1.2
with:
branch: "${{ github.base_ref }}"
- name: Report Branch Exists
id: branch-exists
if: github.event_name == 'push' || steps.check-branch-exists.outputs.exists == 'true' && github.event_name == 'pull_request'
run: |
if [ "${{ github.event_name }}" == "push" ]; then
echo ">> Action is from a push event, will continue with linting"
else
echo ">> Branch ${{ github.base_ref }} exists, will continue with linting"
fi
echo ""
echo "----"
echo "exists=true" >> $GITHUB_OUTPUT
- name: Set Up Node.js
if: steps.branch-exists.outputs.exists == 'true'
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: '24'
- name: Check Directories for Changes
id: check-dir-changes
if: steps.branch-exists.outputs.exists == 'true'
run: |
echo ">> Target branch for diff is: ${BASE_BRANCH}"
if [ "${{ github.event_name }}" == "pull_request" ]; then
DIFF_TARGET="${BASE_BRANCH}"
echo ""
echo ">> Checking for changes in a pull request ..."
else
DIFF_TARGET="${{ github.event.before }}..HEAD"
echo ""
echo ">> Checking for changes from a push ..."
fi
CHANGED_COMPOSE=$(git diff --name-only "${DIFF_TARGET}" | grep -E "^hosts/[^/]+/[^/]+/" | cut -d/ -f1,2,3 | sort -u || true)
if [ -n "${CHANGED_COMPOSE}" ]; then
echo ""
echo ">> Compose to Lint:"
echo ""
echo "${CHANGED_COMPOSE}"
CHANGED_COMPOSE_CSV=$(echo "$CHANGED_COMPOSE" | paste -sd ',' -)
echo ""
echo "----"
echo "changes-detected=true" >> $GITHUB_OUTPUT
echo "compose-dir-csv=${CHANGED_COMPOSE_CSV}" >> $GITHUB_OUTPUT
echo "compose-dir<<EOF" >> $GITHUB_OUTPUT
echo "${CHANGED_COMPOSE}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo ""
echo ">> Did not find any docker compose files to lint"
echo ""
echo "----"
echo "changes-detected=false" >> $GITHUB_OUTPUT
fi
- name: Lint Docker Compose
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
CHANGED_COMPOSE: ${{ steps.check-dir-changes.outputs.compose-dir }}
run: |
echo ">> Running dclint on changed compose files ..."
for COMPOSE in $CHANGED_COMPOSE; do
echo ">> Linting ${COMPOSE} ..."
npx dclint ${COMPOSE}
done
echo ""
echo "----"
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Docker Compose Test Failure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Docker linting for compose dirs: ${{ steps.check-dir-changes.outputs.compose-dir-csv }}"
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "View Logs", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'

View File

@@ -0,0 +1,90 @@
name: lint-test-helm
on:
pull_request:
branches:
- main
paths:
- 'clusters/**'
- ! 'clusters/*/archive'
jobs:
helm-lint:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check Branch Exists
id: check-branch-exists
uses: GuillaumeFalourd/branch-exists@v1.1
with:
branch: ${{ github.base_ref }}
- name: Branch Does Not Exist
if: steps.check-branch-exists.outputs.exists == 'false'
run: echo "Branch ${{ github.base_ref }} was not found, likely already merged"
- name: Set up Helm
if: steps.check-branch-exists.outputs.exists == 'true'
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
version: latest
- name: Lint Helm Chart
if: steps.check-branch-exists.outputs.exists == 'true'
run: |
set -e # Exit immediately if a command exits with a non-zero status.
TARGET_BRANCH="origin/${{ github.base_ref }}"
echo ">> Target branch for diff is: $TARGET_BRANCH"
CHANGED_FILES=$(git diff --name-only "$TARGET_BRANCH" -- 'clusters/**')
echo ">> Found changed files:"
echo "$CHANGED_FILES"
# For each changed file, find its parent chart directory (the one with Chart.yaml).
# Then, create a unique list of those directories.
CHANGED_CHARTS=$(echo "$CHANGED_FILES" | while read -r file; do
dir=$(dirname "$file")
while [[ "$dir" != "." && ! -f "$dir/Chart.yaml" ]]; do
dir=$(dirname "$dir")
done
if [[ "$dir" != "." ]]; then
echo "$dir"
fi
done | sort -u)
if [[ -z "$CHANGED_CHARTS" ]]; then
echo ">> Could not determine changed charts. This could happen if only files outside a chart were changed."
exit 0
fi
echo ">> Running helm lint on changed charts:"
echo "$CHANGED_CHARTS"
echo "$CHANGED_CHARTS" | while read -r chart; do
echo ">> Building dependency for "$chart" ..."
helm dependency build "$chart"
echo ">> Linting $chart..."
helm lint "$chart"
done
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Test Failure - Infrastructure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Helm linting on Pull Request for Infrastructure has failed!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=lint-test-helm-pull.yaml", "clear": true}]'
image: true

View File

@@ -0,0 +1,78 @@
name: lint-test-helm
on:
push:
branches:
- main
paths:
- 'clusters/**'
- ! 'clusters/*/archive'
jobs:
helm-lint:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
version: latest
- name: Lint Helm Chart
run: |
set -e # Exit immediately if a command exits with a non-zero status.
TARGET_BRANCH="origin/main"
echo ">> Target branch for diff is: $TARGET_BRANCH"
CHANGED_FILES=$(git diff --name-only "$TARGET_BRANCH" -- 'clusters/**')
echo ">> Found changed files:"
echo "$CHANGED_FILES"
# For each changed file, find its parent chart directory (the one with Chart.yaml).
# Then, create a unique list of those directories.
CHANGED_CHARTS=$(echo "$CHANGED_FILES" | while read -r file; do
dir=$(dirname "$file")
while [[ "$dir" != "." && ! -f "$dir/Chart.yaml" ]]; do
dir=$(dirname "$dir")
done
if [[ "$dir" != "." ]]; then
echo "$dir"
fi
done | sort -u)
if [[ -z "$CHANGED_CHARTS" ]]; then
echo ">> Could not determine changed charts. This could happen if only files outside a chart were changed."
exit 0
fi
echo ">> Running helm lint on changed charts:"
echo "$CHANGED_CHARTS"
echo "$CHANGED_CHARTS" | while read -r chart; do
echo ">> Building dependency for "$chart" ..."
helm dependency build "$chart"
echo ">> Linting $chart..."
helm lint "$chart"
done
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Test Failure - Infrastructure'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Helm linting on Push for Infrastructure has failed!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=lint-test-helm-push.yaml", "clear": true}]'
image: true

View File

@@ -1,367 +0,0 @@
name: lint-test-helm
on:
pull_request:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
push:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
env:
CLUSTER: cl01tl
BASE_BRANCH: "origin/${{ github.base_ref }}"
# renovate: datasource=github-releases depName=yannh/kubeconform
KUBECONFORM_VERSION: "v0.6.7"
jobs:
lint-helm:
runs-on: ubuntu-js
outputs:
chart-dir: ${{ steps.check-dir-changes.outputs.chart-dir }}
chart-dir-csv: ${{ steps.check-dir-changes.outputs.chart-dir-csv }}
changes-detected: ${{ steps.check-dir-changes.outputs.changes-detected }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Check Branch Exists
id: check-branch-exists
if: github.event_name == 'pull_request'
uses: GuillaumeFalourd/branch-exists@650358876c774d6ccbd581b5553eb636dab79a97 # v1.2
with:
branch: ${{ github.base_ref }}
- name: Report Branch Exists
id: branch-exists
if: github.event_name == 'push' || steps.check-branch-exists.outputs.exists == 'true' && github.event_name == 'pull_request'
run: |
if [ "${{ github.event_name }}" == "push" ]; then
echo ">> Action is from a push event, will continue with linting"
else
echo ">> Branch ${{ github.base_ref }} exists, will continue with linting"
fi
echo ""
echo "----"
echo "exists=true" >> $GITHUB_OUTPUT
- name: Set Up Helm
if: steps.branch-exists.outputs.exists == 'true'
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
with:
token: ${{ secrets.GITEA_TOKEN }}
# renovate: datasource=github-releases depName=helm/helm
version: v4.1.3
cache: true
- name: Cache Helm Dependencies
if: steps.branch-exists.outputs.exists == 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/helm
~/.config/helm
key: helm-cache-${{ runner.os }}-${{ hashFiles('infrastructure/clusters/cl01tl/helm/**/Chart.yaml', 'infrastructure/clusters/cl01tl/helm/**/Chart.lock') }}
restore-keys: |
helm-cache-${{ runner.os }}-
- name: Check Directories for Changes
id: check-dir-changes
if: steps.branch-exists.outputs.exists == 'true'
run: |
echo ">> Target branch for diff is: ${BASE_BRANCH}"
if [ "${{ github.event_name }}" == "pull_request" ]; then
DIFF_TARGET="${BASE_BRANCH}"
echo ""
echo ">> Checking for changes in a pull request ..."
else
DIFF_TARGET="${{ github.event.before }}..HEAD"
echo ""
echo ">> Checking for changes from a push ..."
fi
CHANGED_CHARTS=$(git diff --name-only "${DIFF_TARGET}" | grep -E "^clusters/${CLUSTER}/helm/" | awk -F '/' '{print $4}' | sort -u || true)
if [ -n "${CHANGED_CHARTS}" ]; then
echo ""
echo ">> Chart to Lint:"
echo ""
echo "${CHANGED_CHARTS}"
CHANGED_CHARTS_CSV=$(echo "$CHANGED_CHARTS" | paste -sd ',' -)
echo ""
echo "----"
echo "changes-detected=true" >> $GITHUB_OUTPUT
echo "chart-dir-csv=${CHANGED_CHARTS_CSV}" >> $GITHUB_OUTPUT
echo "chart-dir<<EOF" >> $GITHUB_OUTPUT
echo "${CHANGED_CHARTS}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo ""
echo ">> Did not find any helm charts files to lint"
echo ""
echo "----"
echo "changes-detected=false" >> $GITHUB_OUTPUT
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
CHANGED_CHARTS: ${{ steps.check-dir-changes.outputs.chart-dir }}
run: |
echo ">> Adding repositories for chart dependencies ..."
echo ""
for DIR in ${CHANGED_CHARTS}; do
helm dependency list --max-col-width 120 clusters/${CLUSTER}/helm/${DIR} 2> /dev/null \
| tail -n +2 \
| awk 'NF > 0 { print $1, $3 }' \
| while read -r REPO_NAME REPO_URL; do
if [[ "${REPO_URL}" == oci://* ]]; then
echo ">> Ignoring OCI repo: ${REPO_URL}"
elif [[ -n "${REPO_NAME}" && -n "${REPO_URL}" ]]; then
helm repo add "${REPO_NAME}" "${REPO_URL}"
fi
done || true
done
if helm repo list > /dev/null 2>&1; then
echo ""
echo ">> Update repository cache ..."
helm repo update
fi
echo ""
echo "----"
- name: Lint Helm Chart
id: lint
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
CHANGED_CHARTS: ${{ steps.check-dir-changes.outputs.chart-dir }}
run: |
EXIT_CODE=0
FAILED_CHARTS=""
echo ">> Running linting on changed charts ..."
for DIR in ${CHANGED_CHARTS}; do
CHART_PATH="clusters/${CLUSTER}/helm/${DIR}"
CHART_NAME=$(basename "${CHART_PATH}")
if [ -f "${CHART_PATH}/Chart.yaml" ]; then
echo ""
echo ">> Building helm dependency for ${CHART_NAME} ..."
helm dependency build "${CHART_PATH}" --skip-refresh
echo ""
echo ">> Linting helm chart ${CHART_NAME} ..."
if ! helm lint "${CHART_PATH}" --namespace "default"; then
EXIT_CODE=1
if [ -z "${FAILED_CHARTS}" ]; then
FAILED_CHARTS="${DIR}"
else
FAILED_CHARTS="${FAILED_CHARTS}, ${DIR}"
fi
fi
else
echo ""
echo ">> Directory ${CHART_PATH} does not contain a Chart.yaml. Skipping ..."
fi
done
echo ""
echo "----"
echo "failed-charts=${FAILED_CHARTS}" >> "$GITHUB_OUTPUT"
exit $EXIT_CODE
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Helm Test Failure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Helm linting for cluster '${{ env.CLUSTER }}' failed on charts: ${{ steps.lint.outputs.failed-charts }}"
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "View Run", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'
image: true
validate-kubeconform:
needs: lint-helm
runs-on: ubuntu-js
if: |
needs.lint-helm.result == 'success' &&
needs.lint-helm.outputs.changes-detected == 'true' &&
github.event_name == 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Install Kubeconform
run: |
echo ">> Downloading Kubeconform ${{ env.KUBECONFORM_VERSION }} ..."
wget -q https://github.com/yannh/kubeconform/releases/download/${{ env.KUBECONFORM_VERSION }}/kubeconform-linux-amd64.tar.gz
echo ""
echo ">> Extracting Kubeconform ..."
tar xf kubeconform-linux-amd64.tar.gz
echo ""
echo ">> Installing Kubeconform ..."
sudo mv kubeconform /usr/local/bin/
echo ""
echo ">> Verifying installation ..."
kubeconform -v
echo ""
echo "----"
- name: Set Up Helm
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
with:
token: ${{ secrets.GITEA_TOKEN }}
# renovate: datasource=github-releases depName=helm/helm
version: v4.1.3
cache: true
- name: Cache Helm Dependencies
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/helm
~/.config/helm
key: helm-cache-${{ runner.os }}-${{ hashFiles('infrastructure/clusters/cl01tl/helm/**/Chart.yaml', 'infrastructure/clusters/cl01tl/helm/**/Chart.lock') }}
restore-keys: |
helm-cache-${{ runner.os }}-
- name: Add Repositories
env:
CHANGED_CHARTS: ${{ needs.lint-helm.outputs.chart-dir }}
run: |
echo ">> Adding repositories for chart dependencies ..."
echo ""
for DIR in ${CHANGED_CHARTS}; do
helm dependency list --max-col-width 120 clusters/${CLUSTER}/helm/${DIR} 2> /dev/null \
| tail -n +2 \
| awk 'NF > 0 { print $1, $3 }' \
| while read -r REPO_NAME REPO_URL; do
if [[ "${REPO_URL}" == oci://* ]]; then
echo ">> Ignoring OCI repo: ${REPO_URL}"
elif [[ -n "${REPO_NAME}" && -n "${REPO_URL}" ]]; then
helm repo add "${REPO_NAME}" "${REPO_URL}"
fi
done || true
done
if helm repo list > /dev/null 2>&1; then
echo ""
echo ">> Update repository cache ..."
helm repo update
fi
echo ""
echo "----"
- name: Validate Rendered Templates
id: validate
env:
CHANGED_CHARTS: ${{ needs.lint-helm.outputs.chart-dir }}
run: |
SCHEMA_LOCATIONS="-schema-location default -schema-location https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json"
EXIT_CODE=0
FAILED_CHARTS=""
for DIR in ${CHANGED_CHARTS}; do
CHART_PATH="clusters/${CLUSTER}/helm/${DIR}"
echo ""
echo ">> Validating: ${DIR}"
helm dependency build "${CHART_PATH}" --skip-refresh
if ! helm template "${DIR}" "${CHART_PATH}" --include-crds --namespace default --api-versions "gateway.networking.k8s.io/v1/HTTPRoute" | \
kubeconform \
${SCHEMA_LOCATIONS} \
-ignore-missing-schemas \
-strict \
-summary; then
EXIT_CODE=1
if [ -z "${FAILED_CHARTS}" ]; then
FAILED_CHARTS="${DIR}"
else
FAILED_CHARTS="${FAILED_CHARTS}, ${DIR}"
fi
fi
done
echo ""
echo "----"
echo "failed-charts=${FAILED_CHARTS}" >> "$GITHUB_OUTPUT"
exit $EXIT_CODE
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Kubeconform Test Failure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Kubeconform for cluster '${{ env.CLUSTER }}' failed on charts: ${{ steps.validate.outputs.failed-charts }}"
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "View Run", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'
image: true

View File

@@ -1,624 +0,0 @@
name: render-manifests
on:
schedule:
- cron: '0 15 * * *'
workflow_dispatch:
pull_request:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
types:
- closed
env:
CLUSTER: cl01tl
BASE_BRANCH: manifests
BRANCH_NAME_BASE: auto/update-manifests
ASSIGNEE: alexlebens
MAIN_DIR: /workspace/alexlebens/infrastructure/infrastructure
MANIFEST_DIR: /workspace/alexlebens/infrastructure/infrastructure-manifests
jobs:
render-manifests:
runs-on: ubuntu-js
if: >-
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true)
steps:
- name: Checkout Main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
path: infrastructure
fetch-depth: 0
- name: Checkout Manifests
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: manifests
path: infrastructure-manifests
- name: Set Up Helm
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
with:
token: ${{ secrets.GITEA_TOKEN }}
version: v3.17.2 # Pending https://github.com/helm/helm/pull/30743
cache: true
- name: Configure Kubeconfig
uses: azure/k8s-set-context@ae59a723ba9abe7a9655538854a025448dbab4aa # v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Cache Helm Dependencies
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/helm
~/.config/helm
key: helm-cache-${{ runner.os }}-${{ hashFiles('infrastructure/clusters/cl01tl/helm/**/Chart.yaml', 'infrastructure/clusters/cl01tl/helm/**/Chart.lock') }}
restore-keys: |
helm-cache-${{ runner.os }}-
- name: Determine Workflow Mode
id: mode
run: |
IS_AUTOMERGE="false"
RENDER_ALL="false"
DIFF_TARGET=""
if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo ">> Mode: Dispatch/Schedule (Render All)"
RENDER_ALL="true"
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'automerge') }}" == "true" ]]; then
echo ">> Mode: PR Merged (Automerge)"
IS_AUTOMERGE="true"
else
echo ">> Mode: PR Merged (Standard)"
fi
DIFF_TARGET="HEAD^..HEAD"
fi
echo ""
echo "----"
echo "is-automerge=${IS_AUTOMERGE}" >> "$GITHUB_OUTPUT"
echo "render-all=${RENDER_ALL}" >> "$GITHUB_OUTPUT"
echo "diff-target=${DIFF_TARGET}" >> "$GITHUB_OUTPUT"
- name: Prepare Manifest Branch
id: prepare-manifest-branch
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
IS_AUTOMERGE: ${{ steps.mode.outputs.is-automerge }}
run: |
cd "${MANIFEST_DIR}"
echo ">> Configure git to use gitea-bot as user ..."
git config user.name "gitea-bot"
git config user.email "gitea-bot@alexlebens.net"
if [[ "$IS_AUTOMERGE" == "true" ]]; then
BRANCH_NAME="${BRANCH_NAME_BASE}-automerge-${PR_NUMBER}"
echo ""
echo ">> Creating branch ${BRANCH_NAME} ..."
git checkout -B "$BRANCH_NAME"
else
echo ""
echo ">> Checking if PR branch exists ..."
BRANCH_NAME="${BRANCH_NAME_BASE}"
if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" > /dev/null 2>&1; then
echo ""
echo ">> Branch '${BRANCH_NAME}' exists, pulling changes ..."
git fetch origin "${BRANCH_NAME}"
git checkout "${BRANCH_NAME}"
git pull --rebase
else
echo ""
echo ">> Branch '${BRANCH_NAME}' does not exist, creating ..."
git checkout -b "${BRANCH_NAME}"
fi
fi
echo ""
echo "----"
echo "branch-name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT"
- name: Check Which Directories Have Changes
id: check-dir-changes
env:
RENDER_ALL: ${{ steps.mode.outputs.render-all }}
DIFF_TARGET: ${{ steps.mode.outputs.diff-target }}
run: |
cd "${MAIN_DIR}"
if [[ "$RENDER_ALL" == "true" ]]; then
echo ">> Triggered on dispatch, will check all paths ..."
RENDER_DIR=$(find "clusters/${CLUSTER}/helm" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort -u)
else
echo ">> Checking for changes from ${DIFF_TARGET} ..."
RENDER_DIR=$(git diff --name-only "${DIFF_TARGET}" | grep -E "^clusters/${CLUSTER}/helm/" | awk -F '/' '{print $4}' | sort -u || true)
fi
if [ -n "${RENDER_DIR}" ]; then
echo ""
echo ">> Directories to Render:"
echo ""
echo "${RENDER_DIR}"
echo ""
echo "----"
echo "changes-detected=true" >> "$GITHUB_OUTPUT"
echo "render-dir<<EOF" >> "$GITHUB_OUTPUT"
echo "${RENDER_DIR}" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo ""
echo ">> No chart changes detected"
echo ""
echo "----"
echo "changes-detected=false" >> "$GITHUB_OUTPUT"
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd "${MAIN_DIR}"
echo ">> Adding repositories for chart dependencies ..."
echo ""
for DIR in ${RENDER_DIR}; do
helm dependency list --max-col-width 120 "${MAIN_DIR}/clusters/${CLUSTER}/helm/${DIR}" 2> /dev/null \
| tail -n +2 \
| awk 'NF > 0 { print $1, $3 }' \
| while read -r REPO_NAME REPO_URL; do
if [[ "${REPO_URL}" == oci://* ]]; then
echo ">> Ignoring OCI repo: ${REPO_URL}"
elif [[ -n "${REPO_NAME}" && -n "${REPO_URL}" ]]; then
helm repo add "${REPO_NAME}" "${REPO_URL}"
fi
done || true
done
if helm repo list > /dev/null 2>&1; then
echo ""
echo ">> Update repository cache ..."
helm repo update
fi
echo ""
echo "----"
- name: Remove Changed Manifest Files
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd "${MANIFEST_DIR}"
echo ">> Remove manifest files and rebuild from source ..."
echo ""
for DIR in ${RENDER_DIR}; do
CHART_PATH="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/${DIR}"
echo "${CHART_PATH}"
rm -rf "${CHART_PATH}"/*
done
echo ""
echo "----"
- name: Render Helm Manifests
id: render-manifests
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd "${MAIN_DIR}"
echo ">> Rendering Manifests ..."
render_chart() {
local DIR="$1"
local CHART_PATH="${MAIN_DIR}/clusters/${CLUSTER}/helm/${DIR}"
local CHART_NAME=$(basename "${CHART_PATH}")
echo ""
echo ">> Rendering chart: ${CHART_NAME}"
if [ -f "${CHART_PATH}/Chart.yaml" ]; then
local OUTPUT_FOLDER="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/${CHART_NAME}/"
mkdir -p "${OUTPUT_FOLDER}"
cd "${CHART_PATH}"
helm dependency update --skip-refresh > /dev/null
helm lint --namespace "${CHART_NAME}" --quiet
local NAMESPACE="${CHART_NAME}"
case "${CHART_NAME}" in
"stack")
NAMESPACE="argocd"
echo ">> Special Rendering into 'argocd' namespace ..."
;;
"cilium" | "coredns" | "metrics-server" | "prometheus-operator-crds")
NAMESPACE="kube-system"
echo ">> Special Rendering for ${CHART_NAME} into 'kube-system' namespace ..."
;;
*)
echo ">> Standard Rendering ..."
esac
echo ">> Formating rendered template ..."
local TEMPLATE
TEMPLATE=$(helm template "${CHART_NAME}" ./ --namespace "${NAMESPACE}" --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
# Format and split rendered template
echo "${TEMPLATE}" | yq '... comments=""' | yq 'select(. != null)' | yq -s '"'"${OUTPUT_FOLDER}"'" + .kind + "-" + .metadata.name + ".yaml"'
# Strip comments again to ensure formatting correctness
for file in "$OUTPUT_FOLDER"/*; do
yq -i '... comments=""' $file
done
echo ">> Manifests for ${CHART_NAME} rendered successfully to $OUTPUT_FOLDER:"
echo ""
ls $OUTPUT_FOLDER
echo ""
else
echo ""
echo ">> Directory ${CHART_PATH} does not contain a Chart.yaml. Skipping ..."
fi
}
export -f render_chart
export MAIN_DIR CLUSTER MANIFEST_DIR
# Run rendering in parallel
for DIR in ${RENDER_DIR}; do
echo "${DIR}"
done | xargs -P 4 -I {} bash -c 'OUT=$(render_chart "$@" 2>&1); printf "%s\n" "$OUT"' _ {}
echo ""
echo "----"
- name: Check for Changes
id: check-changes
if: steps.check-dir-changes.outputs.changes-detected == 'true'
run: |
cd "${MANIFEST_DIR}"
GIT_CHANGES=$(git status --porcelain)
if [ -n "${GIT_CHANGES}" ]; then
echo ">> Changes detected"
git status --porcelain
CHANGED_CHARTS=$(echo "$GIT_CHANGES" | grep -oE "clusters/${CLUSTER}/manifests/[^/]+" | awk -F '/' '{print $4}' | sort -u | paste -sd ',' -)
echo ""
echo "----"
echo "changes-detected=true" >> "$GITHUB_OUTPUT"
echo "changed-charts-csv=${CHANGED_CHARTS}" >> "$GITHUB_OUTPUT"
else
echo ">> No changes detected, skipping PR creation"
echo ""
echo "----"
fi
- name: Commit and Push Changes
id: commit-push
if: steps.check-changes.outputs.changes-detected == 'true'
env:
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.branch-name }}
IS_AUTOMERGE: ${{ steps.mode.outputs.is-automerge }}
run: |
cd "${MANIFEST_DIR}"
MSG="chore: Update manifests after change"
if [[ "$IS_AUTOMERGE" == "true" ]]; then
MSG="chore: Update manifests after automerge"
fi
echo ">> Commiting changes to ${BRANCH_NAME} ..."
git add .
git commit -m "${MSG}"
REPO_URL="${{ secrets.REPO_URL }}/${{ gitea.repository }}"
echo ""
echo ">> Pushing changes to ${REPO_URL} ..."
git push -u "https://oauth2:${{ secrets.BOT_TOKEN }}@${REPO_URL#*://}" "${BRANCH_NAME}"
echo ""
echo "----"
echo "push=true" >> "$GITHUB_OUTPUT"
echo "head-branch=${BRANCH_NAME}" >> "$GITHUB_OUTPUT"
- name: Check for Pull Request
id: check-for-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.mode.outputs.is-automerge == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.head-branch }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls?base_branch=${BASE_BRANCH}&state=open&page=1"
echo ">> Checking if PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
HTTP_STATUS=$(curl -X GET -s -w '%{http_code}' -o response_body.json -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "open" ]; then
echo ""
echo ">> Pull Request has been found open, will update"
echo ""
echo "----"
echo "pull-request-exists=$(cat response_body.json | jq -r .[0].number)" >> "$GITHUB_OUTPUT"
else
echo ""
echo ">> Pull Request not found"
echo ""
echo "----"
echo "pull-request-exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Create Pull Request
id: create-pull-request
if: steps.commit-push.outputs.push == 'true' && (steps.mode.outputs.is-automerge == 'true' || steps.check-for-pull-request.outputs.pull-request-exists == 'false')
env:
IS_AUTOMERGE: ${{ steps.mode.outputs.is-automerge }}
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.head-branch }}
CHARTS: ${{ steps.check-changes.outputs.changed-charts-csv }}
EVENT_NAME: ${{ github.event_name }}
ACTOR: ${{ github.actor }}
SHA: ${{ github.sha }}
REF: ${{ github.ref_name }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls"
BODY=$(printf "This PR contains newly rendered Kubernetes manifests automatically generated by the CI workflow.\n\n### Details\n- **Trigger**: \`%s\` by \`@%s\`\n- **Commit**: \`%s\` (on \`%s\`)\n- **Charts Updated**: \`%s\`" "${EVENT_NAME}" "${ACTOR}" "${SHA:0:7}" "${REF}" "${CHARTS}")
if [[ "$IS_AUTOMERGE" == "true" ]]; then
TITLE="Automated Manifest Update - Automerge"
BODY=$(printf "%s\n\n_This PR is expected to be automerged._" "${BODY}")
else
TITLE="Automated Manifest Update"
fi
PAYLOAD=$(jq -n --arg head "${HEAD_BRANCH}" --arg base "${BASE_BRANCH}" --arg assignee "${ASSIGNEE}" --arg title "${TITLE}" --arg body "${BODY}" '{head: $head, base: $base, assignee: $assignee, title: $title, body: $body}')
HTTP_STATUS=$(curl -X POST -s -w '%{http_code}' -o response_body.json --data "$PAYLOAD" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "201" ]; then
echo ">> Pull Request created successfully!"
echo ""
echo "----"
echo "pull-request-id=$(jq -r .id response_body.json)" >> "$GITHUB_OUTPUT"
echo "pull-request-number=$(jq -r .number response_body.json)" >> "$GITHUB_OUTPUT"
echo "pull-request-operation=created" >> "$GITHUB_OUTPUT"
elif [[ "$HTTP_STATUS" == "422" || "$HTTP_STATUS" == "409" ]]; then
echo ""
echo ">> Failed to create PR (Already exists)"
echo ""
echo "----"
else
echo ""
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
echo ""
echo "----"
exit 1
fi
- name: Update Pull Request
id: update-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-request.outputs.pull-request-exists != 'false' && steps.mode.outputs.is-automerge == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
PR_NUMBER: ${{ steps.check-for-pull-request.outputs.pull-request-exists }}
CHARTS: ${{ steps.check-changes.outputs.changed-charts-csv }}
EVENT_NAME: ${{ github.event_name }}
ACTOR: ${{ github.actor }}
SHA: ${{ github.sha }}
REF: ${{ github.ref_name }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls/${PR_NUMBER}"
EXISTING_BODY=$(jq -r '.[0].body' response_body.json)
NEW_DETAILS=$(printf "### Update Details (%s)\n- **Trigger**: \`%s\` by \`@%s\`\n- **Commit**: \`%s\` (on \`%s\`)\n- **Charts Updated**: \`%s\`" "$(date -u +'%Y-%m-%d %H:%M UTC')" "${EVENT_NAME}" "${ACTOR}" "${SHA:0:7}" "${REF}" "${CHARTS}")
UPDATED_BODY=$(printf "%s\n\n%s" "${EXISTING_BODY}" "${NEW_DETAILS}")
PAYLOAD=$(jq -n --arg body "${UPDATED_BODY}" '{body: $body}')
HTTP_STATUS=$(curl -X PATCH -s -w '%{http_code}' -o update_response.json --data "$PAYLOAD" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "201" ] || [ "$HTTP_STATUS" == "200" ]; then
echo ">> Pull Request updated successfully!"
echo ""
echo "----"
echo "pull-request-operation=updated" >> "$GITHUB_OUTPUT"
else
echo ">> Failed to update PR, HTTP status code: $HTTP_STATUS"; exit 1
echo ""
echo "----"
fi
- name: Merge Changes
id: merge-changes
if: steps.commit-push.outputs.push == 'true' && steps.mode.outputs.is-automerge == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
PR_NUMBER: ${{ steps.create-pull-request.outputs.pull-request-number }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls/${PR_NUMBER}/merge"
PAYLOAD=$(jq -n --arg Do "merge" '{Do: $Do}')
HTTP_STATUS=$(curl -X POST -s -w '%{http_code}' -o response_body.json --data "$PAYLOAD" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "200" ]; then
echo ">> Pull Request merged successfully!"
echo ""
echo "----"
echo "pull-request-operation=merged" >> "$GITHUB_OUTPUT"
else
echo ">> Failed to merge PR, HTTP status code: $HTTP_STATUS"; exit 1
echo ""
echo "----"
fi
- name: Cleanup Branch
if: failure() && steps.mode.outputs.is-automerge == 'true'
env:
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.branch-name }}
run: |
cd "${MANIFEST_DIR}"
echo ">> Removing branch: ${BRANCH_NAME}"
git push origin --delete "${BRANCH_NAME}" || true
echo ""
echo "----"
- name: ntfy Created
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: steps.create-pull-request.outputs.pull-request-operation == 'created' && steps.mode.outputs.is-automerge == 'false'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render - Open PR"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Created renderd manifests for cluster '${{ env.CLUSTER }}' with charts: ${{ steps.check-changes.outputs.changed-charts-csv }}"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View PR", "url": "${{ vars.USER_URL }}/${{ github.repository }}/pulls/${{ steps.create-pull-request.outputs.pull-request-number }}", "clear": true}]'
- name: ntfy Updated
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-request.outputs.pull-request-exists != 'false' && steps.mode.outputs.is-automerge == 'false'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render - PR Updated"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Updated rendered manifests PR for cluster '${{ env.CLUSTER }}' with charts: ${{ steps.check-changes.outputs.changed-charts-csv }}"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View PR", "url": "${{ vars.USER_URL }}/${{ github.repository }}/pulls/${{ steps.create-pull-request.outputs.pull-request-number }}", "clear": true}]'
- name: ntfy Merged
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: steps.merge-changes.outputs.pull-request-operation == 'merged'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render - Automerged"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Automerged manifest rendering for cluster '${{ env.CLUSTER }}' with charts: ${{ steps.check-changes.outputs.changed-charts-csv }}"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View PR", "url": "${{ vars.USER_URL }}/${{ github.repository }}/pulls/${{ steps.create-pull-request.outputs.pull-request-number }}", "clear": true}]'
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: failure()
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render Failure"
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Manifest rendering for Infrastructure has failed!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View Logs", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'

View File

@@ -13,10 +13,10 @@ on:
jobs:
renovate:
runs-on: ubuntu-latest
container: ghcr.io/renovatebot/renovate:43.100.0@sha256:1188aeae54f7f2103a9dcea554316efde65eb4221793bcee5a0b29772e16aed3
container: ghcr.io/renovatebot/renovate:42
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
- name: Renovate
run: renovate
@@ -25,7 +25,7 @@ jobs:
RENOVATE_ENDPOINT: ${{ vars.INSTANCE_URL }}
RENOVATE_REPOSITORIES: alexlebens/infrastructure
RENOVATE_GIT_AUTHOR: Renovate Bot <renovate-bot@alexlebens.net>
LOG_LEVEL: debug
LOG_LEVEL: info
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
RENOVATE_GIT_PRIVATE_KEY: ${{ secrets.RENOVATE_GIT_PRIVATE_KEY }}
RENOVATE_GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }}

4
.gitignore vendored
View File

@@ -1,4 +0,0 @@
/**/archive/
/**/charts/
/**/manifests/
/**/tmpcharts*/

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: v2.3.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
@@ -9,9 +9,7 @@ repos:
exclude: '^.*\/templates\/.*$'
args:
- --multi
- id: check-merge-conflict
- id: check-json
- repo: https://github.com/IamTheFij/docker-pre-commit
rev: v3.0.1
rev: v2.0.0
hooks:
- id: docker-compose-check

View File

@@ -2,12 +2,6 @@
GitOps definied infrastrucutre for the alexlebens.net domain.
## Stack-cl01tl
https://argocd.alexlebens.net/api/badge?name=stack-cl01tl&revision=true&showAppName=true
App-of-Apps Application for cl01tl
## License
This project is licensed under the terms of the Apache 2.0 License license.

View File

@@ -5,23 +5,17 @@ description: Actual
keywords:
- actual
- budget
home: https://docs.alexlebens.dev/applications/actual/
home: https://wiki.alexlebens.dev/s/86192f45-94b7-45de-872c-6ef3fec7df5e
sources:
- https://github.com/actualbudget/actual
- https://github.com/actualbudget/actual/pkgs/container/actual
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: actual
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: volsync-target
alias: volsync-target-data
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/actual-budget.png
# renovate: datasource=github-releases depName=actualbudget/actual
appVersion: 26.3.0
appVersion: v25.5.0

View File

@@ -0,0 +1,55 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: actual-data-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: actual-data-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/actual/actual-data"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-actual
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-actual
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- actual.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: actual
port: 80
weight: 100

View File

@@ -0,0 +1,25 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: actual-data-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: actual-data-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: actual-data
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: actual-data-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot

View File

@@ -4,18 +4,20 @@ actual:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/actualbudget/actual
tag: 26.3.0@sha256:eb8bc26f53025e07e464594c12d77c52c4b95840c8dadd9b95c4f0c4660f8ad2
tag: 25.11.0
pullPolicy: IfNotPresent
env:
- name: ACTUAL_PORT
value: 5006
- name: TZ
value: US/Central
resources:
requests:
cpu: 10m
memory: 50Mi
memory: 128Mi
probes:
liveness:
enabled: true
@@ -40,43 +42,15 @@ actual:
port: 80
targetPort: 5006
protocol: HTTP
route:
main:
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- actual.alexlebens.net
rules:
- backendRefs:
- name: actual
port: 80
matches:
- path:
type: PathPrefix
value: /
persistence:
data:
forceRename: actual-data
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 2Gi
retain: true
advancedMounts:
main:
main:
- path: /data
readOnly: false
volsync-target-data:
pvcTarget: actual-data
local:
enabled: true
schedule: 0 8 * * *
remote:
enabled: true
schedule: 0 9 * * *
external:
enabled: true
schedule: 0 10 * * *

View File

@@ -0,0 +1,23 @@
apiVersion: v2
name: audiobookshelf
version: 1.0.0
description: Audiobookshelf
keywords:
- audiobookshelf
- books
- podcasts
- audiobooks
home: https://wiki.alexlebens.dev/s/d4d6719f-cd1c-4b6e-b78e-2d2d7a5097d7
sources:
- https://github.com/advplyr/audiobookshelf
- https://github.com/advplyr/audiobookshelf/pkgs/container/audiobookshelf
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: audiobookshelf
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/audiobookshelf.png
appVersion: 2.21.0

View File

@@ -0,0 +1,135 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: audiobookshelf-apprise-config
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-apprise-config
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ntfy-url
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/audiobookshelf/apprise
metadataPolicy: None
property: ntfy-url
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: audiobookshelf-config-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-config-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/audiobookshelf/audiobookshelf-config"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: audiobookshelf-metadata-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-metadata-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/audiobookshelf/audiobookshelf-metadata"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-audiobookshelf
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-audiobookshelf
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- audiobookshelf.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: audiobookshelf
port: 80
weight: 100

View File

@@ -0,0 +1,36 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: audiobookshelf-nfs-storage-backup
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-nfs-storage-backup
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeMode: Filesystem
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: audiobookshelf-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: audiobookshelf-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

View File

@@ -1,10 +1,10 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: medialyze-nfs-storage
name: audiobookshelf-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: medialyze-nfs-storage
app.kubernetes.io/name: audiobookshelf-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:

View File

@@ -0,0 +1,52 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: audiobookshelf-config-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-config-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: audiobookshelf-config
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: audiobookshelf-config-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: audiobookshelf-metadata-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-metadata-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: audiobookshelf-metadata
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: audiobookshelf-metadata-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot

View File

@@ -0,0 +1,19 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: audiobookshelf-apprise
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: audiobookshelf-apprise
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
endpoints:
- port: apprise
interval: 30s
scrapeTimeout: 15s
path: /metrics
selector:
matchLabels:
app.kubernetes.io/name: audiobookshelf
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,94 @@
audiobookshelf:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/advplyr/audiobookshelf
tag: 2.30.0
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
resources:
requests:
cpu: 10m
memory: 128Mi
apprise-api:
image:
repository: caronc/apprise
tag: 1.2.6
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
- name: PGID
value: "1000"
- name: PUID
value: "1000"
- name: APPRISE_STORAGE_MODE
value: memory
- name: APPRISE_STATEFUL_MODE
value: disabled
- name: APPRISE_WORKER_COUNT
value: 1
- name: APPRISE_STATELESS_URLS
valueFrom:
secretKeyRef:
name: audiobookshelf-apprise-config
key: ntfy-url
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 80
protocol: HTTP
apprise:
port: 8000
targetPort: 8000
protocol: HTTP
persistence:
config:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 2Gi
retain: true
advancedMounts:
main:
main:
- path: /config
readOnly: false
metadata:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 10Gi
retain: true
advancedMounts:
main:
main:
- path: /metadata
readOnly: false
backup:
existingClaim: audiobookshelf-nfs-storage-backup
advancedMounts:
main:
main:
- path: /metadata/backups
readOnly: false
audiobooks:
existingClaim: audiobookshelf-nfs-storage
advancedMounts:
main:
main:
- path: /mnt/store/
readOnly: false

View File

@@ -4,25 +4,20 @@ version: 1.0.0
description: Bazarr
keywords:
- bazarr
- servarr
- subtitles
home: https://docs.alexlebens.dev/applications/bazarr/
home: https://wiki.alexlebens.dev/s/92784d53-1d43-42fd-b509-f42c73454226
sources:
- https://github.com/morpheus65535/bazarr
- https://github.com/linuxserver/docker-bazarr
- https://github.com/linuxserver/docker-bazarr/pkgs/container/bazarr
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: bazarr
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: volsync-target
alias: volsync-target-config
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/bazarr.png
# renovate: datasource=github-releases depName=morpheus65535/bazarr
appVersion: 1.5.6
appVersion: 1.5.2

View File

@@ -0,0 +1,55 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: bazarr-config-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: bazarr-config-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/bazarr/bazarr-config"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-bazarr
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-bazarr
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- bazarr.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: bazarr
port: 80
weight: 100

View File

@@ -0,0 +1,30 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: bazarr-config-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: bazarr-config-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: bazarr-config
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: bazarr-config-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
moverSecurityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot

View File

@@ -4,6 +4,7 @@ bazarr:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
pod:
securityContext:
runAsUser: 1000
@@ -14,20 +15,19 @@ bazarr:
main:
image:
repository: ghcr.io/linuxserver/bazarr
tag: v1.5.6-ls342@sha256:9a631194c0dee21c85b5bff59e23610e1ae2f54594e922973949d271102e585e
tag: 1.5.3@sha256:2be164c02c0bb311b6c32e57d3d0ddc2813d524e89ab51a3408c1bf6fafecda5
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
value: US/Central
- name: PUID
value: 1000
- name: PGID
value: 1000
resources:
limits:
cpu: 100m
requests:
cpu: 1m
memory: 250Mi
cpu: 10m
memory: 256Mi
service:
main:
controller: main
@@ -36,30 +36,13 @@ bazarr:
port: 80
targetPort: 6767
protocol: HTTP
route:
main:
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- bazarr.alexlebens.net
rules:
- backendRefs:
- name: bazarr
port: 80
matches:
- path:
type: PathPrefix
value: /
persistence:
config:
forceRename: bazarr-config
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
@@ -72,19 +55,3 @@ bazarr:
main:
- path: /mnt/store
readOnly: false
volsync-target-config:
pvcTarget: bazarr-config
moverSecurityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
local:
enabled: true
schedule: 10 8 * * *
remote:
enabled: true
schedule: 10 9 * * *
external:
enabled: true
schedule: 10 10 * * *

View File

@@ -0,0 +1,24 @@
apiVersion: v2
name: booklore
version: 1.0.0
description: booklore
keywords:
- booklore
- books
home: https://wiki.alexlebens.dev/
sources:
- https://github.com/booklore-app/BookLore
- https://github.com/booklore-app/booklore/pkgs/container/booklore
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: booklore
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.4.0
- name: mariadb-cluster
version: 25.10.2
repository: https://helm.mariadb.com/mariadb-operator
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/booklore.png
appVersion: v.1.10.0

View File

@@ -0,0 +1,332 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-database-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-database-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: password
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/booklore/database
metadataPolicy: None
property: password
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-data-replication-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-replication-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: psk.txt
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/booklore/replication
metadataPolicy: None
property: psk.txt
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-config-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-config-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/booklore/booklore-config"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/digital-ocean
metadataPolicy: None
property: BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/digital-ocean
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: AWS_ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: AWS_SECRET_ACCESS_KEY
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-data-backup-secret-local
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-backup-secret-local
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/booklore/booklore-data"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/garage-local
metadataPolicy: None
property: BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/garage-local
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/volsync-backups
metadataPolicy: None
property: ACCESS_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/volsync-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/volsync-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-data-backup-secret-remote
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-backup-secret-remote
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/booklore/booklore-data"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/garage-remote
metadataPolicy: None
property: BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/garage-remote
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/volsync-backups
metadataPolicy: None
property: ACCESS_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/volsync-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/volsync-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-data-backup-secret-external
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-backup-secret-external
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/booklore/booklore-data"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/digital-ocean
metadataPolicy: None
property: BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /volsync/restic/digital-ocean
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: AWS_ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: AWS_SECRET_ACCESS_KEY
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-mariadb-cluster-backup-secret-external
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-mariadb-cluster-backup-secret-external
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: access
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/mariadb-backups
metadataPolicy: None
property: access
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/mariadb-backups
metadataPolicy: None
property: secret
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: booklore-mariadb-cluster-backup-secret-garage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-mariadb-cluster-backup-secret-garage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: access
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/mariadb-backups
metadataPolicy: None
property: access
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/mariadb-backups
metadataPolicy: None
property: secret

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-booklore
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-booklore
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- booklore.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: booklore
port: 80
weight: 100

View File

@@ -1,13 +1,10 @@
apiVersion: v1
kind: Namespace
metadata:
name: grimmory
name: booklore
annotations:
volsync.backube/privileged-movers: "true"
labels:
app.kubernetes.io/name: grimmory
app.kubernetes.io/name: booklore
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
pod-security.kubernetes.io/audit: privileged
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/warn: privileged

View File

@@ -1,14 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grimmory-books-nfs-storage
name: booklore-books-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-nfs-storage
app.kubernetes.io/name: booklore-books-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: grimmory-books-nfs-storage
volumeName: booklore-books-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany
@@ -20,14 +20,14 @@ spec:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grimmory-books-import-nfs-storage
name: booklore-books-import-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-import-nfs-storage
app.kubernetes.io/name: booklore-books-import-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: grimmory-books-import-nfs-storage
volumeName: booklore-books-import-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany

View File

@@ -1,10 +1,10 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: grimmory-books-nfs-storage
name: booklore-books-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-nfs-storage
app.kubernetes.io/name: booklore-books-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -26,10 +26,10 @@ spec:
apiVersion: v1
kind: PersistentVolume
metadata:
name: grimmory-books-import-nfs-storage
name: booklore-books-import-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-import-nfs-storage
app.kubernetes.io/name: booklore-books-import-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:

View File

@@ -0,0 +1,15 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationDestination
metadata:
name: booklore-data-replication-destination
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-replication-destination
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
rsyncTLS:
copyMethod: Direct
accessModes: ["ReadWriteMany"]
destinationPVC: booklore-books-nfs-storage
keySecret: booklore-data-replication-secret

View File

@@ -0,0 +1,129 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: booklore-data-replication-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-replication-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: booklore-data
trigger:
schedule: "0 0 * * *"
rsyncTLS:
keySecret: booklore-data-replication-secret
address: volsync-rsync-tls-dst-booklore-data-replication-destination
copyMethod: Snapshot
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: booklore-config-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-config-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: booklore-config
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: booklore-config-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
cacheCapacity: 10Gi
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: booklore-data-backup-source-local
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-backup-source-local
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: booklore-data
trigger:
schedule: 0 2 * * *
restic:
pruneIntervalDays: 7
repository: booklore-data-backup-secret-local
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
cacheCapacity: 10Gi
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: booklore-data-backup-source-remote
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-backup-source-remote
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: booklore-data
trigger:
schedule: 0 3 * * *
restic:
pruneIntervalDays: 7
repository: booklore-data-backup-secret-remote
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
cacheCapacity: 10Gi
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: booklore-data-backup-source-external
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: booklore-data-backup-source-external
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: booklore-data
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: booklore-data-backup-secret-external
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
cacheCapacity: 10Gi

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: garage-ps10rp
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-ps10rp
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
annotations:
tailscale.com/tailnet-fqdn: garage-ps10rp.boreal-beaufort.ts.net
spec:
externalName: placeholder
type: ExternalName

View File

@@ -0,0 +1,155 @@
booklore:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/booklore-app/booklore
tag: v1.11.0
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
- name: DATABASE_URL
value: jdbc:mariadb://booklore-mariadb-cluster-primary.booklore:3306/booklore
- name: DATABASE_USERNAME
value: booklore
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: booklore-database-secret
key: password
- name: BOOKLORE_PORT
value: 6060
- name: SWAGGER_ENABLED
value: false
resources:
requests:
cpu: 50m
memory: 128Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 6060
protocol: HTTP
persistence:
config:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
- path: /app/data
readOnly: false
data:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 10Gi
retain: true
advancedMounts:
main:
main:
- path: /data
readOnly: false
books-import:
type: emptyDir
advancedMounts:
main:
main:
- path: /bookdrop
readOnly: false
ingest:
existingClaim: booklore-books-import-nfs-storage
advancedMounts:
main:
main:
- path: /bookdrop/ingest
readOnly: false
mariadb-cluster:
mariadb:
rootPasswordSecretKeyRef:
generate: false
name: booklore-database-secret
key: password
storage:
size: 5Gi
replicas: 3
galera:
enabled: true
databases:
- name: booklore
characterSet: utf8
collate: utf8_general_ci
cleanupPolicy: Delete
requeueInterval: 10h
users:
- name: booklore
passwordSecretKeyRef:
name: booklore-database-secret
key: password
host: '%'
cleanupPolicy: Delete
requeueInterval: 10h
retryInterval: 30s
grants:
- name: booklore
privileges:
- "ALL PRIVILEGES"
database: "booklore"
table: "*"
username: booklore
grantOption: true
host: '%'
cleanupPolicy: Delete
requeueInterval: 10h
retryInterval: 30s
physicalBackups:
- name: backup-external
schedule:
cron: "0 0 * * 0"
suspend: false
immediate: true
compression: gzip
maxRetention: 720h
storage:
s3:
bucket: mariadb-backups-b230a2f5aecf080a4b372c08
prefix: cl01tl/booklore
endpoint: nyc3.digitaloceanspaces.com
region: us-east-1
accessKeyIdSecretKeyRef:
name: booklore-mariadb-cluster-backup-secret-external
key: access
secretAccessKeySecretKeyRef:
name: booklore-mariadb-cluster-backup-secret-external
key: secret
tls:
enabled: true
- name: backup-garage
schedule:
cron: "0 0 * * *"
suspend: false
immediate: true
compression: gzip
maxRetention: 360h
storage:
s3:
bucket: mariadb-backups
prefix: cl01tl/booklore
endpoint: garage-main.garage:3900
region: us-east-1
accessKeyIdSecretKeyRef:
name: booklore-mariadb-cluster-backup-secret-garage
key: access
secretAccessKeySecretKeyRef:
name: booklore-mariadb-cluster-backup-secret-garage
key: secret

View File

@@ -5,28 +5,24 @@ description: Code Server
keywords:
- code-server
- code
home: https://docs.alexlebens.dev/applications/code-server/
- ide
home: https://wiki.alexlebens.dev/s/233f96bb-db70-47e4-8b22-a8efcbb0f93d
sources:
- https://github.com/coder/code-server
- https://github.com/linuxserver/docker-code-server
- https://github.com/linuxserver/docker-code-server/pkgs/container/code-server
- https://github.com/cloudflare/cloudflared
- https://hub.docker.com/r/linuxserver/code-server
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: code-server
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
version: 4.4.0
- name: cloudflared
alias: cloudflared
repository: oci://harbor.alexlebens.net/helm-charts
version: 2.4.0
- name: volsync-target
alias: volsync-target-config
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
version: 1.23.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/visual-studio-code.png
# renovate: datasource=github-releases depName=coder/code-server
appVersion: 4.112.0
appVersion: 4.100.2

View File

@@ -1,11 +1,10 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: ollama-key-secret
name: codeserver-password-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ollama-key-secret
app.kubernetes.io/name: codeserver-password-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -13,22 +12,29 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: key
- secretKey: PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/ollama/key
key: /cl01tl/code-server/auth
metadataPolicy: None
property: key
property: PASSWORD
- secretKey: SUDO_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/code-server/auth
metadataPolicy: None
property: SUDO_PASSWORD
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: ollama-oidc-secret
name: code-server-cloudflared-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ollama-oidc-secret
app.kubernetes.io/name: code-server-cloudflared-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -36,17 +42,10 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: client
- secretKey: cf-tunnel-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/ollama
key: /cloudflare/tunnels/codeserver
metadataPolicy: None
property: client
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/ollama
metadataPolicy: None
property: secret
property: token

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-code-server
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-code-server
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- code-server.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: code-server
port: 8443
weight: 100

View File

@@ -1,17 +1,17 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vault-storage-backup
name: code-server-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: vault-storage-backup
app.kubernetes.io/name: code-server-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeMode: Filesystem
storageClassName: ceph-filesystem
storageClassName: nfs-client
accessModes:
- ReadWriteMany
- ReadWriteOnce
resources:
requests:
storage: 1Gi

View File

@@ -0,0 +1,47 @@
code-server:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/linuxserver/code-server
tag: 4.106.2@sha256:a98afdbcb59559f11e5e8df284062e55da1076b2e470e13db4aae133ea82bad0
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
- name: PUID
value: 1000
- name: PGID
value: 1000
- name: DEFAULT_WORKSPACE
value: /config
envFrom:
- secretRef:
name: codeserver-password-secret
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
ports:
http:
port: 8443
targetPort: 8443
protocol: HTTP
persistence:
config:
existingClaim: code-server-nfs-storage
advancedMounts:
main:
main:
- path: /config
readOnly: false
cloudflared:
existingSecretName: code-server-cloudflared-secret

View File

@@ -4,29 +4,30 @@ version: 1.0.0
description: Directus
keywords:
- directus
- content-management-system
home: https://docs.alexlebens.dev/applications/descheduler/
- cms
home: https://wiki.alexlebens.dev/s/c2d242de-dcaa-4801-86a2-c4761dc8bf9b
sources:
- https://github.com/directus/directus
- https://github.com/directus/directus/pkgs/container/directus
- https://github.com/cloudflare/cloudflared
- https://github.com/cloudnative-pg/cloudnative-pg
- https://hub.docker.com/r/directus/directus
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/valkey
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: directus
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 4.4.0
- name: cloudflared
alias: cloudflared-directus
repository: oci://harbor.alexlebens.net/helm-charts
- name: valkey
alias: valkey
version: 0.5.0
version: 1.23.0
- name: postgres-cluster
alias: postgres-17-cluster
version: 6.16.0
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/directus.png
# renovate: datasource=github-releases depName=directus/directus
appVersion: 11.17.0
appVersion: 11.7.2

View File

@@ -1,10 +1,160 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: garage-directus-secret
name: directus-config
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-directus-secret
app.kubernetes.io/name: directus-config
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: admin-email
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: admin-email
- secretKey: admin-password
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: admin-password
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: secret
- secretKey: key
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: key
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: directus-metric-token
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus-metric-token
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: metric-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/metrics
metadataPolicy: None
property: metric-token
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: directus-redis-config
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus-redis-config
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: user
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/redis
metadataPolicy: None
property: user
- secretKey: password
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/redis
metadataPolicy: None
property: password
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: directus-oidc-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus-oidc-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: OIDC_CLIENT_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/directus
metadataPolicy: None
property: client
- secretKey: OIDC_CLIENT_SECRET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/directus
metadataPolicy: None
property: secret
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: directus-cloudflared-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus-cloudflared-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: cf-tunnel-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cloudflare/tunnels/directus
metadataPolicy: None
property: token
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: directus-postgresql-17-cluster-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus-postgresql-17-cluster-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -16,199 +166,25 @@ spec:
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/directus-assets
key: /digital-ocean/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/directus-assets
metadataPolicy: None
property: ACCESS_REGION
property: access
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/directus-assets
key: /digital-ocean/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: SRC_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/local
metadataPolicy: None
property: ENDPOINT
- secretKey: DEST_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/remote
metadataPolicy: None
property: ENDPOINT
property: secret
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: garage-karakeep-secret
name: directus-postgresql-17-cluster-backup-secret-weekly
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-karakeep-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/karakeep-assets
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/karakeep-assets
metadataPolicy: None
property: ACCESS_REGION
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/karakeep-assets
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: SRC_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/local
metadataPolicy: None
property: ENDPOINT
- secretKey: DEST_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/remote
metadataPolicy: None
property: ENDPOINT
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: garage-talos-backups-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-talos-backups-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
metadataPolicy: None
property: ACCESS_REGION
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: SRC_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/local
metadataPolicy: None
property: ENDPOINT
- secretKey: DEST_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/remote
metadataPolicy: None
property: ENDPOINT
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: garage-web-assets-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-web-assets-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/web-assets
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/web-assets
metadataPolicy: None
property: ACCESS_REGION
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/web-assets
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: SRC_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/local
metadataPolicy: None
property: ENDPOINT
- secretKey: DEST_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/remote
metadataPolicy: None
property: ENDPOINT
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: garage-postgres-backups-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-postgres-backups-secret
app.kubernetes.io/name: directus-postgresql-17-cluster-backup-secret-weekly
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -223,13 +199,6 @@ spec:
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_REGION
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
@@ -237,17 +206,40 @@ spec:
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: SRC_ENDPOINT
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: directus-postgresql-17-cluster-backup-secret-garage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus-postgresql-17-cluster-backup-secret-garage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/local
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ENDPOINT
- secretKey: DEST_ENDPOINT
property: ACCESS_KEY_ID
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/config/remote
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ENDPOINT
property: ACCESS_SECRET_KEY
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_REGION

View File

@@ -0,0 +1,11 @@
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: ceph-bucket-directus
labels:
app.kubernetes.io/name: ceph-bucket-directus
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
generateBucketName: bucket-directus
storageClassName: ceph-bucket

View File

@@ -0,0 +1,35 @@
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisReplication
metadata:
name: redis-replication-directus
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: redis-replication-directus
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
clusterSize: 3
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
kubernetesConfig:
image: quay.io/opstree/redis:v8.2.1
imagePullPolicy: IfNotPresent
redisSecret:
name: directus-redis-config
key: password
resources:
requests:
cpu: 50m
memory: 128Mi
storage:
volumeClaimTemplate:
spec:
storageClassName: ceph-block
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
redisExporter:
enabled: true
image: quay.io/opstree/redis-exporter:v1.76.0

View File

@@ -0,0 +1,30 @@
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisSentinel
metadata:
name: redis-sentinel-directus
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: redis-sentinel-directus
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
clusterSize: 3
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
redisSentinelConfig:
redisReplicationName: redis-replication-directus
redisReplicationPassword:
secretKeyRef:
name: directus-redis-config
key: password
kubernetesConfig:
image: quay.io/opstree/redis-sentinel:v7.0.15
imagePullPolicy: IfNotPresent
redisSecret:
name: directus-redis-config
key: password
resources:
requests:
cpu: 10m
memory: 128Mi

View File

@@ -0,0 +1,43 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: directus
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: directus
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: directus
app.kubernetes.io/instance: {{ .Release.Name }}
endpoints:
- port: http
interval: 30s
scrapeTimeout: 15s
path: /metrics
bearerTokenSecret:
name: directus-metric-token
key: metric-token
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: redis-replication-directus
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: redis-replication-directus
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
redis-operator: "true"
env: production
spec:
selector:
matchLabels:
redis_setup_type: replication
endpoints:
- port: redis-exporter
interval: 30s
scrapeTimeout: 10s

View File

@@ -4,15 +4,16 @@ directus:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/directus/directus
tag: 11.17.0@sha256:076269ccbe7d4a0c44ce5f5b7f11e2ea5f7b3e4c4f704c0f88a52805e069c1c6
repository: directus/directus
tag: 11.13.4
pullPolicy: IfNotPresent
env:
- name: PUBLIC_URL
value: https://directus.alexlebens.net
value: https://directus.alexlebens.dev
- name: WEBSOCKETS_ENABLED
value: true
- name: ADMIN_EMAIL
@@ -40,27 +41,27 @@ directus:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: directus-postgresql-18-cluster-app
name: directus-postgresql-17-cluster-app
key: host
- name: DB_DATABASE
valueFrom:
secretKeyRef:
name: directus-postgresql-18-cluster-app
name: directus-postgresql-17-cluster-app
key: dbname
- name: DB_PORT
valueFrom:
secretKeyRef:
name: directus-postgresql-18-cluster-app
name: directus-postgresql-17-cluster-app
key: port
- name: DB_USER
valueFrom:
secretKeyRef:
name: directus-postgresql-18-cluster-app
name: directus-postgresql-17-cluster-app
key: user
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: directus-postgresql-18-cluster-app
name: directus-postgresql-17-cluster-app
key: password
- name: SYNCHRONIZATION_STORE
value: redis
@@ -71,16 +72,16 @@ directus:
- name: REDIS_ENABLED
value: true
- name: REDIS_HOST
value: directus-valkey
value: redis-replication-directus-master
- name: REDIS_USERNAME
valueFrom:
secretKeyRef:
name: directus-valkey-config
name: directus-redis-config
key: user
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: directus-valkey-config
name: directus-redis-config
key: password
- name: STORAGE_LOCATIONS
value: s3
@@ -89,22 +90,22 @@ directus:
- name: STORAGE_S3_KEY
valueFrom:
secretKeyRef:
name: directus-bucket-garage
key: ACCESS_KEY_ID
name: ceph-bucket-directus
key: AWS_ACCESS_KEY_ID
- name: STORAGE_S3_SECRET
valueFrom:
secretKeyRef:
name: directus-bucket-garage
key: ACCESS_SECRET_KEY
- name: STORAGE_S3_REGION
valueFrom:
secretKeyRef:
name: directus-bucket-garage
key: ACCESS_REGION
name: ceph-bucket-directus
key: AWS_SECRET_ACCESS_KEY
- name: STORAGE_S3_BUCKET
value: directus-assets
valueFrom:
configMapKeyRef:
name: ceph-bucket-directus
key: BUCKET_NAME
- name: STORAGE_S3_REGION
value: us-east-1
- name: STORAGE_S3_ENDPOINT
value: http://garage-main.garage:3900
value: http://rook-ceph-rgw-ceph-objectstore.rook-ceph.svc:80
- name: STORAGE_S3_FORCE_PATH_STYLE
value: true
- name: AUTH_PROVIDERS
@@ -124,7 +125,7 @@ directus:
- name: AUTH_AUTHENTIK_SCOPE
value: openid profile email
- name: AUTH_AUTHENTIK_ISSUER_URL
value: https://authentik.alexlebens.net/application/o/directus/.well-known/openid-configuration
value: https://auth.alexlebens.dev/application/o/directus/.well-known/openid-configuration
- name: AUTH_AUTHENTIK_IDENTIFIER_KEY
value: email
- name: AUTH_AUTHENTIK_ALLOW_PUBLIC_REGISTRATION
@@ -143,7 +144,7 @@ directus:
resources:
requests:
cpu: 10m
memory: 300Mi
memory: 256Mi
service:
main:
controller: main
@@ -152,63 +153,62 @@ directus:
port: 80
targetPort: 8055
protocol: TCP
serviceMonitor:
main:
selector:
matchLabels:
app.kubernetes.io/name: directus
app.kubernetes.io/instance: directus
serviceName: '{{ include "bjw-s.common.lib.chart.names.fullname" $ }}'
endpoints:
- port: http
interval: 30s
scrapeTimeout: 15s
path: /metrics
bearerTokenSecret:
name: directus-metric-token
key: metric-token
route:
main:
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- directus.alexlebens.net
rules:
- backendRefs:
- name: directus
port: 80
matches:
- path:
type: PathPrefix
value: /
postgres-18-cluster:
cloudflared-directus:
name: cloudflared-directus
existingSecretName: directus-cloudflared-secret
postgres-17-cluster:
mode: recovery
cluster:
storage:
storageClass: local-path
walStorage:
storageClass: local-path
monitoring:
enabled: true
prometheusRule:
enabled: true
recovery:
method: objectStore
objectStore:
destinationPath: s3://postgres-backups/cl01tl/directus/directus-postgresql-17-cluster
endpointURL: http://garage-main.garage:3900
index: 1
endpointCredentials: directus-postgresql-17-cluster-backup-secret-garage
backup:
objectStore:
- name: garage-local
- name: external
destinationPath: s3://postgres-backups-ce540ddf106d186bbddca68a/cl01tl/directus/directus-postgresql-17-cluster
index: 1
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
retentionPolicy: "30d"
isWALArchiver: false
- name: garage-local
destinationPath: s3://postgres-backups/cl01tl/directus/directus-postgresql-17-cluster
index: 1
endpointURL: http://garage-main.garage:3900
endpointCredentials: directus-postgresql-17-cluster-backup-secret-garage
endpointCredentialsIncludeRegion: true
retentionPolicy: "3d"
isWALArchiver: true
# - name: garage-remote
# destinationPath: s3://postgres-backups/cl01tl/directus/directus-postgresql-17-cluster
# index: 1
# endpointURL: https://garage-ps10rp.boreal-beaufort.ts.net:3900
# endpointCredentials: directus-postgresql-17-cluster-backup-secret-garage
# retentionPolicy: "30d"
# data:
# compression: bzip2
# jobs: 2
scheduledBackups:
- name: daily-backup
suspend: false
schedule: "0 0 0 * * *"
backupName: external
- name: live-backup
suspend: false
immediate: true
schedule: "0 15 14 * * *"
schedule: "0 0 0 * * *"
backupName: garage-local
valkey:
valkey:
auth:
enabled: true
usersExistingSecret: directus-valkey-config
aclUsers:
default:
permissions: "~* &* +@all"
# - name: weekly-backup
# suspend: false
# schedule: "0 0 4 * * SAT"
# backupName: garage-remote

View File

@@ -4,22 +4,24 @@ version: 1.0.0
description: Element Web
keywords:
- element-web
- matrix-chat
home: https://docs.alexlebens.dev/applications/element-web/
- chat
- matrix
home: https://wiki.alexlebens.dev/s/e3b03481-1a1d-4b56-8cd9-e75a8dcc0f6c
sources:
- https://github.com/element-hq/element-web
- https://github.com/element-hq/element-web/pkgs/container/element-web
- https://github.com/cloudflare/cloudflared
- https://hub.docker.com/r/vectorim/element-web
- https://gitlab.com/ananace/charts/-/tree/master/charts/element-web
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
maintainers:
- name: alexlebens
dependencies:
- name: element-web
version: 1.4.33
version: 1.4.24
repository: https://ananace.gitlab.io/charts
- name: cloudflared
alias: cloudflared
repository: oci://harbor.alexlebens.net/helm-charts
version: 2.4.0
version: 1.23.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/element.png
# renovate: datasource=github-releases depName=element-hq/element-web
appVersion: v1.12.13
appVersion: v1.11.100

View File

@@ -1,10 +1,10 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: vaultwarden-oidc-secret
name: element-web-cloudflared-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: vaultwarden-oidc-secret
app.kubernetes.io/name: element-web-cloudflared-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -12,17 +12,10 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: client
- secretKey: cf-tunnel-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/vaultwarden
key: /cloudflare/tunnels/element
metadataPolicy: None
property: client
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/vaultwarden
metadataPolicy: None
property: secret
property: token

View File

@@ -1,8 +1,9 @@
element-web:
replicaCount: 1
image:
repository: ghcr.io/element-hq/element-web
tag: v1.12.13@sha256:5107e63026c13ed014f743e485821b7d4b56d275a41e76303859bb14f5f94eb6
repository: vectorim/element-web
tag: v1.12.4
pullPolicy: IfNotPresent
defaultServer:
url: https://matrix.alexlebens.dev
name: alexlebens.dev
@@ -17,7 +18,11 @@ element-web:
immediate: true
default_theme: dark
default_country_code: US
ingress:
enabled: false
resources:
requests:
cpu: 1m
memory: 10Mi
cpu: 10m
memory: 128Mi
cloudflared:
existingSecretName: element-web-cloudflared-secret

View File

@@ -0,0 +1,23 @@
apiVersion: v2
name: ephemera
version: 1.0.0
description: ephemera
keywords:
- ephemera
- books
home: https://wiki.alexlebens.dev/
sources:
- https://github.com/OrwellianEpilogue/ephemera
- https://github.com/FlareSolverr/FlareSolverr
- https://github.com/orwellianepilogue/ephemera/pkgs/container/ephemera
- https://github.com/flaresolverr/FlareSolverr/pkgs/container/flaresolverr
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: ephemera
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/ephemera.png
appVersion: 1.3.1

View File

@@ -0,0 +1,101 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: ephemera-key-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ephemera-key-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: key
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/ephemera/config
metadataPolicy: None
property: key
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: ephemera-apprise-config
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ephemera-apprise-config
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ntfy-url
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/ephemera/config
metadataPolicy: None
property: ntfy-url
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: ephemera-config-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ephemera-config-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/ephemera/ephemera-config"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-ephemera
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-ephemera
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- ephemera.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: ephemera
port: 80
weight: 100

View File

@@ -1,14 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: music-grabber-nfs-storage
name: ephemera-import-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: music-grabber-nfs-storage
app.kubernetes.io/name: ephemera-import-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: music-grabber-nfs-storage
volumeName: ephemera-import-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany

View File

@@ -0,0 +1,23 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: ephemera-import-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ephemera-import-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-client
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
nfs:
path: /volume2/Storage/Books Import
server: synologybond.alexlebens.net
mountOptions:
- vers=4
- minorversion=1
- noac

View File

@@ -0,0 +1,26 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: ephemera-config-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: ephemera-config-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: ephemera-config
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: ephemera-config-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
cacheCapacity: 10Gi

View File

@@ -0,0 +1,107 @@
ephemera:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/orwellianepilogue/ephemera
tag: 1.3.1
pullPolicy: IfNotPresent
env:
- name: AA_BASE_URL
value: https://annas-archive.org
# - name: AA_API_KEY
# valueFrom:
# secretKeyRef:
# name: ephemera-key-secret
# key: key
- name: FLARESOLVERR_URL
value: http://127.0.0.1:8191
- name: LG_BASE_URL
value: https://gen.com
- name: PUID
value: 0
- name: PGID
value: 0
resources:
requests:
cpu: 50m
memory: 128Mi
flaresolverr:
image:
repository: ghcr.io/flaresolverr/flaresolverr
tag: v3.4.5
pullPolicy: IfNotPresent
env:
- name: LOG_LEVEL
value: info
- name: LOG_HTML
value: false
- name: CAPTCHA_SOLVER
value: none
- name: TZ
value: America/Chicago
resources:
requests:
cpu: 10m
memory: 128Mi
apprise-api:
image:
repository: caronc/apprise
tag: 1.2.6
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
- name: APPRISE_STORAGE_MODE
value: memory
- name: APPRISE_STATEFUL_MODE
value: disabled
- name: APPRISE_WORKER_COUNT
value: 1
- name: APPRISE_STATELESS_URLS
valueFrom:
secretKeyRef:
name: ephemera-apprise-config
key: ntfy-url
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 8286
protocol: HTTP
persistence:
config:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
- path: /app/data
readOnly: false
cache:
type: emptyDir
advancedMounts:
main:
main:
- path: /app/downloads
readOnly: false
ingest:
existingClaim: ephemera-import-nfs-storage
advancedMounts:
main:
main:
- path: /app/ingest
readOnly: false

View File

@@ -5,32 +5,29 @@ description: FreshRSS
keywords:
- freshrss
- rss
home: https://docs.alexlebens.dev/applications/freshrss/
home: https://wiki.alexlebens.dev/s/251cb7cb-2797-4bbb-8597-32757aa96391
sources:
- https://github.com/FreshRSS/FreshRSS
- https://github.com/cloudflare/cloudflared
- https://github.com/cloudnative-pg/cloudnative-pg
- https://hub.docker.com/r/freshrss/freshrss
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: freshrss
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
version: 4.4.0
- name: cloudflared
alias: cloudflared
repository: oci://harbor.alexlebens.net/helm-charts
version: 2.4.0
version: 1.23.0
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
repository: oci://harbor.alexlebens.net/helm-charts
- name: volsync-target
alias: volsync-target-data
version: 0.8.0
alias: postgres-17-cluster
version: 6.16.0
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/freshrss.png
# renovate: datasource=github-releases depName=FreshRSS/FreshRSS
appVersion: 1.28.1
appVersion: 1.26.2

View File

@@ -1,10 +1,10 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: talos-etcd-backup-local-secret
name: freshrss-install-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: talos-etcd-backup-local-secret
app.kubernetes.io/name: freshrss-install-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -12,50 +12,36 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: AWS_ACCESS_KEY_ID
- secretKey: ADMIN_EMAIL
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
key: /cl01tl/freshrss/config
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
property: ADMIN_EMAIL
- secretKey: ADMIN_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
key: /cl01tl/freshrss/config
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: .s3cfg
property: ADMIN_PASSWORD
- secretKey: ADMIN_API_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
key: /cl01tl/freshrss/config
metadataPolicy: None
property: s3cfg-local
- secretKey: BUCKET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
metadataPolicy: None
property: BUCKET
- secretKey: AGE_X25519_PUBLIC_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/talos/etcd-backup
metadataPolicy: None
property: AGE_X25519_PUBLIC_KEY
property: ADMIN_API_PASSWORD
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: talos-etcd-backup-remote-secret
name: freshrss-oidc-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: talos-etcd-backup-remote-secret
app.kubernetes.io/name: freshrss-oidc-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -63,50 +49,36 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: AWS_ACCESS_KEY_ID
- secretKey: OIDC_CLIENT_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
key: /authentik/oidc/freshrss
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
property: client
- secretKey: OIDC_CLIENT_SECRET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
key: /authentik/oidc/freshrss
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: .s3cfg
property: secret
- secretKey: OIDC_CLIENT_CRYPTO_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
key: /authentik/oidc/freshrss
metadataPolicy: None
property: s3cfg-remote
- secretKey: BUCKET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/talos-backups
metadataPolicy: None
property: BUCKET
- secretKey: AGE_X25519_PUBLIC_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/talos/etcd-backup
metadataPolicy: None
property: AGE_X25519_PUBLIC_KEY
property: crypto-key
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: talos-etcd-backup-external-secret
name: freshrss-cloudflared-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: talos-etcd-backup-external-secret
app.kubernetes.io/name: freshrss-cloudflared-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -114,87 +86,79 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: AWS_ACCESS_KEY_ID
- secretKey: cf-tunnel-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/etcd-backup
metadataPolicy: None
property: AWS_ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/etcd-backup
metadataPolicy: None
property: AWS_SECRET_ACCESS_KEY
- secretKey: .s3cfg
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/etcd-backup
metadataPolicy: None
property: s3cfg
- secretKey: BUCKET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/etcd-backup
metadataPolicy: None
property: BUCKET
- secretKey: AGE_X25519_PUBLIC_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/talos/etcd-backup
metadataPolicy: None
property: AGE_X25519_PUBLIC_KEY
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: talos-backup-ntfy-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: talos-backup-ntfy-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: NTFY_TOKEN
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /ntfy/user/cl01tl
key: /cloudflare/tunnels/freshrss
metadataPolicy: None
property: token
- secretKey: NTFY_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /ntfy/user/cl01tl
metadataPolicy: None
property: endpoint
- secretKey: NTFY_TOPIC
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/talos/etcd-backup
metadataPolicy: None
property: NTFY_TOPIC
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: talos-etcd-defrag-secret
name: freshrss-data-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: talos-etcd-defrag-secret
app.kubernetes.io/name: freshrss-data-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/freshrss/freshrss-data"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: freshrss-postgresql-17-cluster-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: freshrss-postgresql-17-cluster-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -202,10 +166,54 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: config
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/talos/etcd-defrag
key: /digital-ocean/home-infra/postgres-backups
metadataPolicy: None
property: config
property: access
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/postgres-backups
metadataPolicy: None
property: secret
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: freshrss-postgresql-17-cluster-backup-secret-garage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: freshrss-postgresql-17-cluster-backup-secret-garage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_REGION

View File

@@ -0,0 +1,35 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: freshrss-data-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: freshrss-data-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: freshrss-data
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: freshrss-data-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
moverSecurityContext:
runAsUser: 568
runAsGroup: 568
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups:
- 44
- 100
- 109
- 65539
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot

View File

@@ -0,0 +1,251 @@
freshrss:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
initContainers:
init-download-extension-1:
securityContext:
runAsUser: 0
image:
repository: alpine
tag: 3.22.2
pullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
apk add --no-cache git;
cd /tmp;
git clone -n --depth=1 --filter=tree:0 https://github.com/cn-tools/cntools_FreshRssExtensions.git;
cd cntools_FreshRssExtensions;
git sparse-checkout set --no-cone /xExtension-YouTubeChannel2RssFeed;
git checkout;
rm -rf /var/www/FreshRSS/extensions/xExtension-YouTubeChannel2RssFeed
cp -r xExtension-YouTubeChannel2RssFeed /var/www/FreshRSS/extensions
chown -R 568:568 /var/www/FreshRSS/extensions/xExtension-YouTubeChannel2RssFeed
resources:
requests:
cpu: 10m
memory: 128Mi
init-download-extension-2:
securityContext:
runAsUser: 0
image:
repository: alpine
tag: 3.22.2
pullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
apk add --no-cache git;
cd /tmp;
git clone -n --depth=1 --filter=tree:0 https://github.com/FreshRSS/Extensions.git;
cd Extensions;
git sparse-checkout set --no-cone /xExtension-ImageProxy;
git checkout;
rm -rf /var/www/FreshRSS/extensions/xExtension-ImageProxy
cp -r xExtension-ImageProxy /var/www/FreshRSS/extensions
chown -R 568:568 /var/www/FreshRSS/extensions/xExtension-ImageProxy
resources:
requests:
cpu: 10m
memory: 128Mi
init-download-extension-3:
securityContext:
runAsUser: 0
image:
repository: alpine
tag: 3.22.2
pullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
cd /tmp;
wget https://github.com/zimmra/xExtension-karakeep-button/archive/refs/tags/v1.1.tar.gz;
tar -xvzf *.tar.gz;
rm -rf /var/www/FreshRSS/extensions/xExtension-karakeep-button
mkdir /var/www/FreshRSS/extensions/xExtension-karakeep-button
cp -r /tmp/xExtension-karakeep-button-*/* /var/www/FreshRSS/extensions/xExtension-karakeep-button
chown -R 568:568 /var/www/FreshRSS/extensions/xExtension-karakeep-button
resources:
requests:
cpu: 10m
memory: 128Mi
containers:
main:
image:
repository: freshrss/freshrss
tag: 1.27.1
pullPolicy: IfNotPresent
env:
- name: PGID
value: "568"
- name: PUID
value: "568"
- name: TZ
value: US/Central
- name: FRESHRSS_ENV
value: production
- name: CRON_MIN
value: 13,43
- name: BASE_URL
value: https://rss.alexlebens.dev
- name: DB_HOST
valueFrom:
secretKeyRef:
name: freshrss-postgresql-17-cluster-app
key: host
- name: DB_BASE
valueFrom:
secretKeyRef:
name: freshrss-postgresql-17-cluster-app
key: dbname
- name: DB_USER
valueFrom:
secretKeyRef:
name: freshrss-postgresql-17-cluster-app
key: user
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: freshrss-postgresql-17-cluster-app
key: password
- name: FRESHRSS_INSTALL
value: |
--api-enabled
--base-url $(BASE_URL)
--db-base $(DB_BASE)
--db-host $(DB_HOST)
--db-password $(DB_PASSWORD)
--db-type pgsql
--db-user $(DB_USER)
--auth-type http_auth
--default-user admin
--language en
- name: FRESHRSS_USER
value: |
--api-password $(ADMIN_API_PASSWORD)
--email $(ADMIN_EMAIL)
--language en
--password $(ADMIN_PASSWORD)
--user admin
- name: OIDC_ENABLED
value: 1
- name: OIDC_PROVIDER_METADATA_URL
value: https://auth.alexlebens.dev/application/o/freshrss/.well-known/openid-configuration
- name: OIDC_X_FORWARDED_HEADERS
value: X-Forwarded-Port X-Forwarded-Proto X-Forwarded-Host
- name: OIDC_SCOPES
value: openid email profile
- name: OIDC_REMOTE_USER_CLAIM
value: preferred_username
envFrom:
- secretRef:
name: freshrss-oidc-secret
- secretRef:
name: freshrss-install-secret
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 80
protocol: HTTP
persistence:
data:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
- path: /var/www/FreshRSS/data
readOnly: false
extensions:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 1Gi
retain: true
advancedMounts:
main:
init-download-extension-1:
- path: /var/www/FreshRSS/extensions
readOnly: false
init-download-extension-2:
- path: /var/www/FreshRSS/extensions
readOnly: false
init-download-extension-3:
- path: /var/www/FreshRSS/extensions
readOnly: false
main:
- path: /var/www/FreshRSS/extensions
readOnly: false
cloudflared:
existingSecretName: freshrss-cloudflared-secret
postgres-17-cluster:
mode: recovery
cluster:
storage:
storageClass: local-path
walStorage:
storageClass: local-path
monitoring:
enabled: true
prometheusRule:
enabled: true
recovery:
method: objectStore
objectStore:
destinationPath: s3://postgres-backups/cl01tl/freshrss/freshrss-postgresql-17-cluster
endpointURL: http://garage-main.garage:3900
index: 1
endpointCredentials: freshrss-postgresql-17-cluster-backup-secret-garage
backup:
objectStore:
- name: external
destinationPath: s3://postgres-backups-ce540ddf106d186bbddca68a/cl01tl/freshrss/freshrss-postgresql-17-cluster
index: 1
retentionPolicy: "30d"
isWALArchiver: false
- name: garage-local
destinationPath: s3://postgres-backups/cl01tl/freshrss/freshrss-postgresql-17-cluster
index: 1
endpointURL: http://garage-main.garage:3900
endpointCredentials: freshrss-postgresql-17-cluster-backup-secret-garage
endpointCredentialsIncludeRegion: true
retentionPolicy: "3d"
isWALArchiver: true
# - name: garage-remote
# destinationPath: s3://postgres-backups/cl01tl/freshrss/freshrss-postgresql-17-cluster
# index: 1
# endpointURL: https://garage-ps10rp.boreal-beaufort.ts.net:3900
# endpointCredentials: freshrss-postgresql-17-cluster-backup-secret-garage
# retentionPolicy: "30d"
# data:
# compression: bzip2
# jobs: 2
scheduledBackups:
- name: daily-backup
suspend: false
schedule: "0 0 0 * * *"
backupName: external
- name: live-backup
suspend: false
immediate: true
schedule: "0 0 0 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: false
# schedule: "0 2 4 * * SAT"
# backupName: garage-remote

View File

@@ -4,24 +4,20 @@ version: 1.0.0
description: Home Assistant
keywords:
- home-assistant
- home-automation
home: https://docs.alexlebens.dev/applications/home-assistant/
- home
- automation
home: https://wiki.alexlebens.dev/s/5462c17e-cd39-4082-ad01-94545a2fa3ca
sources:
- https://www.home-assistant.io/
- https://github.com/home-assistant/core
- https://github.com/home-assistant/core/pkgs/container/home-assistant
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: home-assistant
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: volsync-target
alias: volsync-target-config
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/home-assistant.png
# renovate: datasource=github-releases depName=home-assistant/core
appVersion: 2026.3.4
appVersion: 2025.5.2

View File

@@ -14,11 +14,17 @@ spec:
data:
- secretKey: PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/home-assistant/code-server/auth
metadataPolicy: None
property: PASSWORD
- secretKey: SUDO_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/home-assistant/code-server/auth
metadataPolicy: None
property: SUDO_PASSWORD
---
@@ -38,5 +44,8 @@ spec:
data:
- secretKey: bearer-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/home-assistant/auth
metadataPolicy: None
property: bearer-token

View File

@@ -0,0 +1,58 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-home-assistant
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-home-assistant
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- home-assistant.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: home-assistant-main
port: 80
weight: 100
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-home-assistant-code-server
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-home-assistant-code-server
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- home-assistant-code-server.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: home-assistant-code-server
port: 8443
weight: 100

View File

@@ -0,0 +1,23 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: home-assistant
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: home-assistant
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: home-assistant
app.kubernetes.io/service: home-assistant-main
app.kubernetes.io/instance: {{ .Release.Name }}
endpoints:
- port: http
interval: 3m
scrapeTimeout: 1m
path: /api/prometheus
bearerTokenSecret:
name: home-assistant-token-secret
key: bearer-token

View File

@@ -0,0 +1,70 @@
home-assistant:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/home-assistant/home-assistant
tag: 2025.11.3
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
resources:
requests:
cpu: 50m
memory: 512Mi
code-server:
image:
repository: ghcr.io/linuxserver/code-server
tag: 4.106.2@sha256:a98afdbcb59559f11e5e8df284062e55da1076b2e470e13db4aae133ea82bad0
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
- name: PUID
value: 1000
- name: PGID
value: 1000
- name: DEFAULT_WORKSPACE
value: /config
envFrom:
- secretRef:
name: home-assistant-code-server-password-secret
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 8123
protocol: TCP
code-server:
controller: main
ports:
http:
port: 8443
targetPort: 8443
protocol: HTTP
persistence:
config:
forceRename: home-assistant-config
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
advancedMounts:
main:
main:
- path: /config
readOnly: false
code-server:
- path: /config/home-assistant
readOnly: false

View File

@@ -0,0 +1,27 @@
apiVersion: v2
name: homepage
version: 1.0.0
description: Homepage
keywords:
- homepage
- dashboard
home: https://wiki.alexlebens.dev/s/a5fabd91-3d89-4e2b-9417-06111aedaeaa
sources:
- https://github.com/gethomepage/homepage
- https://github.com/cloudflare/cloudflared
- https://github.com/gethomepage/homepage/pkgs/container/homepage
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: homepage
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.4.0
- name: cloudflared
alias: cloudflared
repository: oci://harbor.alexlebens.net/helm-charts
version: 1.23.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/homepage.png
appVersion: v1.2.0

View File

@@ -0,0 +1,21 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: homepage-dev-cloudflared-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: homepage-dev-cloudflared-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: cf-tunnel-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cloudflare/tunnels/homepage-dev
metadataPolicy: None
property: token

View File

@@ -0,0 +1,167 @@
homepage:
global:
nameOverride: homepage
controllers:
main:
type: deployment
annotations:
reloader.stakater.com/auto: "true"
strategy: Recreate
containers:
main:
image:
repository: ghcr.io/gethomepage/homepage
tag: v1.7.0
pullPolicy: IfNotPresent
env:
- name: HOMEPAGE_ALLOWED_HOSTS
value: home.alexlebens.dev
resources:
requests:
cpu: 10m
memory: 128Mi
configMaps:
config:
enabled: true
data:
docker.yaml: ""
kubernetes.yaml: ""
settings.yaml: |
favicon: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/logo-new-round.svg
headerStyle: clean
hideVersion: true
color: zinc
background:
image: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/background.jpg
brightness: 50
theme: dark
disableCollapse: true
widgets.yaml: |
- logo:
icon: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/logo-new-round.png
- datetime:
text_size: xl
format:
dateStyle: long
timeStyle: short
hour12: false
- openmeteo:
label: St. Paul
latitude: 44.954445
longitude: -93.091301
timezone: America/Chicago
units: metric
cache: 5
format:
maximumFractionDigits: 0
services.yaml: |
- Applications:
- Auth:
icon: sh-authentik.webp
description: Authentik
href: https://auth.alexlebens.dev
siteMonitor: https://auth.alexlebens.dev
statusStyle: dot
- Gitea:
icon: sh-gitea.webp
description: Gitea
href: https://gitea.alexlebens.dev
siteMonitor: https://gitea.alexlebens.dev
statusStyle: dot
- Code:
icon: sh-visual-studio-code.webp
description: VS Code
href: https://codeserver.alexlebens.dev
siteMonitor: https://codeserver.alexlebens.dev
statusStyle: dot
- Site:
icon: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/logo-new-round.png
description: Profile Website
href: https://www.alexlebens.dev
siteMonitor: https://www.alexlebens.dev
statusStyle: dot
- Content Management:
icon: directus.png
description: Directus
href: https://directus.alexlebens.dev
siteMonitor: https://directus.alexlebens.dev
statusStyle: dot
- Social Media Management:
icon: sh-postiz.webp
description: Postiz
href: https://postiz.alexlebens.dev
siteMonitor: https://postiz.alexlebens.dev
statusStyle: dot
- Chat:
icon: sh-element.webp
description: Matrix
href: https://chat.alexlebens.dev
siteMonitor: https://chat.alexlebens.dev
statusStyle: dot
- Wiki:
icon: sh-outline.webp
description: Outline
href: https://wiki.alexlebens.dev
siteMonitor: https://wiki.alexlebens.dev
statusStyle: dot
- Passwords:
icon: sh-vaultwarden-light.webp
description: Vaultwarden
href: https://passwords.alexlebens.dev
siteMonitor: https://passwords.alexlebens.dev
statusStyle: dot
- Bookmarks:
icon: sh-karakeep-light.webp
description: Karakeep
href: https://karakeep.alexlebens.dev
siteMonitor: https://karakeep.alexlebens.dev
statusStyle: dot
- RSS:
icon: sh-freshrss.webp
description: FreshRSS
href: https://rss.alexlebens.dev
siteMonitor: https://rss.alexlebens.dev
statusStyle: dot
bookmarks.yaml: ""
service:
http:
controller: main
ports:
http:
port: 80
targetPort: 3000
protocol: HTTP
persistence:
config:
enabled: true
type: configMap
name: homepage-dev
advancedMounts:
main:
main:
- path: /app/config/bookmarks.yaml
readOnly: true
mountPropagation: None
subPath: bookmarks.yaml
- path: /app/config/docker.yaml
readOnly: true
mountPropagation: None
subPath: docker.yaml
- path: /app/config/kubernetes.yaml
readOnly: true
mountPropagation: None
subPath: kubernetes.yaml
- path: /app/config/services.yaml
readOnly: true
mountPropagation: None
subPath: services.yaml
- path: /app/config/settings.yaml
readOnly: true
mountPropagation: None
subPath: settings.yaml
- path: /app/config/widgets.yaml
readOnly: true
mountPropagation: None
subPath: widgets.yaml
cloudflared:
existingSecretName: homepage-dev-cloudflared-secret

View File

@@ -16,7 +16,6 @@ dependencies:
- name: app-template
alias: homepage
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/homepage.png
# renovate: datasource=github-releases depName=gethomepage/homepage
appVersion: v1.12.1
appVersion: v1.2.0

View File

@@ -12,41 +12,6 @@ spec:
kind: ClusterSecretStore
name: vault
data:
- secretKey: HOMEPAGE_VAR_GITEA_API_TOKEN
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/gitea/auth/homepage
metadataPolicy: None
property: token
- secretKey: HOMEPAGE_VAR_ARGOCD_API_TOKEN
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/argocd/credentials/homepage
metadataPolicy: None
property: token
- secretKey: HOMEPAGE_VAR_KOMODO_API_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/komodo/homepage
metadataPolicy: None
property: key
- secretKey: HOMEPAGE_VAR_KOMODO_API_SECRET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/komodo/homepage
metadataPolicy: None
property: secret
- secretKey: HOMEPAGE_VAR_JELLYSTAT_API_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/jellystat/homepage
metadataPolicy: None
property: key
- secretKey: HOMEPAGE_VAR_SYNOLOGY_USER
remoteRef:
conversionStrategy: Default
@@ -61,13 +26,20 @@ spec:
key: /synology/auth/cl01tl
metadataPolicy: None
property: password
- secretKey: HOMEPAGE_VAR_UNIFI_API_KEY
- secretKey: HOMEPAGE_VAR_UNIFI_USER
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /unifi/auth/cl01tl
metadataPolicy: None
property: api-key
property: user
- secretKey: HOMEPAGE_VAR_UNIFI_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /unifi/auth/cl01tl
metadataPolicy: None
property: password
- secretKey: HOMEPAGE_VAR_SONARR_KEY
remoteRef:
conversionStrategy: Default

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-homepage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-homepage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- home.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: homepage
port: 80
weight: 100

View File

@@ -0,0 +1,46 @@
apiVersion: v1
kind: Service
metadata:
name: gitea-ps10rp
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: gitea-ps10rp
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
annotations:
tailscale.com/tailnet-fqdn: gitea-ps10rp.boreal-beaufort.ts.net
spec:
externalName: placeholder
type: ExternalName
---
apiVersion: v1
kind: Service
metadata:
name: home-ps10rp
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: home-ps10rp
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
annotations:
tailscale.com/tailnet-fqdn: home-ps10rp.boreal-beaufort.ts.net
spec:
externalName: placeholder
type: ExternalName
---
apiVersion: v1
kind: Service
metadata:
name: garage-ui-ps10rp
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: garage-ps10rp
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
annotations:
tailscale.com/tailnet-fqdn: garage-ui-ps10rp.boreal-beaufort.ts.net
spec:
externalName: placeholder
type: ExternalName

View File

@@ -4,10 +4,9 @@ homepage:
controllers:
main:
type: deployment
replicas: 1
strategy: RollingUpdate
annotations:
reloader.stakater.com/auto: "true"
strategy: Recreate
serviceAccount:
name: homepage
pod:
@@ -16,7 +15,7 @@ homepage:
main:
image:
repository: ghcr.io/gethomepage/homepage
tag: v1.12.1
tag: v1.7.0
pullPolicy: IfNotPresent
env:
- name: HOMEPAGE_ALLOWED_HOSTS
@@ -36,10 +35,6 @@ homepage:
config:
enabled: true
data:
custom.css: |
html {
font-size: 18px;
}
docker.yaml: ""
kubernetes.yaml: |
mode: cluster
@@ -47,10 +42,10 @@ homepage:
favicon: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/logo-new-round.svg
headerStyle: clean
hideVersion: true
cardBlur: xs
color: slate
color: zinc
background:
image: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/background.jpg
brightness: 50
theme: dark
disableCollapse: true
layout:
@@ -81,20 +76,20 @@ homepage:
- Storage:
tab: Services
icon: mdi-database-#ffffff
- Content:
- Servarr:
tab: Services
icon: mdi-multimedia-#ffffff
- TV Shows:
tab: Content
tab: Servarr
icon: mdi-television-#ffffff
- Movies:
tab: Content
tab: Servarr
icon: mdi-filmstrip-#ffffff
- Music:
tab: Content
tab: Servarr
icon: mdi-music-box-multiple-#ffffff
- Books:
tab: Content
tab: Servarr
icon: mdi-book-open-variant-#ffffff
- External Services:
tab: Bookmarks
@@ -134,11 +129,23 @@ homepage:
maximumFractionDigits: 0
services.yaml: |
- Media:
- Plex:
icon: sh-plex.webp
description: Media server
href: https://plex.alexlebens.net
siteMonitor: http://plex.plex:32400
statusStyle: dot
- Jellyfin:
icon: sh-jellyfin.webp
description: Media server
href: https://jellyfin.alexlebens.net
siteMonitor: http://jellyfin.jellyfin:80
statusStyle: dot
- Media Requests:
icon: sh-seerr.webp
description: Seerr
href: https://seerr.alexlebens.net
siteMonitor: http://seerr-seerr-chart.seerr:80
icon: sh-overseerr.webp
description: Overseer
href: https://overseerr.alexlebens.net
siteMonitor: http://overseerr.overseerr:80
statusStyle: dot
- Media Tracking:
icon: sh-yamtrack.webp
@@ -146,41 +153,17 @@ homepage:
href: https://yamtrack.alexlebens.net
siteMonitor: http://yamtrack.yamtrack:80
statusStyle: dot
- Movie Roulette:
icon: https://raw.githubusercontent.com/sahara101/Movie-Roulette/refs/heads/main/static/icons/icon.png
description: Movie Roulette
href: https://movie-roulette.alexlebens.net
siteMonitor: http://movie-roulette.movie-roulette:80
statusStyle: dot
- Movies and TV:
icon: sh-jellyfin.webp
description: Jellyfin
href: https://jellyfin.alexlebens.net
siteMonitor: http://jellyfin.jellyfin:80
statusStyle: dot
- Youtube Archive:
icon: sh-tube-archivist-light.webp
description: TubeArchivist
description: TubeAchivist
href: https://tubearchivist.alexlebens.net/login
siteMonitor: http://tubearchivist.tubearchivist:80
statusStyle: dot
- Music:
icon: sh-feishin.webp
description: Feishin
href: https://feishin.alexlebens.net
siteMonitor: http://navidrome-feishin.navidrome:80
statusStyle: dot
- Podcasts and Audiobooks:
icon: sh-audiobookshelf.webp
description: Audiobookshelf
href: https://audiobookshelf.alexlebens.net
siteMonitor: http://audiobookshelf.audiobookshelf:80
statusStyle: dot
- Photos:
icon: sh-immich.webp
description: Immich
href: https://immich.alexlebens.net
siteMonitor: http://immich.immich:2283
siteMonitor: http://immich-main.immich:2283
statusStyle: dot
- Pictures:
icon: sh-photoview.webp
@@ -188,17 +171,17 @@ homepage:
href: https://photoview.alexlebens.net
siteMonitor: http://photoview.photoview:80
statusStyle: dot
- Pictures:
icon: https://raw.githubusercontent.com/foldergram/foldergram/refs/heads/main/client/public/icon-512.png
description: Foldergram
href: https://foldergram.alexlebens.net
siteMonitor: http://foldergram.foldergram:80
- Podcasts and Audiobooks:
icon: sh-audiobookshelf.webp
description: Audiobookshelf
href: https://audiobookshelf.alexlebens.net
siteMonitor: http://audiobookshelf.audiobookshelf:80
statusStyle: dot
- Books:
icon: sh-booklore.webp
description: Grimmory
href: https://grimmory.alexlebens.net
siteMonitor: http://grimmory.grimmory:80
description: Booklore
href: https://booklore.alexlebens.net
siteMonitor: http://booklore.booklore:80
statusStyle: dot
- Public:
- Site:
@@ -207,23 +190,11 @@ homepage:
href: https://www.alexlebens.dev
siteMonitor: https://www.alexlebens.dev
statusStyle: dot
- Documentation:
icon: https://web-assets-3bfcb5585cbd63dc365d32a3.nyc3.cdn.digitaloceanspaces.com/alexlebens-net/logo-new-round.png
description: Documentation Wiki
href: https://docs.alexlebens.dev
siteMonitor: https://docs.alexlebens.dev
statusStyle: dot
- Content Management:
icon: directus.png
description: Directus
href: https://directus.alexlebens.net
siteMonitor: http://directus.directus:80
statusStyle: dot
- Analytics:
icon: sh-rybbit-light.webp
description: Rybbit
href: https://rybbit.alexlebens.dev
siteMonitor: http://rybbit-client.rybbit:3002
href: https://directus.alexlebens.dev
siteMonitor: https://directus.alexlebens.dev
statusStyle: dot
- Social Media Management:
icon: sh-postiz.webp
@@ -268,12 +239,6 @@ homepage:
href: https://home-assistant.alexlebens.net
siteMonitor: http://home-assistant-main.home-assistant:80
statusStyle: dot
- Location:
icon: sh-dawarich.webp
description: Dawarich
href: https://dawarich.alexlebens.net
siteMonitor: http://dawarich.dawarich:80
statusStyle: dot
- Budgeting:
icon: sh-actual-budget.webp
description: Actual
@@ -310,12 +275,6 @@ homepage:
href: https://kiwix.alexlebens.net
siteMonitor: http://kiwix.kiwix:80
statusStyle: dot
- Draw:
icon: sh-excalidraw.webp
description: Excalidraw
href: https://excalidraw.alexlebens.net
siteMonitor: http://excalidraw.excalidraw:80
statusStyle: dot
- Code:
- Code (Public):
icon: sh-gitea.webp
@@ -323,16 +282,6 @@ homepage:
href: https://gitea.alexlebens.dev
siteMonitor: https://gitea.alexlebens.dev
statusStyle: dot
namespace: gitea
app: gitea
podSelector: >-
app.kubernetes.io/instance in (
gitea
)
widget:
type: gitea
url: http://gitea-http.gitea:3000
key: {{ "{{HOMEPAGE_VAR_GITEA_API_TOKEN}}" }}
- Code (Local):
icon: sh-gitea.webp
description: Gitea
@@ -357,43 +306,37 @@ homepage:
href: https://home-assistant-code-server.alexlebens.net
siteMonitor: http://home-assistant-code-server.home-assistant:8443
statusStyle: dot
- Automation:
- Continuous Deployment:
icon: sh-argo-cd.webp
description: ArgoCD
href: https://argocd.alexlebens.net
siteMonitor: http://argocd-server.argocd:80
statusStyle: dot
namespace: argocd
app: argocd
podSelector: >-
app.kubernetes.io/instance in (
argocd
)
widget:
type: argocd
url: http://argocd-server.argocd:80
key: {{ "{{HOMEPAGE_VAR_ARGOCD_API_TOKEN}}" }}
fields: ["outOfSync", "progressing", "suspended", "degraded"]
- Docker Deployment:
icon: sh-komodo-light.webp
description: Komodo
href: https://komodo.alexlebens.net
siteMonitor: http://komodo-main.komodo:80
statusStyle: dot
widget:
type: komodo
url: http://komodo-main.komodo:80
key: {{ "{{HOMEPAGE_VAR_KOMODO_API_KEY}}" }}
secret: {{ "{{HOMEPAGE_VAR_KOMODO_API_SECRET}}" }}
showStacks: true
fields: ["running", "down", "unhealthy", "unknown"]
- Automation:
- Deployment Workflows:
icon: sh-argo-cd.webp
description: Argo Workflows
href: https://argo-workflows.alexlebens.net
siteMonitor: http://argo-workflows-server.argo-workflows:2746
statusStyle: dot
- API Workflows:
icon: sh-n8n.webp
description: n8n
href: https://n8n.alexlebens.net
siteMonitor: http://n8n-main.n8n:80
statusStyle: dot
- Jobs:
icon: https://raw.githubusercontent.com/mshade/kronic/main/static/android-chrome-192x192.png
description: Kronic
href: https://kronic.alexlebens.net
siteMonitor: http://kronic.kronic:80
statusStyle: dot
- Uptime:
icon: sh-gatus.webp
description: Gatus
@@ -413,36 +356,24 @@ homepage:
href: https://headlamp.alexlebens.net
siteMonitor: http://headlamp.headlamp:80
statusStyle: dot
- Dashboard:
icon: sh-grafana.webp
description: Grafana
href: https://grafana.alexlebens.net
siteMonitor: http://grafana-main-service.grafana-operator:3000/api/health
statusStyle: dot
- Network Monitoring:
icon: sh-cilium.webp
description: Hubble for Cilium
href: https://hubble.alexlebens.net
siteMonitor: http://hubble-ui.kube-system:80
statusStyle: dot
namespace: kube-system
app: cilium-operator
podSelector: >-
app.kubernetes.io/part-of in (
cilium
)
- Dashboard:
icon: sh-grafana.webp
description: Grafana
href: https://grafana.alexlebens.net
siteMonitor: http://grafana-main-service.grafana-operator:3000/api/health
statusStyle: dot
- Metrics:
icon: sh-prometheus.webp
description: Prometheus
href: https://prometheus.alexlebens.net
siteMonitor: http://kube-prometheus-stack-prometheus.kube-prometheus-stack:9090
statusStyle: dot
namespace: kube-prometheus-stack
app: prometheus
podSelector: >-
app.kubernetes.io/instance in (
kube-prometheus-stack
)
widget:
type: prometheus
url: http://kube-prometheus-stack-prometheus.kube-prometheus-stack:9090
@@ -463,18 +394,18 @@ homepage:
query: prometheus_tsdb_storage_blocks_bytes
format:
type: bytes
- Tautulli:
icon: sh-tautulli.webp
description: Plex Monitoring
href: https://tautulli.alexlebens.net
siteMonitor: http://tautulli.tautulli:80
statusStyle: dot
- Jellystat:
icon: sh-jellystat.webp
description: Jellyfin Monitoring
href: https://jellystat.alexlebens.net
siteMonitor: http://jellystat.jellystat:80
statusStyle: dot
- MediaLyze:
icon: https://raw.githubusercontent.com/frederikemmer/MediaLyze/d8f69c0628bac7c047b90f91a66341648029c273/frontend/public/favicon.svg
description: Jellyfin Media Monitoring
href: https://medialyze.alexlebens.net
siteMonitor: http://medialyze.medialyze:80
statusStyle: dot
- Services:
- Auth (Public):
icon: sh-authentik.webp
@@ -522,11 +453,6 @@ homepage:
href: https://unifi.alexlebens.net
siteMonitor: https://unifi.alexlebens.net
statusStyle: dot
widget:
type: unifi
url: https://unifi.alexlebens.net
key: {{ "{{HOMEPAGE_VAR_UNIFI_API_KEY}}" }}
fields: ["uptime", "wan", "lan_devices", "wlan_devices"]
- Network Attached Storage:
icon: sh-synology-light.webp
description: Synology
@@ -581,6 +507,12 @@ homepage:
href: https://garage-ui-ps10rp.boreal-beaufort.ts.net
siteMonitor: https://garage-ui-ps10rp.boreal-beaufort.ts.net
statusStyle: dot
- Database:
icon: sh-pgadmin-light.webp
description: PGAdmin
href: https://pgadmin.alexlebens.net
siteMonitor: http://pgadmin.pgadmin:80
statusStyle: dot
- Database:
icon: sh-whodb.webp
description: WhoDB
@@ -599,10 +531,7 @@ homepage:
href: https://backrest.alexlebens.net
siteMonitor: http://backrest.backrest:80
statusStyle: dot
widget:
type: backrest
url: http://backrest.backrest:80
- Content:
- Servarr:
- qUI:
icon: https://raw.githubusercontent.com/autobrr/qui/8487c818886df9abb2b1456f43b54e0ba180a2bd/web/public/icons.svg
description: qbitorrent
@@ -619,18 +548,18 @@ homepage:
href: https://prowlarr.alexlebens.net
siteMonitor: http://prowlarr.prowlarr:80
statusStyle: dot
- Huntarr:
icon: https://raw.githubusercontent.com/plexguide/Huntarr.io/main/frontend/static/logo/128.png
description: Content upgrader
href: https://huntarr.alexlebens.net
siteMonitor: http://huntarr.huntarr:80
statusStyle: dot
- Bazarr:
icon: sh-bazarr.webp
description: Subtitles
href: https://bazarr.alexlebens.net
siteMonitor: http://bazarr.bazarr:80
statusStyle: dot
- Houndarr:
icon: https://raw.githubusercontent.com/av1155/houndarr/main/src/houndarr/static/img/houndarr-logo-dark.png
description: Media Searches
href: https://houndarr.alexlebens.net
siteMonitor: http://houndarr.houndarr:80
statusStyle: dot
- Tdarr:
icon: sh-tdarr.webp
description: Media transcoding and health checks
@@ -738,32 +667,38 @@ homepage:
url: http://lidarr.lidarr:80
key: {{ "{{HOMEPAGE_VAR_LIDARR_KEY}}" }}
fields: ["wanted", "queued", "artists"]
- Yubal:
icon: sh-yubal.webp
description: Replicate Youtube playlists
href: https://yubal.alexlebens.net
siteMonitor: http://yubal.yubal:80
- LidaTube:
icon: sh-lidatube.webp
description: Searches for Music
href: https://lidatube.alexlebens.net
siteMonitor: http://lidatube.lidatube:80
statusStyle: dot
- Music Grabber:
icon: sh-music-service.webp
description: Replicate Music playlists
href: https://music-grabber.alexlebens.net
siteMonitor: http://music-grabber.music-grabber:80
statusStyle: dot
- slskd:
- Soulseek:
icon: sh-slskd.webp
description: slskd
href: https://slskd.alexlebens.net
siteMonitor: http://slskd.slskd:5030
statusStyle: dot
- Books:
- Shelfmark:
icon: sh-shelfmark.webp
- Ephemera:
icon: sh-ephemera.webp
description: Books
href: https://shelfmark.alexlebens.net
siteMonitor: http://shelfmark.shelfmark:80
href: https://ephemera.alexlebens.net
siteMonitor: http://ephemera.ephemera:80
statusStyle: dot
- Listenarr:
icon: sh-audiobookrequest.webp
description: Audiobooks
href: https://listenarr.alexlebens.net
siteMonitor: http://listenarr.listenarr:80
statusStyle: dot
- Other Homes:
- Dev:
icon: sh-homepage.webp
description: Public Homepage
href: https://home.alexlebens.dev
siteMonitor: https://home.alexlebens.dev
statusStyle: dot
- Lebens Home:
icon: sh-homepage.webp
description: Lebens Homepage
@@ -778,6 +713,9 @@ homepage:
- Digital Ocean:
- abbr: DO
href: https://www.digitalocean.com/
- AWS:
- abbr: AW
href: https://aws.amazon.com/console/
- Cloudflare:
- abbr: CF
href: https://dash.cloudflare.com/b76e303258b84076ee01fd0f515c0768
@@ -787,12 +725,12 @@ homepage:
- ProtonVPN:
- abbr: PV
href: https://account.protonvpn.com/
- AirVPN:
- abbr: AV
href: https://airvpn.org/
- Unifi:
- abbr: UF
href: https://unifi.ui.com/
- Pushover:
- abbr: PO
href: https://pushover.net
- ReCaptcha:
- abbr: RC
href: https://www.google.com/recaptcha/admin/site/698983587
@@ -823,27 +761,6 @@ homepage:
port: 80
targetPort: 3000
protocol: HTTP
route:
main:
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- home.alexlebens.net
rules:
- backendRefs:
- group: ''
kind: Service
name: homepage
port: 80
weight: 100
matches:
- path:
type: PathPrefix
value: /
persistence:
config:
enabled: true
@@ -852,10 +769,10 @@ homepage:
advancedMounts:
main:
main:
- path: /app/config/custom.css
- path: /app/config/bookmarks.yaml
readOnly: true
mountPropagation: None
subPath: custom.css
subPath: bookmarks.yaml
- path: /app/config/docker.yaml
readOnly: true
mountPropagation: None
@@ -864,6 +781,10 @@ homepage:
readOnly: true
mountPropagation: None
subPath: kubernetes.yaml
- path: /app/config/services.yaml
readOnly: true
mountPropagation: None
subPath: services.yaml
- path: /app/config/settings.yaml
readOnly: true
mountPropagation: None
@@ -872,11 +793,3 @@ homepage:
readOnly: true
mountPropagation: None
subPath: widgets.yaml
- path: /app/config/services.yaml
readOnly: true
mountPropagation: None
subPath: services.yaml
- path: /app/config/bookmarks.yaml
readOnly: true
mountPropagation: None
subPath: bookmarks.yaml

View File

@@ -0,0 +1,21 @@
apiVersion: v2
name: huntarr
version: 1.0.0
description: Huntarr
keywords:
- huntarr
- servarr
home: https://wiki.alexlebens.dev/s/831ca16e-d308-4d7b-9213-f841834c1181
sources:
- https://github.com/plexguide/Huntarr.io
- https://hub.docker.com/r/huntarr/huntarr
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: huntarr
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/huntarr.png
appVersion: 7.0.0

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-huntarr
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-huntarr
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- huntarr.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: huntarr
port: 80
weight: 100

View File

@@ -0,0 +1,39 @@
huntarr:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/plexguide/huntarr
tag: 8.2.10
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
resources:
requests:
cpu: 100m
memory: 256Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 9705
protocol: HTTP
persistence:
config:
forceRename: huntarr-config
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
advancedMounts:
main:
main:
- path: /config
readOnly: false

View File

@@ -17,19 +17,10 @@ dependencies:
- name: app-template
alias: immich
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
version: 4.4.0
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
repository: oci://harbor.alexlebens.net/helm-charts
- name: valkey
alias: valkey
version: 0.5.0
repository: oci://harbor.alexlebens.net/helm-charts
- name: volsync-target
alias: volsync-target-data
version: 0.8.0
alias: postgres-17-cluster
version: 6.16.0
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/immich.png
# renovate: datasource=github-releases depName=immich-app/immich
appVersion: v2.6.3
appVersion: v2.0.1

View File

@@ -0,0 +1,88 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: immich-config-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: immich-config-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: immich.json
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/immich/config
metadataPolicy: None
property: immich.json
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: immich-postgresql-17-cluster-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: immich-postgresql-17-cluster-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/postgres-backups
metadataPolicy: None
property: access
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/postgres-backups
metadataPolicy: None
property: secret
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: immich-postgresql-17-cluster-backup-secret-garage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: immich-postgresql-17-cluster-backup-secret-garage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/postgres-backups
metadataPolicy: None
property: ACCESS_REGION

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-immich
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-immich
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- immich.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: immich-main
port: 2283
weight: 100

View File

@@ -1,14 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: yubal-nfs-storage
name: immich-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: yubal-nfs-storage
app.kubernetes.io/name: immich-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: yubal-nfs-storage
volumeName: immich-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany

View File

@@ -1,10 +1,10 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: qbittorrent-nfs-storage
name: immich-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: qbittorrent-nfs-storage
app.kubernetes.io/name: immich-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -15,7 +15,7 @@ spec:
accessModes:
- ReadWriteMany
nfs:
path: /volume2/Storage
path: /volume2/Storage/Immich
server: synologybond.alexlebens.net
mountOptions:
- vers=4

View File

@@ -0,0 +1,32 @@
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisReplication
metadata:
name: redis-replication-immich
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: redis-replication-immich
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
clusterSize: 3
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
kubernetesConfig:
image: quay.io/opstree/redis:v8.0.3
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 50m
memory: 128Mi
storage:
volumeClaimTemplate:
spec:
storageClassName: ceph-block
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
redisExporter:
enabled: true
image: quay.io/opstree/redis-exporter:v1.48.0

View File

@@ -0,0 +1,44 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: immich
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: immich
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: immich
app.kubernetes.io/instance: {{ .Release.Name }}
endpoints:
- port: metrics-api
interval: 3m
scrapeTimeout: 1m
path: /metrics
- port: metrics-ms
interval: 3m
scrapeTimeout: 1m
path: /metrics
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: redis-replication-immich
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: redis-replication-immich
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
redis-operator: "true"
env: production
spec:
selector:
matchLabels:
redis_setup_type: replication
endpoints:
- port: redis-exporter
interval: 30s
scrapeTimeout: 10s

View File

@@ -9,7 +9,7 @@ immich:
main:
image:
repository: ghcr.io/immich-app/immich-server
tag: v2.6.3
tag: v2.3.1
pullPolicy: IfNotPresent
env:
- name: TZ
@@ -18,34 +18,36 @@ immich:
value: all
- name: IMMICH_CONFIG_FILE
value: /config/immich.json
- name: IMMICH_MACHINE_LEARNING_URL
value: http://immich-machine-learning.immich:3003
- name: REDIS_HOSTNAME
value: immich-valkey
value: redis-replication-immich-master
- name: DB_VECTOR_EXTENSION
value: vectorchord
- name: DB_HOSTNAME
valueFrom:
secretKeyRef:
name: immich-postgresql-18-cluster-app
name: immich-postgresql-17-cluster-app
key: host
- name: DB_DATABASE_NAME
valueFrom:
secretKeyRef:
name: immich-postgresql-18-cluster-app
name: immich-postgresql-17-cluster-app
key: dbname
- name: DB_PORT
valueFrom:
secretKeyRef:
name: immich-postgresql-18-cluster-app
name: immich-postgresql-17-cluster-app
key: port
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: immich-postgresql-18-cluster-app
name: immich-postgresql-17-cluster-app
key: user
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: immich-postgresql-18-cluster-app
name: immich-postgresql-17-cluster-app
key: password
probes:
liveness:
@@ -88,6 +90,52 @@ immich:
gpu.intel.com/i915: 1
cpu: 10m
memory: 512Mi
machine-learning:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/immich-app/immich-machine-learning
tag: v2.3.1
pullPolicy: IfNotPresent
env:
- name: TRANSFORMERS_CACHE
value: /cache
probes:
liveness:
enabled: true
custom: true
spec:
httpGet:
path: /ping
port: 3003
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readiness:
enabled: true
custom: true
spec:
httpGet:
path: /ping
port: 3003
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
startup:
enabled: false
resources:
limits:
gpu.intel.com/i915: 1
requests:
gpu.intel.com/i915: 1
cpu: 10m
memory: 256Mi
service:
main:
controller: main
@@ -104,43 +152,13 @@ immich:
port: 8082
targetPort: 8082
protocol: TCP
serviceMonitor:
main:
selector:
matchLabels:
app.kubernetes.io/name: immich
app.kubernetes.io/instance: immich
serviceName: '{{ include "bjw-s.common.lib.chart.names.fullname" $ }}'
endpoints:
- port: metrics-api
interval: 3m
scrapeTimeout: 1m
path: /metrics
- port: metrics-ms
interval: 3m
scrapeTimeout: 1m
path: /metrics
route:
main:
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- immich.alexlebens.net
rules:
- backendRefs:
- group: ''
kind: Service
name: immich
port: 2283
weight: 100
matches:
- path:
type: PathPrefix
value: /
machine-learning:
controller: machine-learning
ports:
http:
port: 3003
targetPort: 3003
protocol: TCP
persistence:
config:
enabled: true
@@ -153,23 +171,37 @@ immich:
readOnly: true
mountPropagation: None
subPath: immich.json
data:
forceRename: immich
cache:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 50Gi
size: 10Gi
retain: true
advancedMounts:
machine-learning:
main:
- path: /cache
readOnly: false
media:
existingClaim: immich-nfs-storage
advancedMounts:
main:
main:
- path: /usr/src/app/upload
readOnly: false
postgres-18-cluster:
postgres-17-cluster:
mode: recovery
cluster:
image:
repository: ghcr.io/tensorchord/cloudnative-vectorchord
tag: 18.0-0.5.3
tag: 17.5-0.4.3
storage:
storageClass: local-path
walStorage:
storageClass: local-path
monitoring:
enabled: true
prometheusRule:
enabled: true
postgresql:
parameters:
shared_buffers: 256MB
@@ -184,57 +216,45 @@ postgres-18-cluster:
recovery:
method: objectStore
objectStore:
destinationPath: s3://postgres-backups/cl01tl/immich/immich-postgresql-17-cluster
endpointURL: http://garage-main.garage:3900
index: 1
endpointCredentials: immich-postgresql-17-cluster-backup-secret-garage
backup:
objectStore:
- name: external
destinationPath: s3://postgres-backups-ce540ddf106d186bbddca68a/cl01tl/immich/immich-postgresql-17-cluster
index: 2
retentionPolicy: "30d"
isWALArchiver: false
- name: garage-local
destinationPath: s3://postgres-backups/cl01tl/immich/immich-postgresql-17-cluster
index: 1
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
endpointURL: http://garage-main.garage:3900
endpointCredentials: immich-postgresql-17-cluster-backup-secret-garage
endpointCredentialsIncludeRegion: true
retentionPolicy: "3d"
isWALArchiver: true
# - name: garage-remote
# destinationPath: s3://postgres-backups/cl01tl/immich/immich-postgresql-17-cluster
# index: 1
# destinationBucket: postgres-backups
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# retentionPolicy: "90d"
# endpointURL: https://garage-ps10rp.boreal-beaufort.ts.net:3900
# endpointCredentials: immich-postgresql-17-cluster-backup-secret-garage
# retentionPolicy: "30d"
# data:
# compression: bzip2
# - name: external
# index: 1
# endpointURL: https://nyc3.digitaloceanspaces.com
# destinationBucket: postgres-backups-ce540ddf106d186bbddca68a
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# isWALArchiver: false
# jobs: 2
scheduledBackups:
- name: daily-backup
suspend: false
schedule: "0 0 0 * * *"
backupName: external
- name: live-backup
suspend: false
immediate: true
schedule: "0 40 14 * * *"
schedule: "0 0 0 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: true
# immediate: true
# schedule: "0 0 4 * * SAT"
# suspend: false
# schedule: "0 4 4 * * SAT"
# backupName: garage-remote
# - name: daily-backup
# suspend: true
# immediate: true
# schedule: "0 0 0 * * *"
# backupName: external
volsync-target-data:
pvcTarget: immich
local:
enabled: true
schedule: 24 8 * * *
restic:
cacheCapacity: 10Gi
remote:
enabled: true
schedule: 24 9 * * *
restic:
cacheCapacity: 10Gi
external:
enabled: true
schedule: 24 10 * * *
restic:
cacheCapacity: 10Gi

View File

@@ -12,10 +12,7 @@ keywords:
home: https://wiki.alexlebens.dev/s/a58be5b0-7935-458a-b990-b45223e39d68
sources:
- https://github.com/jellyfin/jellyfin
- https://github.com/rebelcore/jellyfin_exporter
- https://github.com/meilisearch/meilisearch
- https://hub.docker.com/r/jellyfin/jellyfin
- https://hub.docker.com/r/rebelcore/jellyfin-exporter
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
@@ -23,14 +20,6 @@ dependencies:
- name: app-template
alias: jellyfin
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: meilisearch
version: 0.29.0
repository: https://meilisearch.github.io/meilisearch-kubernetes
- name: volsync-target
alias: volsync-target-config
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
version: 4.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/jellyfin.png
# renovate: datasource=github-releases depName=jellyfin/jellyfin
appVersion: 10.11.6
appVersion: 10.10.7

View File

@@ -0,0 +1,55 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: jellyfin-config-backup-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: jellyfin-config-backup-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
template:
mergePolicy: Merge
engineVersion: v2
data:
RESTIC_REPOSITORY: "{{ `{{ .BUCKET_ENDPOINT }}` }}/jellyfin/jellyfin-config"
data:
- secretKey: BUCKET_ENDPOINT
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: S3_BUCKET_ENDPOINT
- secretKey: RESTIC_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: RESTIC_PASSWORD
- secretKey: AWS_DEFAULT_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/volsync/restic/config
metadataPolicy: None
property: AWS_DEFAULT_REGION
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: access_key
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/volsync-backups
metadataPolicy: None
property: secret_key

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route-jellyfin
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: http-route-jellyfin
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- jellyfin.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: jellyfin
port: 80
weight: 100

View File

@@ -0,0 +1,26 @@
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
name: jellyfin-config-backup-source
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: jellyfin-config-backup-source
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
sourcePVC: jellyfin-config
trigger:
schedule: 0 4 * * *
restic:
pruneIntervalDays: 7
repository: jellyfin-config-backup-secret
retain:
hourly: 1
daily: 3
weekly: 2
monthly: 2
yearly: 4
copyMethod: Snapshot
storageClassName: ceph-block
volumeSnapshotClassName: ceph-blockpool-snapshot
cacheCapacity: 10Gi

View File

@@ -0,0 +1,68 @@
jellyfin:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/jellyfin/jellyfin
tag: 10.11.3
pullPolicy: IfNotPresent
env:
- name: TZ
value: US/Central
- name: JELLYFIN_hostwebclient
value: true
- name: JELLYFIN_PublishedServerUrl
value: https://jellyfin.alexlebens.net/
resources:
limits:
gpu.intel.com/i915: 1
requests:
gpu.intel.com/i915: 1
cpu: 1
memory: 2Gi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 8096
protocol: HTTP
persistence:
config:
forceRename: jellyfin-config
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 100Gi
retain: true
advancedMounts:
main:
main:
- path: /config
readOnly: false
cache:
type: emptyDir
advancedMounts:
main:
main:
- path: /cache
readOnly: false
media:
existingClaim: jellyfin-nfs-storage
advancedMounts:
main:
main:
- path: /mnt/store
readOnly: false
youtube:
existingClaim: jellyfin-youtube-nfs-storage
advancedMounts:
main:
main:
- path: /mnt/youtube
readOnly: true

Some files were not shown because too many files have changed in this diff Show More