Compare commits

...

38 Commits

Author SHA1 Message Date
Morten Olsen
8f5e148bb2 update 2025-09-04 18:22:33 +02:00
Morten Olsen
21262705a7 fixes 2025-09-03 23:06:59 +02:00
Morten Olsen
4d46998668 more-charts 2025-09-03 21:41:58 +02:00
Morten Olsen
00d90bfa21 more-stuff 2025-09-03 17:24:27 +02:00
Morten Olsen
03e406322f more stuff 2025-09-03 15:16:50 +02:00
Morten Olsen
5ee7a76443 more stuff 2025-09-03 14:33:48 +02:00
mortenolsenzn
683de402ff Merge pull request #1 from morten-olsen/rewrite2
Rewrite2
2025-09-03 12:24:40 +02:00
Morten Olsen
e8e939ad19 fixes 2025-08-22 11:44:53 +02:00
Morten Olsen
1b5b5145b0 stuff 2025-08-22 07:35:50 +02:00
Morten Olsen
cfd2d76873 more 2025-08-20 22:45:30 +02:00
Morten Olsen
9e5081ed9b updates 2025-08-20 14:58:34 +02:00
Morten Olsen
3ab2b1969a stuff 2025-08-19 22:05:41 +02:00
Morten Olsen
a27b563113 rewrite2 2025-08-18 08:02:48 +02:00
Morten Olsen
295472a028 update 2025-08-15 22:01:18 +02:00
Morten Olsen
91298b3cf7 update 2025-08-15 21:20:23 +02:00
Morten Olsen
638c288a5c update 2025-08-15 20:52:17 +02:00
Morten Olsen
2be6bdca84 update 2025-08-15 20:45:28 +02:00
Morten Olsen
f362f4afc4 fix: missing permissions 2025-08-13 09:01:30 +02:00
Morten Olsen
9fadbf75fb publish operator yaml 2025-08-13 08:50:17 +02:00
Morten Olsen
2add15d283 fix: authentik port 2025-08-12 23:25:03 +02:00
Morten Olsen
5426495be5 updates 2025-08-12 23:22:47 +02:00
Morten Olsen
b8bb16ccbb updates 2025-08-12 22:32:09 +02:00
Morten Olsen
d4b56007f1 add authentik connection crd 2025-08-12 08:36:29 +02:00
Morten Olsen
130bfec468 fix reconciliation of db 2025-08-11 20:00:01 +02:00
Morten Olsen
ddb3c79657 fix pg db 2025-08-11 15:00:06 +02:00
Morten Olsen
47cf43b44e Added storage provisioner 2025-08-11 12:07:36 +02:00
Morten Olsen
aa6d14738a simplify 2025-08-07 23:26:33 +02:00
Morten Olsen
9cdbaf7929 stuff 2025-08-07 22:21:33 +02:00
Morten Olsen
cfb90f7c9f more 2025-08-06 21:18:02 +02:00
Morten Olsen
757b2fcfac lot more stuff 2025-08-04 23:44:14 +02:00
Morten Olsen
daf0ea21bb update 2025-08-01 14:47:53 +02:00
Morten Olsen
26b58a59c0 lot of updates 2025-08-01 14:40:16 +02:00
Morten Olsen
a25e0b9ffb updates 2025-08-01 07:52:09 +02:00
Morten Olsen
5782d59f71 add dotenv 2025-07-31 13:23:01 +02:00
Morten Olsen
34bba171ef Migrate to zod 2025-07-31 10:42:09 +02:00
Morten Olsen
85d043aec3 more 2025-07-31 08:51:50 +02:00
Morten Olsen
523637d40f add authentik 2025-07-30 13:42:25 +02:00
Morten Olsen
dd1e5a8124 add deployments 2025-07-28 23:23:34 +02:00
332 changed files with 133171 additions and 2175 deletions

5
.dockerignore Normal file
View File

@@ -0,0 +1,5 @@
/node_modules/
/.github/
/.vscode/
/chart/
/.env

48
.github/release-drafter-config.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name-template: "$RESOLVED_VERSION 🌈"
tag-template: "v$RESOLVED_VERSION"
categories:
- title: "🚀 Features"
labels:
- "feature"
- "enhancement"
- title: "🐛 Bug Fixes"
labels:
- "fix"
- "bugfix"
- "bug"
- title: "🧰 Maintenance"
label: "chore"
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- "major"
minor:
labels:
- "minor"
patch:
labels:
- "patch"
default: patch
autolabeler:
- label: "chore"
files:
- "*.md"
branch:
- '/docs{0,1}\/.+/'
- label: "bug"
branch:
- '/fix\/.+/'
title:
- "/fix/i"
- label: "enhancement"
branch:
- '/feature\/.+/'
- '/feat\/.+/'
title:
- "/feat:.+/"
template: |
## Changes
$CHANGES

21
.github/workflows/auto-labeler.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Auto Labeler
on:
pull_request:
types: [opened, reopened, synchronize]
permissions:
contents: read
jobs:
auto-labeler:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6
with:
config-name: release-drafter-config.yml
disable-releaser: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

93
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: Build, tag and release
on:
push:
branches:
- main
pull_request:
types:
- opened
- synchronize
env:
environment: test
release_channel: latest
DO_NOT_TRACK: "1"
NODE_VERSION: "23.x"
DOCKER_REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
PNPM_VERSION: 10.6.0
permissions:
contents: read
packages: read
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE_VERSION }}"
registry-url: "${{ env.NODE_REGISTRY }}"
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Run tests
run: pnpm test
update-release-draft:
name: Update release drafter
if: github.ref == 'refs/heads/main'
permissions:
contents: write
pull-requests: write
needs: build
environment: release
runs-on: ubuntu-latest
steps:
- id: create-release
uses: release-drafter/release-drafter@v6
with:
config-name: release-drafter-config.yml
publish: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create-release.outputs.upload_url }}
asset_path: ./operator.yaml
asset_name: operator.yaml
asset_content_type: application/yaml

65
.github/workflows/publish-tag.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: Publish tag
on:
push:
branches:
- 'main'
tags:
- "v*"
env:
environment: test
release_channel: latest
DO_NOT_TRACK: "1"
NODE_VERSION: "23.x"
DOCKER_REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
PNPM_VERSION: 10.6.0
permissions:
contents: read
packages: read
jobs:
release:
permissions:
contents: read
packages: write
attestations: write
id-token: write
pages: write
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
id: push
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

4
.gitignore vendored
View File

@@ -32,3 +32,7 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Finder (MacOS) folder config
.DS_Store
/data/
/cloudflare.yaml

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.13

6
Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
FROM node:23-slim
RUN corepack enable
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
COPY . .
CMD ["node", "src/index.ts"]

14
Makefile Normal file
View File

@@ -0,0 +1,14 @@
.PHONY: dev-recreate dev-destroy server-install
dev-destroy:
colima delete -f
dev-recreate: dev-destroy
colima start --network-address --kubernetes -m 8 --k3s-arg="--disable helm-controller,local-storage,traefik --docker" # --mount ${PWD}/data:/data:w
flux install --components="source-controller,helm-controller"
setup-flux:
flux install --components="source-controller,helm-controller"
server-install:
curl -sfL https://get.k3s.io | sh -s - --disable traefik,local-storage,helm-controller

View File

@@ -1,15 +0,0 @@
# homelab-operator
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```
This project was created using `bun init` in bun v1.2.16. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: audiobookshelf

View File

@@ -0,0 +1,13 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /audiobookshelf/auth/openid/callback
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict
- path: /audiobookshelf/auth/openid/mobile-redirect
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,52 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /config
name: config
- mountPath: /metadata
name: metadata
- mountPath: /audiobooks
name: audiobooks
- mountPath: /podcasts
name: podcasts
volumes:
- name: config
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-config'
- name: metadata
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-metadata'
- name: audiobooks
persistentVolumeClaim:
claimName: books
- name: podcasts
persistentVolumeClaim:
claimName: podcasts

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,24 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-config'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-metadata'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,7 @@
globals:
environment: prod
image:
repository: ghcr.io/advplyr/audiobookshelf
tag: 2.26.1
pullPolicy: IfNotPresent
subdomain: audiobookshelf

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: ByteStash

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}-headless'
labels:
app: '{{ .Release.Name }}'
spec:
clusterIP: None
ports:
- port: 5000
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: HttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}'
port:
number: 80

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /api/auth/oidc/callback
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,55 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
serviceName: '{{ .Release.Name }}-headless'
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: ghcr.io/jordan-dalby/bytestash:latest
ports:
- containerPort: 5000
name: http
env:
- name: ALLOW_NEW_ACCOUNTS
value: 'true'
- name: DISABLE_INTERNAL_ACCOUNTS
value: 'true'
- name: OIDC_ENABLED
value: 'true'
- name: OIDC_DISPLAY_NAME
value: OIDC
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientId
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientSecret
- name: OIDC_ISSUER_URL
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: configuration
volumeMounts:
- mountPath: /data/snippets
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 5000
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,3 @@
globals:
environment: prod
subdomain: bytestash

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: gitea

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /user/oauth2/Authentik/callback
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,6 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: PostgresDatabase
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,96 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 3000
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /data
name: data
env:
- name: TZ
value: '{{ .Values.globals.timezone }}'
- name: USER_UID
value: '1000'
- name: USER_GID
value: '1000'
- name: GITEA__service__REQUIRE_EXTERNAL_REGISTRATION_PASSWORD
value: 'true'
- name: GITEA__service__ENABLE_BASIC_AUTHENTICATION
value: 'true'
- name: GITEA__service__ENABLE_PASSWORD_SIGNIN_FORM
value: 'false'
- name: GITEA__service__DEFAULT_KEEP_EMAIL_PRIVATE
value: 'true'
- name: GITEA__service__DEFAULT_USER_IS_RESTRICTED
value: 'true'
- name: GITEA__service__DEFAULT_USER_VISIBILITY
value: 'private'
- name: GITEA__service__DEFAULT_ORG_VISIBILITY
value: 'private'
- name: GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION
value: 'true'
- name: GITEA__other__SHOW_FOOTER_POWERED_BY
value: 'false'
- name: GITEA__other__SHOW_FOOTER_TEMPLATE_LOAD_TIME
value: 'false'
- name: GITEA__other__SHOW_FOOTER_VERSION
value: 'false'
- name: GITEA__repository__ENABLE_PUSH_CREATE_USER
value: 'true'
- name: GITEA__repository__ENABLE_PUSH_CREATE_ORG
value: 'true'
- name: GITEA__openid__ENABLE_OPENID_SIGNIN
value: 'false'
- name: GITEA__openid__ENABLE_OPENID_SIGNUP
value: 'false'
- name: GITEA__database__DB_TYPE
value: postgres
- name: GITEA__database__NAME
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: database
- name: GITEA__database__HOST
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: host
- name: GITEA__database__USER
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: user
- name: GITEA__database__PASSWD
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: password
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 3000
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,8 @@
globals:
environment: prod
timezone: Europe/Amsterdam
image:
repository: docker.gitea.com/gitea
tag: latest
pullPolicy: IfNotPresent
subdomain: gitea

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: headscale

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /oidc/callback
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,70 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: '{{ .Release.Name }}-config-template'
data:
config.yaml.template: |
server_url: ${PUBLIC_URL}
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
grpc_listen_addr: 0.0.0.0:50443
private_key_path: /var/lib/headscale/private_key # Path inside the container
noise:
private_key_path: /var/lib/headscale/noise_private_key # Path inside the container
listen_routes: false
base_domain: "${PUBLIC_URL}" # For client routes and DNS push.
derp:
server:
enabled: false
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
automatically_add_embedded_derp_region: true
urls:
- https://controlplane.tailscale.com/derpmap/default
auto_update_enabled: true
update_frequency: 24h
oidc:
enabled: true
only_start_if_oidc_is_available: true
issuer: "${OIDC_ISSUER_URL}"
client_id: "${OIDC_CLIENT_ID}"
client_secret: "${OIDC_CLIENT_SECRET}"
scopes: ["openid", "profile", "email"]
redirect_url: "${PUBLIC_URL}/oidc/callback"
pkce:
enabled: true
method: S256
# DNS configuration
dns:
magic_dns: false
override_local_dns: true # Push Headscale's DNS settings to clients
ttl: 60
nameservers:
global:
- 1.1.1.1 # Cloudflare DNS
#- 10.43.0.10 # Replace with your ClusterIP for kube-dns/CoreDNS
# Domains to search for (e.g., for Kubernetes services)
search_domains:
- svc.cluster.local
- cluster.local
auto_create_users: true
oidc_user_property: preferred_username # Or 'email' or 'sub'
prefixes:
v4: 10.20.20.0/24 # Example: A /24 subnet for your VPN clients
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite

View File

@@ -0,0 +1,97 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
# To expose WireGuard UDP directly, we need a NodePort service.
# The Pod needs to be aware of the external port it's being exposed on.
# The easiest way to get WireGuard to listen on the correct port and make it
# externally accessible is to use `hostNetwork: true` for the UDP component,
# or by directly specifying the listen port in Headscale config if the NodePort is stable.
# OPTION 1: Best for simple homelab on bare metal where host network traffic isn't an issue
# hostNetwork: true # This makes the pod listen directly on the node's IPs
# dnsPolicy: ClusterFirstWithHostNet # Required if using hostNetwork
initContainers:
- name: generate-config
image: alpine/git # A small image with 'envsubst' available or easily installable
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args:
- |
# Install envsubst if it's not present (alpine/git may not have it by default)
apk update && apk add bash gettext
# Substitute environment variables into the template
# The vars are passed via `env` section below
envsubst < /config-template/config.yaml.template > /etc/headscale/config.yaml
mkdir -p /etc/headscale
# Optional: Verify the generated config
echo "--- Generated Headscale Configuration ---"
cat /etc/headscale/config.yaml
echo "---------------------------------------"
env:
# These are the variables that `envsubst` will look for and replace
- name: PUBLIC_URL
value: 'https://{{ .Values.subdomain }}.olsen.cloud'
- name: OIDC_ISSUER_URL
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: configurationIssuer
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientId
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientSecret
# Add any other variables used in config.yaml.template here
volumeMounts:
- name: config-template
mountPath: /config-template # Mount the ConfigMap as a volume
readOnly: true
- name: headscale-config
mountPath: /etc/headscale # Destination for the generated config
containers:
- name: '{{ .Release.Name }}'
image: headscale/headscale:latest # Use the official image
command: ['headscale', 'serve']
ports:
- name: http-api
containerPort: 8080
protocol: TCP
- name: wireguard-udp
containerPort: 41641
protocol: UDP
volumeMounts:
- name: headscale-data
mountPath: /var/lib/headscale
- name: headscale-config
mountPath: /etc/headscale
volumes:
- name: config-template
configMap:
name: '{{ .Release.Name }}-config-template'
- name: headscale-config
emptyDir: {}
- name: headscale-data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
- name: wireguard-udp # TODO: should this be a LB service?
port: 41641
targetPort: 41641
protocol: UDP
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,7 @@
globals:
environment: prod
image:
repository: headscale/headscale
tag: latest
pullPolicy: IfNotPresent
subdomain: headscale

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: Jellyfin

View File

@@ -0,0 +1 @@
https://www.authelia.com/integration/openid-connect/clients/jellyfin/

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.environment }}'
redirectUris:
- path: /sso/OID/redirect/Authentik
subdomain: '{{ .Values.globals.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-config'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.environment }}'

View File

@@ -0,0 +1,52 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 8096
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /config
name: config
- mountPath: /media/movies
name: movies
- mountPath: /media/tv-shows
name: tvshows
- mountPath: /media/music
name: music
volumes:
- name: config
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-config'
- name: movies
persistentVolumeClaim:
claimName: movies
- name: tvshows
persistentVolumeClaim:
claimName: tvshows
- name: music
persistentVolumeClaim:
claimName: music

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8096
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,7 @@
globals:
environment: prod
image:
repository: docker.io/jellyfin/jellyfin
tag: latest
pullPolicy: IfNotPresent
subdomain: jellyfin

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: metamcp

View File

@@ -0,0 +1,6 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: PostgresDatabase
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,79 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 12008
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /data
name: data
env:
- name: TZ
value: '{{ .Values.globals.timezone }}'
- name: APP_URL
value: https://metamcp.olsen.cloud # TODO: Change
- name: NEXT_PUBLIC_APP_URL
value: https://metamcp.olsen.cloud # TODO: Change
- name: BETTER_AUTH_SECRET
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-secrets'
key: betterauth
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: url
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: database
- name: POSTGRES_HOST
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: host
- name: POSTGRES_PORT
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: port
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: user
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: password
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,9 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: GenerateSecret
metadata:
name: '{{ .Release.Name }}-secrets'
spec:
fields:
- name: betterauth
encoding: base64
length: 64

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 12008
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,8 @@
globals:
environment: prod
timezone: Europe/Amsterdam
image:
repository: ghcr.io/metatool-ai/metamcp
tag: latest
pullPolicy: IfNotPresent
subdomain: metamcp

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: ByteStash

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /api/auth/oidc/callback
subdomain: bytestash
matchingMode: strict

View File

@@ -0,0 +1,55 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
serviceName: '{{ .Release.Name }}-headless'
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: ghcr.io/miniflux/miniflux:latest
ports:
- containerPort: 8080
name: http
env:
- name: ALLOW_NEW_ACCOUNTS
value: 'true'
- name: DISABLE_INTERNAL_ACCOUNTS
value: 'true'
- name: OIDC_ENABLED
value: 'true'
- name: OIDC_DISPLAY_NAME
value: OIDC
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientId
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientSecret
- name: OIDC_ISSUER_URL
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: configuration
volumeMounts:
- mountPath: /data/snippets
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,3 @@
globals:
environment: prod
subdomain: miniflux

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: Jellyfin

View File

@@ -0,0 +1,6 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: PostgresDatabase
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,73 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 5678
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /home/node/.n8n
name: data
env:
- name: TZ
value: '{{ .Values.globals.timezone }}'
- name: GENERIC_TIMEZONE
value: '{{ .Values.globals.timezone }}'
- name: N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS
value: 'true'
- name: N8N_RUNNERS_ENABLED
value: 'true'
- name: DB_TYPE
value: postgresdb
- name: DB_POSTGRESDB_DATABASE
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: database
- name: DB_POSTGRESDB_HOST
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: host
- name: DB_POSTGRESDB_PORT
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: port
- name: DB_POSTGRESDB_USER
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: user
- name: DB_POSTGRESDB_PASSWORD
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-pg-connection'
key: password
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 5678
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,8 @@
globals:
environment: prod
timezone: Europe/Amsterdam
image:
repository: docker.n8n.io/n8nio/n8n
tag: latest
pullPolicy: IfNotPresent
subdomain: n8n

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: ollama

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /oauth/oidc/callback
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 11434
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /root/.ollama
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 11434
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,7 @@
globals:
environment: prod
image:
repository: ollama/ollama
tag: 0.11.8
pullPolicy: IfNotPresent
subdomain: openwebui

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: openwebui

View File

@@ -0,0 +1,10 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
redirectUris:
- path: /oauth/oidc/callback
subdomain: '{{ .Values.subdomain }}'
matchingMode: strict

View File

@@ -0,0 +1,70 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}'
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: '{{ .Release.Name }}'
template:
metadata:
labels:
app: '{{ .Release.Name }}'
spec:
containers:
- name: '{{ .Release.Name }}'
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
imagePullPolicy: '{{ .Values.image.pullPolicy }}'
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- mountPath: /app/backend/data
name: data
env:
- name: ENABLE_SIGNUP
value: 'false'
- name: WEBUI_URL # TODO: remove
value: https://openwebui.olsen.cloud
- name: ENABLE_OAUTH_PERSISTENT_CONFIG
value: 'false'
- name: ENABLE_OAUTH_SIGNUP
value: 'true'
- name: OAUTH_MERGE_ACCOUNTS_BY_EMAIL
value: 'true'
- name: OAUTH_PROVIDER_NAME
value: authentik
- name: OPENID_PROVIDER_URL
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: configuration
- name: OAUTH_CLIENT_ID
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientId
- name: OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: '{{ .Release.Name }}-client'
key: clientSecret
- name: ENABLE_LOGIN_FORM
value: 'false'
- name: OPENID_REDIRECT
value: https://openwebui.olsen.cloud/oauth/oidc/callback
volumes:
- name: data
persistentVolumeClaim:
claimName: '{{ .Release.Name }}-data'

View File

@@ -0,0 +1,11 @@
apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
name: '{{ .Release.Name }}'
spec:
environment: '{{ .Values.globals.environment }}'
subdomain: '{{ .Values.subdomain }}'
destination:
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
port:
number: 80

View File

@@ -0,0 +1,11 @@
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: '{{ .Release.Name }}-data'
spec:
accessModes:
- 'ReadWriteOnce'
resources:
requests:
storage: '1Gi'
storageClassName: '{{ .Values.globals.environment }}'

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}'
labels:
app: '{{ .Release.Name }}'
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: '{{ .Release.Name }}'

View File

@@ -0,0 +1,7 @@
globals:
environment: prod
image:
repository: ghcr.io/open-webui/open-webui
tag: main
pullPolicy: IfNotPresent
subdomain: openwebui

View File

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: Resources

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: books
labels:
type: nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual-books
nfs:
path: '{{ .Values.books.path }}'
server: '{{ .Values.host }}'
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: books
spec:
storageClassName: manual-books
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: movies
labels:
type: nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual-movies
nfs:
path: '{{ .Values.movies.path }}'
server: '{{ .Values.host }}'
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: movies
spec:
storageClassName: manual-movies
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: music
labels:
type: nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual-music
nfs:
path: '{{ .Values.music.path }}'
server: '{{ .Values.host }}'
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: music
spec:
storageClassName: manual-music
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: podcasts
labels:
type: nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual-podcasts
nfs:
path: '{{ .Values.podcasts.path }}'
server: '{{ .Values.host }}'
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: podcasts
spec:
storageClassName: manual-podcasts
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: tvshows
labels:
type: nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual-tvshows
nfs:
path: '{{ .Values.tvshows.path }}'
server: '{{ .Values.host }}'
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: tvshows
spec:
storageClassName: manual-tvshows
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi

View File

@@ -0,0 +1,11 @@
host: 192.168.20.106
movies:
path: /mnt/HDD/Movies
tvshows:
path: /mnt/HDD/TV-Shows
music:
path: /mnt/HDD/Music2
books:
path: /mnt/HDD/Books
podcasts:
path: /mnt/HDD/Podcasts

View File

@@ -0,0 +1,3 @@
apiVersion: v2
version: 1.0.0
name: root

View File

@@ -0,0 +1,33 @@
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: homelab-apps
namespace: '{{ .Values.env }}-argo'
spec:
generators:
- git:
repoURL: '{{ .Values.repo }}'
revision: '{{ .Values.ref }}'
directories:
- path: charts/apps/*
include: '.*'
exclude: '.*.disabled'
template:
metadata:
name: '{{`{{path.basename}}`}}'
spec:
project: default
source:
repoURL: '{{ .Values.repo }}'
targetRevision: '{{ .Values.ref }}'
path: charts/apps/{{`{{path.basename}}`}}
helm:
values: |
globals: {{ .Values.globals | toYaml | nindent 14 }}
destination:
server: https://kubernetes.default.svc
namespace: '{{ .Values.globals.env }}'
syncPolicy:
automated:
prune: true
selfHeal: true

View File

@@ -0,0 +1,21 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: homelab-root
namespace: '{{ .Values.globals.env }}-argo'
spec:
project: default
source:
repoURL: '{{ .Values.repo }}'
targetRevision: '{{ .Values.ref }}'
path: charts/root
helm:
valueFiles:
- values.yaml
destination:
server: https://kubernetes.default.svc
namespace: '{{ .Values.globals.env }}-argo'
syncPolicy:
automated:
prune: true
selfHeal: true

View File

@@ -0,0 +1,4 @@
globals:
env: prod
repo: https://github.com/morten-olsen/homelab-operator.git
ref: HEAD

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: homelab-operator
description: A Helm chart for deploying the homelab-operator
type: application
version: 0.1.0
appVersion: "1.0.0" # This is the version of the app being deployed

View File

@@ -0,0 +1,55 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "homelab-operator.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "homelab-operator.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart-level labels to be applied to every resource that comes from this chart.
*/}}
{{- define "homelab-operator.labels" -}}
helm.sh/chart: {{ include "homelab-operator.name" . }}
{{ include "homelab-operator.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "homelab-operator.selectorLabels" -}}
app.kubernetes.io/name: {{ include "homelab-operator.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "homelab-operator.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "homelab-operator.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,12 @@
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: {{ include "homelab-operator.fullname" . }}-local-path
labels:
{{- include "homelab-operator.labels" . | nindent 4 }}
provisioner: reuse-local-path-provisioner
parameters:
# Add any provisioner-specific parameters here
reclaimPolicy: {{ .Values.storage.reclaimPolicy | default "Retain" }}
allowVolumeExpansion: {{ .Values.storage.allowVolumeExpansion | default false }}
volumeBindingMode: {{ .Values.storage.volumeBindingMode | default "WaitForFirstConsumer" }}

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