feat(k8s/homepage): add homepage stack

This commit is contained in:
2026-03-12 12:08:06 +02:00
parent 03bb6ab9b4
commit 5d0e50b39b
9 changed files with 358 additions and 30 deletions

View File

@@ -0,0 +1,111 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: homepage-config
namespace: homepage
data:
settings.yaml: |
---
title: Home
theme: light
color: slate
headerStyle: clean
layout:
# Define your layout sections here
services.yaml: |
---
- Media:
- Jellyfin:
icon: jellyfin.png
href: https://${JELLYFIN_HOST}
description: Media system
widget:
type: jellyfin
url: https://${JELLYFIN_HOST}
key: "{{HOMEPAGE_VAR_JELLYFIN_API_KEY}}"
enableBlocks: true
enableUser: true
enableMediaControl: false
showEpisodeNumber: true
expandOneStreamToTwoRows: false
- Sonarr:
icon: sonarr.png
href: https://${SONARR_HOST}
description: Series management
widget:
type: sonarr
url: https://${SONARR_HOST}
username: "{{HOMEPAGE_VAR_SONARR_USERNAME}}"
password: "{{HOMEPAGE_VAR_SONARR_PASSWORD}}"
key: "{{HOMEPAGE_VAR_SONARR_API_KEY}}"
- Radarr:
icon: radarr.png
href: https://${RADARR_HOST}
description: Movie management
widget:
type: radarr
url: https://${RADARR_HOST}
username: "{{HOMEPAGE_VAR_RADARR_USERNAME}}"
password: "{{HOMEPAGE_VAR_RADARR_PASSWORD}}"
key: "{{HOMEPAGE_VAR_RADARR_API_KEY}}"
- qBittorrent:
icon: qbittorrent.png
href: https://${QBITTORRENT_HOST}
description: Torrent download
- Services:
- Home Assistant:
icon: home-assistant.png
href: "{{HOMEPAGE_VAR_HOMEASSISTANT_URL}}"
ping: "{{HOMEPAGE_VAR_HOMEASSISTANT_URL}}"
description: Home Automation server
widget:
type: homeassistant
url: "{{HOMEPAGE_VAR_HOMEASSISTANT_URL}}"
key: "{{HOMEPAGE_VAR_HOMEASSISTANT_API_KEY}}"
custom:
- state: sensor.system_monitor_processor_temperature
label: CPU
- state: sensor.system_monitor_processor_use
label: CPU
- state: sensor.system_monitor_memory_usage
label: MEM
- state: sensor.system_monitor_swap_usage
label: SWAP
- Immich:
icon: immich.png
href: https://${IMMICH_HOST}
description: Photo gallery
widget:
type: immich
url: https://${IMMICH_HOST}
key: "{{HOMEPAGE_VAR_IMMICH_API_KEY}}"
version: 2
- Paperless-ngx:
icon: paperless-ngx.png
href: https://${PAPERLESS_HOST}/
description: Documents storage
- Grocy:
icon: grocy.png
href: https://${GROCY_HOST}/
description: Chores and inventory tracker
widgets.yaml: |
---
- resources:
cpu: true
memory: true
disk: /
- datetime:
text_size: xl
format:
timeStyle: short
bookmarks.yaml: |
---
# Define your bookmarks here
kubernetes.yaml: |
---
mode: cluster

View File

@@ -0,0 +1,75 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: homepage
namespace: homepage
labels:
app: homepage
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: homepage
template:
metadata:
labels:
app: homepage
spec:
serviceAccountName: homepage
securityContext:
# runAsNonRoot omitted — homepage image starts as root; non-root requires PUID/PGID entrypoint setup
seccompProfile:
type: RuntimeDefault
containers:
- name: homepage
image: ghcr.io/gethomepage/homepage:v1.10.1
ports:
- containerPort: 3000
name: http
protocol: TCP
envFrom:
- secretRef:
name: homepage-credentials
env:
- name: HOMEPAGE_ALLOWED_HOSTS
value: "${HOMEPAGE_HOST}"
volumeMounts:
- name: config
mountPath: /app/config/settings.yaml
subPath: settings.yaml
- name: config
mountPath: /app/config/services.yaml
subPath: services.yaml
- name: config
mountPath: /app/config/widgets.yaml
subPath: widgets.yaml
- name: config
mountPath: /app/config/bookmarks.yaml
subPath: bookmarks.yaml
- name: config
mountPath: /app/config/kubernetes.yaml
subPath: kubernetes.yaml
livenessProbe:
httpGet:
port: 3000
path: /
initialDelaySeconds: 30
periodSeconds: 30
readinessProbe:
httpGet:
port: 3000
path: /
initialDelaySeconds: 10
periodSeconds: 10
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
volumes:
- name: config
configMap:
name: homepage-config

View File

@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: homepage
namespace: homepage
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: authelia-chain-authelia-authelia-auth@kubernetescrd
spec:
tls:
- hosts:
- ${HOMEPAGE_HOST}
secretName: homepage-tls
rules:
- host: ${HOMEPAGE_HOST}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: homepage
port:
number: 3000

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: homepage

View File

@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: homepage
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-controller
namespace: homepage
spec:
podSelector:
matchLabels:
app: homepage
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: traefik

View File

@@ -0,0 +1,36 @@
apiVersion: v1
kind: Secret
metadata:
name: homepage-credentials
namespace: homepage
stringData:
#ENC[AES256_GCM,data:kRiN5JB/ca3N,iv:0sNhC4i6zxUf/0VMKt98ynvRqvwn87vRyNcq6RJTIHs=,tag:s/8/EvIBUaWXIg17r+Iaig==,type:comment]
HOMEPAGE_VAR_JELLYFIN_API_KEY: ENC[AES256_GCM,data:G0l+DMIr56w00qk/66v5SVl/hJnpvfwj6LU5wr73ExM=,iv:smVHXj9rGTxULO+iRcnYsu6pQCU0Uu8DJwwWXQyMzRI=,tag:zg3ggPUA9jBCbBmuIuys2g==,type:str]
#ENC[AES256_GCM,data:+2tpnodG4A==,iv:Jbi23B2V815IC2DHgdZ3JByGP8Pbq8Si4OzAdZg2tfE=,tag:FOKsVAq7MGMy5QbZdzv6Eg==,type:comment]
HOMEPAGE_VAR_SONARR_USERNAME: ENC[AES256_GCM,data:ZqKdBJH1gpQ=,iv:bPjBYxyBr25k58X5gFmQDsFcwRHnHsyJaD1toqFC9hM=,tag:CaxY829WQ4LTzQOG48Or/w==,type:str]
HOMEPAGE_VAR_SONARR_PASSWORD: ENC[AES256_GCM,data:tCFmcRaKylbP33O5VX3jRcsw5To8fPdZXpqmJ7aW5RI+EXr4SgPJCD5hcMY7g2qqGXL6ZUyAe4GPj+cyNkpj1g==,iv:LKeAbrS04u+Ip3IuTGfEc6Ee0SVLkLAcM5BCMP8JQuk=,tag:sQPSG5+SMI2buxG1WxYL3A==,type:str]
HOMEPAGE_VAR_SONARR_API_KEY: ENC[AES256_GCM,data:h1nmDPRl9APUaMBL7SMxJbudG/BAwxhNxmN8ux76ri4=,iv:ssekkf4tPCgmuUBHPugpuYxaCrXAbXjC1kkFZe/y+DI=,tag:l9f4iy1TCd/KyyTrHFeIwA==,type:str]
#ENC[AES256_GCM,data:T0sPU34UTw==,iv:x7slQXYqmrlc8LbXy3WK3435oKt8MPxg63F1Le9Q9Gc=,tag:OQJZgrSbVvQJYxIoADYiUQ==,type:comment]
HOMEPAGE_VAR_RADARR_USERNAME: ENC[AES256_GCM,data:0JO3+tYe73g=,iv:rwc8mN7BspHqQaK4jOamxxfmfCIz+LIOcXP6+ttf+EM=,tag:8xbi9cOv04ydi48/omcAxw==,type:str]
HOMEPAGE_VAR_RADARR_PASSWORD: ENC[AES256_GCM,data:GAQDMYTcmdjsDAKhc+lVMWgqiw/7QI4O4mRczrZTwBNleLIT1ypq2YL6iTSE8VNoMVhayEHWFfhlnW19jfE7Mw==,iv:mV+BFgvu0g56sexTVF39xOXZt0g2WXU6Bvfv5rMSjOo=,tag:ONo3c8WoRP1nkjKuZWbCsA==,type:str]
HOMEPAGE_VAR_RADARR_API_KEY: ENC[AES256_GCM,data:pSop8tQFKlqjubts1dYLmAuJykHgiKfN5AE3TiJ+/FA=,iv:vjkgcKV0fazgKAzwPVCnLnky8vi+C/5oX+kjYZooo1g=,tag:UxL6DV8Soac2kQtiKdjByA==,type:str]
#ENC[AES256_GCM,data:veoFUhGcVc9wxkHhEC+2,iv:NzvpUsCaUUplDgNTQYnZHnp9wp3eYDr3nSHS+KH1O+g=,tag:I4QnzFgNIsnTUqYa/pisqA==,type:comment]
HOMEPAGE_VAR_HOMEASSISTANT_URL: ENC[AES256_GCM,data:/wqyadZAZDLX9wvZBgb1JVWGR4Nx3LLZqasuun70yXLNnhDRfcriSLM+,iv:PE/vo49SP4ietb5MA3QQYNwUGGs9ssZ3HB5OKgCsEcA=,tag:EXutc1HIXAC7lHqHo8lPnw==,type:str]
HOMEPAGE_VAR_HOMEASSISTANT_API_KEY: ENC[AES256_GCM,data:UmYMhbs8uTCpnQavv07fSeEQi48sgaHKhZGUoMMOPY57EKw1TLoLl4Ew2xk7ISM2yzLIJ8ltYkjx3c6XGBhQcrmwibWLr0SqQbF8TTmzG3jNYnmYGmw+ocB+KsVn4dwH22XXBUcPPto9ca8/heCldWvinEb6hEy+/TzQH/J5+j9P91XmYlMkD6a17C4CxEorr5wQ/7mrF3o9YkXOmVl3iOTcLPLWGUBJ7GwerR5TObClwEXvp2di,iv:ueOKaNGs1Afmb51aQorO/NuNrvBF+0kn5t6sPMG5npU=,tag:HZSPBWqrJ3oIueFQieqTNw==,type:str]
#ENC[AES256_GCM,data:SdvlWse6XQ==,iv:qt3dXpJnCdtZRCvhw1WN8Ns5oeqXXJnCZGU/u+IqYs0=,tag:XRQ4NGgrQ1EQ6kgYDKBugg==,type:comment]
HOMEPAGE_VAR_IMMICH_API_KEY: ENC[AES256_GCM,data:U4Va0z9GmVwNUGtcpkopSdi7+tyT7Ar18TBq50014xuJc4btqbpf,iv:59VQPnPURUShRiWj3Ykgxkq0nwc9Ho8NPdP6ZEXbgJw=,tag:2Mb0rh+2zYuJuszOYcPysA==,type:str]
sops:
age:
- recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjcFBqR2NVM2hIdW5sVDRt
MzB0TkFBMEFQYWM0UGhtZUdWYnd5VzhVemdnCm9TK3REanBiNmhyYTZCcVFSbG42
R1d4Z29NUk1nUE5QbWNxNTMwbmliM2cKLS0tIGQ4NTNWQWpTeEovdmwwRmhBd1RR
SGRjZnczTEJuOTZCdk14VEV4M1M3alEKfPm69NHpOdZrWli/DhtAej8nSETSSsYW
SK8sg1oX9oA12J12FpI1XzFppYaKu8GrGv+r4SgbLN5FbXVoyGJ8ZQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-12T10:07:22Z"
mac: ENC[AES256_GCM,data:v742LFJuZoS1h8EuCVotY6iG1qxHxC3hiREbyqpllH0QXTf0k/tQ5FImqsM20N2Qc5aEXtS0dpfuTTgB3hZtHOaiBX4Kuzqewvc2gc1yQNrT/r3e2/iB8RNON+0erOwkqVECyPbgKsG/Y5p368rRgDQMbBwl8wjYrc3mLm2T9Yw=,iv:oI1PtCsJHRE6PjSrtG7vLtQQ+Ym5yBCGyqdLljlO0og=,tag:qPLi2beb2zsAkV5iP14Qiw==,type:str]
encrypted_regex: ^(data|stringData|email)$
version: 3.12.1

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: homepage
namespace: homepage
spec:
selector:
app: homepage
ports:
- port: 3000
targetPort: 3000
name: http

View File

@@ -0,0 +1,39 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: homepage
namespace: homepage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: homepage
rules:
- apiGroups: [""]
resources: ["namespaces", "pods", "nodes"]
verbs: ["get", "list"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list"]
- apiGroups: ["traefik.io"]
resources: ["ingressroutes"]
verbs: ["get", "list"]
- apiGroups: ["metrics.k8s.io"]
resources: ["nodes", "pods"]
verbs: ["get", "list"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: homepage
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: homepage
subjects:
- kind: ServiceAccount
name: homepage
namespace: homepage