Compare commits

...

2 Commits

Author SHA1 Message Date
b9eeadee05 add README 2026-02-07 12:20:35 -05:00
ac9b7d3f67 rm inactive projects 2026-02-07 12:20:28 -05:00
22 changed files with 29 additions and 650 deletions

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# Core Apps
**Production-grade application deployments for my Kubernetes homelab**
This repository contains the core applications deployed to my Kubernetes homelab. Applications are deployed using either Kubernetes manifests or Helm charts (with upstream subcharts and custom values).
**Why Helm?** I prefer Helm charts when upstream versions exist because Renovate can automatically track new chart versions, whereas image tags in raw manifests aren't always semantically versioned.
**GitOps Workflow:** This repository is monitored by ArgoCD and serves as the source of truth for deployments. Each top-level directory is its own ArgoCD Application, with subdirectories representing components within that application.
**Automated Commits:** Apps that I wrote/maintain directly (such as yt-dlp-bot and zap2xml) get their manifests automatically updated via an Actions workflow in their respective repositories
- `arr-stack/` - Arr Stack (manifests)
- `flaresolverr/` - Flaresolverr (captcha processor)
- `prowlarr/` - Prowlarr (indexer manager)
- `radarr/` - Radarr (movie media manager)
- `sonarr/` - Sonarr (TV series media manager)
- `tunnel/` - Custom SSH tunnel to my seedbox to securely communicate with Deluge
- `attic/` - Attic NixOS cache server (manifests)
- `authentik/` - [Authentik](https://auth.dubyatp.xyz) SSO server (Helm chart)
- `gitea/` - [Gitea](https://git.dubyatp.xyz) Git Server (Helm chart)
- `gitea-runner/` - Gitea Runner (manifests)
- `buildkitd/` - Docker Buildkitd build environment
- `grafana/` - [Grafana](https://grafana.dubyatp.xyz) observability dashboard (Helm chart)
- `jellyfin/` - [Jellyfin](https://jellyfin.dubyatp.xyz) media server (Helm chart)
- `renovate/` - [Renovate](https://git.dubyatp.xyz/renovate-bot) automated dependency manager (manifests)
- `vaultwarden/` - [Vaultwarden](https://vaultwarden.dubyatp.xyz) password manager (manifests)
- `whatismyip/` - [Simple "what is my IP" HTTP service](https://whatismyip.dubyatp.xyz) (manifests)
- `yt-dlp-bot/` - [yt-dlp bot](https://git.dubyatp.xyz/williamp/yt-dlp-bot) (manifests); a custom Discord bot i created for downloading and storing YouTube videos ad-hoc
- `zap2xml/` - [kube-zap2xml](https://git.dubyatp.xyz/williamp/kube-zap2xml) (manifests); modified version of zap2xml (zap2it TV listings scraper) designed for use as Kubernetes jobs and sends the result XMLTV format to a Rook-Ceph S3 bucket

View File

@@ -1,61 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: dispatcharr
spec:
selector:
matchLabels:
app: dispatcharr
template:
metadata:
labels:
app: dispatcharr
annotations:
backup.velero.io/backup-volumes: data
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: extensions.talos.dev/i915
operator: Exists
nodeSelector:
kubernetes.io/hostname: weyma-talos-testw04
containers:
- name: dispatcharr
image: ghcr.io/dispatcharr/dispatcharr:0.8.0-amd64
env:
- name: DISPATCHARR_ENV
value: aio
- name: REDIS_HOST
value: localhost
- name: CELERY_BROKER_URL
value: redis://localhost:6379/0
- name: DISPATCHARR_LOG_LEVEL
value: info
- name: UWSGI_NICE_LEVEL
value: "-5"
- name: CELERY_NICE_LEVEL
value: "-5"
volumeMounts:
- name: dispatcharr-data
mountPath: /data
- name: dev-dri
mountPath: /dev/dri
resources:
limits:
memory: "3Gi"
cpu: "1"
requests:
memory: "256Mi"
cpu: "500m"
securityContext:
privileged: true
volumes:
- name: dispatcharr-data
persistentVolumeClaim:
claimName: dispatcharr
- name: dev-dri
hostPath:
path: /dev/dri

View File

@@ -1,18 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dispatcharr
labels:
app.kubernetes.io/name: dispatcharr
spec:
rules:
- host: dispatcharr.dubyatp.xyz
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: dispatcharr-svc
port:
number: 9191

View File

@@ -1,11 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dispatcharr
spec:
resources:
requests:
storage: 20Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany

View File

@@ -1,10 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: dispatcharr-svc
spec:
selector:
app: dispatcharr
ports:
- port: 9191
targetPort: 9191

View File

@@ -1,9 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: immich-config
data:
immich-config.yaml: |
trash:
enabled: true
days: 30

View File

@@ -1,22 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: immich
labels:
name: immich
spec:
rules:
- host: immich.dubyatp.xyz
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: immich
port:
number: 2283
tls:
- secretName: cert-dubyatp-xyz
hosts:
- immich.dubyatp.xyz

View File

@@ -1,11 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: immich-library
spec:
resources:
requests:
storage: 50Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany

View File

@@ -1,94 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: immich-ml
spec:
selector:
matchLabels:
app: immich-ml
template:
metadata:
labels:
app: immich-ml
spec:
containers:
- name: immich-ml
image: ghcr.io/immich-app/immich-machine-learning:v1.134.0
volumeMounts:
- name: model-cache
mountPath: /cache
- name: config
mountPath: /config/immich-config.yaml
- name: dev-dri
mountPath: /dev/dri
env:
- name: DB_HOSTNAME
value: "immich-rw.cloudnativepg.svc.cluster.local"
- name: DB_DATABASE_NAME
value: "immich"
- name: DB_USERNAME
valueFrom:
secretKeyRef:
key: username
name: postgres-credentials
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: postgres-credentials
- name: REDIS_HOSTNAME
value: redis
- name: REDIS_PORT
value: "6379"
- name: IMMICH_PORT
value: "3003"
livenessProbe:
httpGet:
path: /ping
port: 3003
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /ping
port: 3003
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
startupProbe:
httpGet:
path: /ping
port: 3003
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 30
securityContext:
privileged: true
resources:
limits:
memory: "8Gi"
cpu: "2"
requests:
memory: "2Gi"
cpu: "500m"
volumes:
- name: model-cache
emptyDir:
sizeLimit: 10Gi
- name: config
configMap:
name: immich-config
- name: dev-dri
hostPath:
path: /dev/dri
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: extensions.talos.dev/i915
operator: Exists

View File

@@ -1,25 +0,0 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: postgres-credentials
spec:
data:
- remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: cloudnativepg
metadataPolicy: None
property: immich_pw
secretKey: password
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: weyma-vault
target:
template:
data:
username: immich
password: "{{ .password }}"
creationPolicy: Owner
deletionPolicy: Retain
name: postgres-credentials

View File

@@ -1,94 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: immich-server
spec:
selector:
matchLabels:
app: immich-server
template:
metadata:
labels:
app: immich-server
spec:
containers:
- name: immich-server
image: ghcr.io/immich-app/immich-server:v1.134.0
volumeMounts:
- name: library
mountPath: /usr/src/app/upload
- name: config
mountPath: /config/immich-config.yaml
- name: dev-dri
mountPath: /dev/dri
env:
- name: DB_HOSTNAME
value: "immich-rw.cloudnativepg.svc.cluster.local"
- name: DB_DATABASE_NAME
value: "immich"
- name: DB_USERNAME
valueFrom:
secretKeyRef:
key: username
name: postgres-credentials
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: postgres-credentials
- name: REDIS_HOSTNAME
value: redis
- name: REDIS_PORT
value: "6379"
- name: IMMICH_PORT
value: "2283"
livenessProbe:
httpGet:
path: /api/server/ping
port: 2283
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/server/ping
port: 2283
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
startupProbe:
httpGet:
path: /api/server/ping
port: 2283
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 30
securityContext:
privileged: true
resources:
limits:
memory: "8Gi"
cpu: "2"
requests:
memory: "2Gi"
cpu: "500m"
volumes:
- name: library
persistentVolumeClaim:
claimName: immich-library
- name: config
configMap:
name: immich-config
- name: dev-dri
hostPath:
path: /dev/dri
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: extensions.talos.dev/i915
operator: Exists

View File

@@ -1,23 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: immich
spec:
selector:
app: immich-server
ports:
- port: 2283
targetPort: 2283
name: http
---
apiVersion: v1
kind: Service
metadata:
name: immich-ml
spec:
selector:
app: immich-ml
ports:
- port: 3003
targetPort: 3003
name: http

View File

@@ -1,38 +0,0 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
selector:
matchLabels:
app: redis
serviceName: redis
replicas: 1
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:latest
command: ["redis-server"]
args:
- "--port"
- "6379"
- "--dir"
- "/data"
- "--appendonly"
- "yes"
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: rook-ceph-block
resources:
requests:
storage: 10Gi
metadata:
name: data

View File

@@ -1,10 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379

View File

@@ -1,10 +0,0 @@
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: peertube-bucket
namespace: peertube
spec:
generateBucketName: peertube
storageClassName: weyma-s3-bucket
additionalConfig:
maxSize: "100Gi"

View File

@@ -1,35 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: peertube-config
data:
PEERTUBE_INSTANCE_NAME: "dubyatp peertube"
PEERTUBE_INSTANCE_DESCRIPTION: "duby's peertube instance"
POSTGRES_USER: peertube
POSTGRES_DB: peertube
PEERTUBE_DB_USERNAME: peertube
PEERTUBE_DB_HOSTNAME: pooler-weyma-rw.cloudnativepg.svc.cluster.local
PEERTUBE_DB_PORT: "5432"
PEERTUBE_WEBSERVER_HOSTNAME: "tube.dubyatp.xyz"
PEERTUBE_TRUST_PROXY: '["127.0.0.1", "loopback", "172.18.0.0/16"]'
PEERTUBE_SMTP_USERNAME: "peertube_dubyatp"
PEERTUBE_SMTP_HOSTNAME: "mail.smtp2go.com"
PEERTUBE_SMTP_PORT: "465"
PEERTUBE_SMTP_TLS: "true"
PEERTUBE_SMTP_FROM: "peertube@em924671.dubyatp.xyz"
PEERTUBE_ADMIN_EMAIL: "me@williamtpeebles.com"
#PEERTUBE_OBJECT_STORAGE_ENABLED: "true"
#PEERTUBE_OBJECT_STORAGE_ENDPOINT: "https://weyma-s3.infra.dubyatp.xyz"
#PEERTUBE_OBJECT_STORAGE_REGION: ""
#PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BUCKET_NAME: "peertube-953221d2-7649-48b2-b79f-5a9e59daedbb"
#PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_PREFIX: "streaming/"
#PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BUCKET_NAME: "peertube-953221d2-7649-48b2-b79f-5a9e59daedbb"
#PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_PREFIX: "videos/"
#PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BUCKET_NAME: "peertube-953221d2-7649-48b2-b79f-5a9e59daedbb"
#PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_PREFIX: "exports/"
#PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BUCKET_NAME: "peertube-953221d2-7649-48b2-b79f-5a9e59daedbb"
#PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_PREFIX: "original-videos/"
#PEERTUBE_OBJECT_STORAGE_CAPTIONS_BUCKET_NAME: "peertube-953221d2-7649-48b2-b79f-5a9e59daedbb"
#PEERTUBE_OBJECT_STORAGE_CAPTIONS_PREFIX: "captions/"
#PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC: "public-read"
#PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE: "private"

View File

@@ -1,69 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: peertube
labels:
app: peertube
spec:
replicas: 1
selector:
matchLabels:
app: peertube
template:
metadata:
labels:
app: peertube
spec:
containers:
- name: peertube
image: chocobozzz/peertube:v7.2.3-bookworm
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: http
- containerPort: 443
name: https
- containerPort: 9000
name: peertube
- containerPort: 1935
name: rtmp
envFrom:
- secretRef:
name: peertube-secret
- secretRef:
name: peertube-bucket
- configMapRef:
name: peertube-config
env:
- name: PEERTUBE_REDIS_HOSTNAME
value: "localhost"
- name: PEERTUBE_REDIS_AUTH
value: ""
volumeMounts:
- name: peertube-data
mountPath: /data
resources:
requests:
cpu: "0.5"
memory: 1Gi
limits:
cpu: "1"
memory: 2Gi
- name: redis
image: redis:8.2.1-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
name: redis
resources:
requests:
cpu: "0.2"
memory: 256Mi
limits:
cpu: "0.5"
memory: 1Gi
volumes:
- name: peertube-data
persistentVolumeClaim:
claimName: peertube-data

View File

@@ -1,18 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: peertube
labels:
app.kubernetes.io/name: peertube
spec:
rules:
- host: tube.dubyatp.xyz
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: peertube
port:
number: 9000

View File

@@ -1,10 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: peertube-data
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 50Gi

View File

@@ -1,42 +0,0 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: peertube-secret
spec:
data:
- remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: peertube
metadataPolicy: None
property: PEERTUBE_SECRET
secretKey: PEERTUBE_SECRET
- remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: peertube
metadataPolicy: None
property: PEERTUBE_DB_PASSWORD
secretKey: PEERTUBE_DB_PASSWORD
- remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: peertube
metadataPolicy: None
property: PEERTUBE_SMTP_PASSWORD
secretKey: PEERTUBE_SMTP_PASSWORD
- remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: peertube
metadataPolicy: None
property: POSTGRES_PASSWORD
secretKey: POSTGRES_PASSWORD
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: weyma-vault
target:
creationPolicy: Owner
deletionPolicy: Retain
name: peertube-secret

View File

@@ -1,24 +0,0 @@
kind: Service
apiVersion: v1
metadata:
name: peertube
spec:
selector:
app: peertube
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http
- protocol: TCP
port: 25
targetPort: 25
name: smtp
- protocol: TCP
port: 9000
targetPort: 9000
name: peertube
- protocol: TCP
name: rtmp
port: 1935
targetPort: 1935

View File

@@ -1,16 +0,0 @@
apiVersion: hyperspike.io/v1
kind: Valkey
metadata:
name: peertube-kv
labels:
app.kubernetes.io/instance: peertube
spec:
anonymousAuth: true
certIssuerType: ClusterIssuer
clusterDomain: cluster.local
clusterPreferredEndpointType: ip
nodes: 1
prometheus: false
replicas: 3
tls: false
volumePermissions: true