├── .devcontainer.json ├── .github └── workflows │ └── flux-updates.yaml ├── .gitignore ├── .markdownlint.yaml ├── README.md ├── apps ├── base │ ├── commafeed │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── networking.yaml │ │ └── secrets.yaml │ ├── health-api │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── networking.yaml │ │ └── secrets.yaml │ ├── homepage │ │ ├── clusterrole.yaml │ │ ├── config-secret.yaml │ │ ├── configmap-env.yaml │ │ ├── deployment.yaml │ │ ├── ingress.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── secret.yaml │ │ ├── service.yaml │ │ └── serviceaccount.yaml │ ├── linkding │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── networking.yaml │ │ ├── secrets.yaml │ │ └── storage.yaml │ ├── n8n │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── networking.yaml │ │ ├── secrets.yaml │ │ └── storage.yaml │ ├── shelly-api │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── secrets.yaml │ ├── wallabag │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── networking.yaml │ │ └── secrets.yaml │ └── zettelkasten-tracker │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── networking.yaml │ │ └── secrets.yaml └── jotunheim │ ├── commafeed │ ├── cloudflare.yaml │ ├── configmap.yaml │ ├── kustomization.yaml │ └── networkpolicy.yaml │ ├── health-api │ ├── configmap.yaml │ ├── ingress.yaml │ └── kustomization.yaml │ ├── homepage │ └── kustomization.yaml │ ├── kustomization.yaml │ ├── linkding │ ├── cloudflare.yaml │ ├── configmap.yaml │ ├── kustomization.yaml │ └── networkpolicy.yaml │ ├── n8n │ ├── cloudflare.yaml │ ├── configmap.yaml │ ├── ingress.yaml │ └── kustomization.yaml │ ├── shelly-api │ ├── configmap.yaml │ └── kustomization.yaml │ ├── wallabag │ ├── cloudflare.yaml │ ├── configmap.yaml │ ├── kustomization.yaml │ └── networkpolicy.yaml │ └── zettelkasten-tracker │ ├── configmap.yaml │ ├── ingress.yaml │ ├── kustomization.yaml │ └── podmonitor.yaml ├── clusters ├── data │ ├── databases.yaml │ ├── flux-system │ │ ├── gotk-components.yaml │ │ ├── gotk-sync.yaml │ │ └── kustomization.yaml │ ├── infrastructure.yaml │ └── monitoring.yaml └── jotunheim │ ├── apps.yaml │ ├── flux-system │ ├── gotk-components.yaml │ ├── gotk-sync.yaml │ └── kustomization.yaml │ ├── infrastructure.yaml │ └── monitoring.yaml ├── databases └── data │ ├── commafeed │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml │ ├── health-api │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml │ ├── home-iot │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml │ ├── kustomization.yaml │ ├── linkding │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml │ ├── n8n │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml │ ├── pgadmin │ ├── configmap.yaml │ ├── deployment.yaml │ ├── ingress.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── networking.yaml │ ├── secrets.yaml │ └── storage.yaml │ ├── quantified-self │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml │ └── wallabag │ ├── database.yaml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── scheduled-backup.yaml │ └── secrets.yaml ├── images └── homelab-feedback.png ├── infrastructure ├── configs │ ├── base │ │ ├── cert-manager │ │ │ ├── clusterissuer-production.yaml │ │ │ ├── clusterissuer-staging.yaml │ │ │ └── kustomization.yaml │ │ ├── external-secrets │ │ │ ├── cluster-secret-store.yaml │ │ │ └── kustomization.yaml │ │ └── kustomization.yaml │ ├── data │ │ ├── cilium │ │ │ ├── ippool.yaml │ │ │ ├── kustomization.yaml │ │ │ └── l2policy.yaml │ │ ├── external-secrets │ │ │ └── kustomization.yaml │ │ └── kustomization.yaml │ └── jotunheim │ │ ├── cilium │ │ ├── apps-l2policy.yaml │ │ ├── apps-pool.yaml │ │ ├── ingress-l2policy.yaml │ │ ├── ingress-pool.yaml │ │ └── kustomization.yaml │ │ ├── external-secrets │ │ └── kustomization.yaml │ │ └── kustomization.yaml └── controllers │ ├── base │ ├── cert-manager │ │ ├── kustomization.yaml │ │ ├── release.yaml │ │ └── repository.yaml │ ├── cloudnativepg │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── release.yaml │ │ └── repository.yaml │ ├── external-dns │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ ├── namespace.yaml │ │ ├── release.yaml │ │ ├── repository.yaml │ │ ├── secret.yaml │ │ └── values.yaml │ ├── external-secrets │ │ ├── deployment-crds.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── release.yaml │ │ └── repositories.yaml │ ├── local-path │ │ └── kustomization.yaml │ ├── renovate │ │ ├── configmap.yaml │ │ ├── cronjob.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── secrets.yaml │ └── synology-csi │ │ ├── controller.yaml │ │ ├── csi-driver.yaml │ │ ├── kustomization.yaml │ │ ├── node.yaml │ │ ├── storage-class-retain.yaml │ │ ├── storage-class-smb.yaml │ │ └── storage-class.yaml │ ├── data │ ├── cilium │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ ├── release.yaml │ │ ├── repository.yaml │ │ └── values.yaml │ ├── cloudnativepg │ │ └── kustomization.yaml │ ├── external-dns │ │ └── kustomization.yaml │ ├── external-secrets │ │ └── kustomization.yaml │ ├── kustomization.yaml │ └── local-path │ │ └── kustomization.yaml │ └── jotunheim │ ├── cilium │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ ├── release.yaml │ ├── repository.yaml │ └── values.yaml │ ├── external-dns │ └── kustomization.yaml │ ├── external-secrets │ └── kustomization.yaml │ ├── kustomization.yaml │ ├── local-path │ └── kustomization.yaml │ └── renovate │ └── kustomization.yaml ├── monitoring ├── configs │ ├── base │ │ ├── dashboards │ │ │ ├── extra-dashboards │ │ │ │ ├── cloudflared.json │ │ │ │ ├── edb-postgres.json │ │ │ │ ├── node-exporter-full.json │ │ │ │ └── node-exporter-nodename.json │ │ │ ├── kustomization.yaml │ │ │ ├── personal-dashboards │ │ │ │ ├── running-this-week.json │ │ │ │ ├── running-this-year.json │ │ │ │ └── zettelkasten-count.json │ │ │ └── standard-dashboards │ │ │ │ ├── alertmanager-overview.json │ │ │ │ ├── apiserver.json │ │ │ │ ├── cluster-total.json │ │ │ │ ├── controller-manager.json │ │ │ │ ├── etcd.json │ │ │ │ ├── grafana-overview.json │ │ │ │ ├── k8s-coredns.json │ │ │ │ ├── k8s-resources-cluster.json │ │ │ │ ├── k8s-resources-multicluster.json │ │ │ │ ├── k8s-resources-namespace.json │ │ │ │ ├── k8s-resources-node.json │ │ │ │ ├── k8s-resources-pod.json │ │ │ │ ├── k8s-resources-workload.json │ │ │ │ ├── k8s-resources-workloads-namespace.json │ │ │ │ ├── kubelet.json │ │ │ │ ├── namespace-by-pod.json │ │ │ │ ├── namespace-by-workload.json │ │ │ │ ├── node-cluster-rsrc-use.json │ │ │ │ ├── node-rsrc-use.json │ │ │ │ ├── nodes-darwin.json │ │ │ │ ├── nodes.json │ │ │ │ ├── persistentvolumesusage.json │ │ │ │ ├── pod-total.json │ │ │ │ ├── prometheus.json │ │ │ │ ├── proxy.json │ │ │ │ ├── scheduler.json │ │ │ │ └── workload-total.json │ │ ├── grafana │ │ │ ├── datasources.yaml │ │ │ ├── kustomization.yaml │ │ │ └── loadbalancer.yaml │ │ └── prometheus │ │ │ ├── cloudflare.yaml │ │ │ ├── kustomization.yaml │ │ │ └── postgresql-operator-default-alerts.yaml │ └── jotunheim │ │ └── grafana │ │ └── dashboards │ │ ├── home-iot │ │ └── home-iot.json │ │ ├── kustomization.yaml │ │ └── quantified-self │ │ ├── health.json │ │ ├── sleep.json │ │ └── zettelkasten.json └── controllers │ ├── base │ ├── grafana │ │ ├── kustomization.yaml │ │ ├── release.yaml │ │ └── repository.yaml │ ├── kube-prometheus-stack │ │ ├── kustomization.yaml │ │ ├── release.yaml │ │ └── repository.yaml │ ├── loki │ │ ├── kustomization.yaml │ │ └── release.yaml │ └── promtail │ │ ├── kustomization.yaml │ │ └── promtail.yaml │ ├── data │ └── kube-prometheus-stack │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ ├── namespace.yaml │ │ ├── release.yaml │ │ ├── repository.yaml │ │ ├── secrets.yaml │ │ └── values.yaml │ └── jotunheim │ ├── kube-prometheus-stack │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ ├── namespace.yaml │ ├── release.yaml │ ├── repository.yaml │ ├── secrets.yaml │ └── values.yaml │ ├── kustomization.yaml │ ├── loki │ └── kustomization.yaml │ └── promtail │ └── kustomization.yaml ├── renovate.json └── utils ├── README.md ├── dashboards ├── configmapcollector ├── dashboardcopy ├── oct-2024 │ ├── health-metrics.json │ ├── home-iot.json │ └── zk-tracker.json ├── personal-dashboards │ ├── dashboardcopy │ ├── running-this-week.json │ └── running-this-year.json └── standard-dashboards │ ├── alertmanager-overview.json │ ├── apiserver.json │ ├── cluster-total.json │ ├── controller-manager.json │ ├── etcd.json │ ├── grafana-overview.json │ ├── k8s-coredns.json │ ├── k8s-resources-cluster.json │ ├── k8s-resources-multicluster.json │ ├── k8s-resources-namespace.json │ ├── k8s-resources-node.json │ ├── k8s-resources-pod.json │ ├── k8s-resources-workload.json │ ├── k8s-resources-workloads-namespace.json │ ├── kubelet.json │ ├── namespace-by-pod.json │ ├── namespace-by-workload.json │ ├── node-cluster-rsrc-use.json │ ├── node-rsrc-use.json │ ├── nodes-darwin.json │ ├── nodes.json │ ├── persistentvolumesusage.json │ ├── pod-total.json │ ├── prometheus.json │ ├── proxy.json │ ├── scheduler.json │ └── workload-total.json └── database ├── README.md ├── backups ├── backup-mealie.yaml └── test-restore-mealie.yaml ├── delete.py ├── deployment_example.yaml ├── generated-configs ├── health_kubernetes_configs.yaml ├── shelly_kubernetes_configs.yaml └── skool_kubernetes_configs.yaml ├── kubernetes_config_template.yaml ├── main.py ├── poetry.lock ├── pyproject.toml ├── secrets_example.yaml ├── setup_db.py └── yaml_generator.py /.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/base:debian", 3 | "features": { 4 | "ghcr.io/devcontainers-contrib/features/neovim-homebrew:1": {} 5 | }, 6 | "onCreateCommand": "sudo chsh -s /usr/bin/zsh $USER", 7 | "settings": { 8 | "terminal.integrated.defaultProfile.linux": "zsh", 9 | "terminal.integrated.profiles.linux": { 10 | "zsh": { 11 | "path": "/usr/bin/zsh" 12 | } 13 | } 14 | }, 15 | "remoteEnv": { 16 | "LANG": "en_US.UTF-8" 17 | }, 18 | "forwardPorts": [ 19 | 8000 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/flux-updates.yaml: -------------------------------------------------------------------------------- 1 | name: update-flux 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | permissions: 9 | contents: write 10 | pull-requests: write 11 | 12 | jobs: 13 | components: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Check out code 17 | uses: actions/checkout@v3 18 | - name: Setup Flux CLI 19 | uses: fluxcd/flux2/action@main 20 | - name: Check for updates 21 | id: update 22 | run: | 23 | flux install \ 24 | --export \ 25 | --components-extra=image-reflector-controller,image-automation-controller \ 26 | > ./clusters/production/flux-system/gotk-components.yaml 27 | 28 | VERSION="$(flux -v)" 29 | echo "flux_version=$VERSION" >> $GITHUB_OUTPUT 30 | - name: Create Pull Request 31 | uses: peter-evans/create-pull-request@v4 32 | with: 33 | token: ${{ secrets.GITHUB_TOKEN }} 34 | branch: update-flux 35 | commit-message: Update to ${{ steps.update.outputs.flux_version }} 36 | title: Update to ${{ steps.update.outputs.flux_version }} 37 | body: | 38 | ${{ steps.update.outputs.flux_version }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | private/ 2 | .DS_Store 3 | 4 | __pycache__ 5 | .envrc 6 | kubeconfig 7 | valhalla/ 8 | talosconfig 9 | admin/ 10 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | MD013: false 2 | MD033: false 3 | MD045: false 4 | -------------------------------------------------------------------------------- /apps/base/commafeed/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: commafeed 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: commafeed 10 | 11 | template: 12 | metadata: 13 | labels: 14 | app: commafeed 15 | try: "0" 16 | policy-type: "app" 17 | 18 | spec: 19 | securityContext: 20 | runAsUser: 1000 21 | runAsGroup: 3000 22 | fsGroup: 2000 23 | 24 | containers: 25 | - name: commafeed 26 | image: athou/commafeed:4.5.0 27 | 28 | securityContext: 29 | allowPrivilegeEscalation: false 30 | 31 | envFrom: 32 | - configMapRef: 33 | name: commafeed-configmap 34 | - secretRef: 35 | name: commafeed-container-env 36 | 37 | ports: 38 | - containerPort: 8082 39 | protocol: TCP 40 | 41 | restartPolicy: Always 42 | -------------------------------------------------------------------------------- /apps/base/commafeed/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - namespace.yaml 5 | - secrets.yaml 6 | - networking.yaml 7 | - deployment.yaml 8 | -------------------------------------------------------------------------------- /apps/base/commafeed/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: commafeed 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/commafeed/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: commafeed 5 | spec: 6 | ports: 7 | - port: 8082 8 | selector: 9 | app: commafeed 10 | type: ClusterIP 11 | -------------------------------------------------------------------------------- /apps/base/commafeed/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: commafeed-container-env 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: CF_DATABASE_USER 12 | remoteRef: 13 | key: commafeed-db-username 14 | - secretKey: CF_DATABASE_PASSWORD 15 | remoteRef: 16 | key: commafeed-db-password 17 | --- 18 | apiVersion: external-secrets.io/v1beta1 19 | kind: ExternalSecret 20 | metadata: 21 | name: cloudflare-tunnel 22 | namespace: commafeed 23 | spec: 24 | refreshInterval: 1h 25 | secretStoreRef: 26 | name: azure-kv-store 27 | kind: ClusterSecretStore 28 | data: 29 | - secretKey: credentials.json 30 | remoteRef: 31 | key: commafeed-cloudflare-tunnel 32 | 33 | -------------------------------------------------------------------------------- /apps/base/health-api/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: health-api 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: health-api 10 | template: 11 | metadata: 12 | labels: 13 | app: health-api 14 | policy-type: "app" 15 | spec: 16 | containers: 17 | - name: health-api 18 | image: ghcr.io/mischavandenburg/health-api:v0.2.6 19 | securityContext: 20 | allowPrivilegeEscalation: false 21 | 22 | envFrom: 23 | - configMapRef: 24 | name: health-api-configmap 25 | - secretRef: 26 | name: health-api-container-env 27 | 28 | restartPolicy: Always 29 | -------------------------------------------------------------------------------- /apps/base/health-api/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - namespace.yaml 5 | - secrets.yaml 6 | - networking.yaml 7 | - deployment.yaml 8 | -------------------------------------------------------------------------------- /apps/base/health-api/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: commafeed 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/health-api/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: health-api 5 | spec: 6 | ports: 7 | - port: 8000 8 | selector: 9 | app: health-api 10 | type: ClusterIP 11 | -------------------------------------------------------------------------------- /apps/base/health-api/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: health-api-container-env 5 | namespace: health 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: DB_USER 13 | remoteRef: 14 | key: health-db-username 15 | - secretKey: DB_PASSWORD 16 | remoteRef: 17 | key: health-db-password 18 | -------------------------------------------------------------------------------- /apps/base/homepage/clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: homepage 5 | labels: 6 | app.kubernetes.io/name: homepage 7 | rules: 8 | - apiGroups: 9 | - "" 10 | resources: 11 | - namespaces 12 | - pods 13 | - nodes 14 | verbs: 15 | - get 16 | - list 17 | - apiGroups: 18 | - extensions 19 | - networking.k8s.io 20 | resources: 21 | - ingresses 22 | verbs: 23 | - get 24 | - list 25 | - apiGroups: 26 | - traefik.containo.us 27 | resources: 28 | - ingressroutes 29 | verbs: 30 | - get 31 | - list 32 | - apiGroups: 33 | - metrics.k8s.io 34 | resources: 35 | - nodes 36 | - pods 37 | verbs: 38 | - get 39 | - list 40 | --- 41 | apiVersion: rbac.authorization.k8s.io/v1 42 | kind: ClusterRoleBinding 43 | metadata: 44 | name: homepage 45 | labels: 46 | app.kubernetes.io/name: homepage 47 | roleRef: 48 | apiGroup: rbac.authorization.k8s.io 49 | kind: ClusterRole 50 | name: homepage 51 | subjects: 52 | - kind: ServiceAccount 53 | name: homepage 54 | namespace: homepage 55 | -------------------------------------------------------------------------------- /apps/base/homepage/configmap-env.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: homepage-env-configmap 5 | data: 6 | HOMEPAGE_ALLOWED_HOSTS: "homepage.mischavandenburg.net" 7 | -------------------------------------------------------------------------------- /apps/base/homepage/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: homepage 5 | labels: 6 | app.kubernetes.io/name: homepage 7 | try: "1" 8 | spec: 9 | revisionHistoryLimit: 3 10 | replicas: 1 11 | strategy: 12 | type: RollingUpdate 13 | selector: 14 | matchLabels: 15 | app.kubernetes.io/name: homepage 16 | template: 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: homepage 20 | spec: 21 | serviceAccountName: homepage 22 | automountServiceAccountToken: true 23 | dnsPolicy: ClusterFirst 24 | enableServiceLinks: true 25 | securityContext: 26 | runAsUser: 1000 27 | runAsGroup: 1000 28 | fsGroup: 1000 29 | containers: 30 | - name: homepage 31 | image: "ghcr.io/gethomepage/homepage:v0.10.9" 32 | imagePullPolicy: Always 33 | envFrom: 34 | - configMapRef: 35 | name: homepage-env-configmap 36 | ports: 37 | - name: http 38 | containerPort: 3000 39 | protocol: TCP 40 | securityContext: 41 | allowPrivilegeEscalation: false 42 | runAsNonRoot: true 43 | seccompProfile: 44 | type: "RuntimeDefault" 45 | capabilities: 46 | drop: 47 | - ALL 48 | volumeMounts: 49 | - mountPath: /app/config/custom.js 50 | name: homepage-config 51 | subPath: custom.js 52 | - mountPath: /app/config/custom.css 53 | name: homepage-config 54 | subPath: custom.css 55 | - mountPath: /app/config/bookmarks.yaml 56 | name: homepage-config 57 | subPath: bookmarks.yaml 58 | - mountPath: /app/config/docker.yaml 59 | name: homepage-config 60 | subPath: docker.yaml 61 | - mountPath: /app/config/kubernetes.yaml 62 | name: homepage-config 63 | subPath: kubernetes.yaml 64 | - mountPath: /app/config/services.yaml 65 | name: homepage-config 66 | subPath: services.yaml 67 | - mountPath: /app/config/settings.yaml 68 | name: homepage-config 69 | subPath: settings.yaml 70 | - mountPath: /app/config/widgets.yaml 71 | name: homepage-config 72 | subPath: widgets.yaml 73 | - mountPath: /app/config/logs 74 | name: logs 75 | volumes: 76 | - name: homepage-config 77 | # configMap: 78 | # name: homepage 79 | secret: 80 | secretName: homepage-config 81 | - name: logs 82 | emptyDir: {} 83 | -------------------------------------------------------------------------------- /apps/base/homepage/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: homepage 5 | labels: 6 | app.kubernetes.io/name: homepage 7 | annotations: 8 | external-dns.alpha.kubernetes.io/hostname: homepage.mischavandenburg.net 9 | # cert-manager.io/cluster-issuer: letsencrypt-production 10 | gethomepage.dev/description: Dynamically Detected Homepage 11 | gethomepage.dev/enabled: "true" 12 | gethomepage.dev/group: Cluster Management 13 | gethomepage.dev/icon: homepage.png 14 | gethomepage.dev/name: Homepage 15 | spec: 16 | ingressClassName: cilium 17 | rules: 18 | - host: homepage.mischavandenburg.net 19 | http: 20 | paths: 21 | - path: "/" 22 | pathType: Prefix 23 | backend: 24 | service: 25 | name: homepage 26 | port: 27 | number: 3000 28 | # tls: 29 | # - hosts: 30 | # - homepage.mischavandenburg.net 31 | # secretName: homepage-ingress-production 32 | -------------------------------------------------------------------------------- /apps/base/homepage/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: homepage 4 | resources: 5 | - namespace.yaml 6 | - secret.yaml 7 | - serviceaccount.yaml 8 | - service.yaml 9 | - ingress.yaml 10 | - configmap-env.yaml 11 | - config-secret.yaml 12 | - deployment.yaml 13 | - clusterrole.yaml 14 | 15 | # The configmap for the dashboard is deployed from a private repo becuase I want to keep certain bookmarks private. 16 | -------------------------------------------------------------------------------- /apps/base/homepage/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: homepage 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/homepage/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: kubernetes.io/service-account-token 4 | metadata: 5 | name: homepage 6 | labels: 7 | app.kubernetes.io/name: homepage 8 | annotations: 9 | kubernetes.io/service-account.name: homepage 10 | -------------------------------------------------------------------------------- /apps/base/homepage/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: homepage 5 | labels: 6 | app.kubernetes.io/name: homepage 7 | annotations: 8 | spec: 9 | type: ClusterIP 10 | ports: 11 | - port: 3000 12 | targetPort: http 13 | protocol: TCP 14 | name: http 15 | selector: 16 | app.kubernetes.io/name: homepage 17 | -------------------------------------------------------------------------------- /apps/base/homepage/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: homepage 5 | labels: 6 | app.kubernetes.io/name: homepage 7 | secrets: 8 | - name: homepage 9 | -------------------------------------------------------------------------------- /apps/base/linkding/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: linkding 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: linkding 10 | template: 11 | metadata: 12 | labels: 13 | app: linkding 14 | policy-type: "app" 15 | spec: 16 | securityContext: 17 | runAsUser: 33 18 | runAsGroup: 33 19 | fsGroup: 33 20 | 21 | containers: 22 | - name: linkding 23 | image: sissbruecker/linkding:1.39.1 24 | 25 | securityContext: 26 | allowPrivilegeEscalation: false 27 | 28 | envFrom: 29 | - configMapRef: 30 | name: linkding-configmap 31 | - secretRef: 32 | name: linkding-container-env 33 | 34 | ports: 35 | - containerPort: 3003 36 | protocol: TCP 37 | 38 | volumeMounts: 39 | - name: linkding-data 40 | mountPath: /etc/linkding/data 41 | 42 | restartPolicy: Always 43 | 44 | volumes: 45 | - name: linkding-data 46 | persistentVolumeClaim: 47 | claimName: linkding-data 48 | -------------------------------------------------------------------------------- /apps/base/linkding/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: linkding 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - networking.yaml 8 | - deployment.yaml 9 | - storage.yaml 10 | -------------------------------------------------------------------------------- /apps/base/linkding/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: linkding 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/linkding/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: linkding 5 | spec: 6 | ports: 7 | - port: 3003 8 | selector: 9 | app: linkding 10 | type: ClusterIP 11 | -------------------------------------------------------------------------------- /apps/base/linkding/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: linkding-container-env 5 | namespace: linkding 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: LD_SUPERUSER_NAME 13 | remoteRef: 14 | key: linkding-superuser-name 15 | - secretKey: LD_SUPERUSER_PASSWORD 16 | remoteRef: 17 | key: linkding-superuser-password 18 | - secretKey: LD_DB_USER 19 | remoteRef: 20 | key: linkding-db-username 21 | - secretKey: LD_DB_PASSWORD 22 | remoteRef: 23 | key: linkding-db-password 24 | --- 25 | apiVersion: external-secrets.io/v1beta1 26 | kind: ExternalSecret 27 | metadata: 28 | name: cloudflare-tunnel 29 | namespace: linkding 30 | spec: 31 | refreshInterval: 1h 32 | secretStoreRef: 33 | name: azure-kv-store 34 | kind: ClusterSecretStore 35 | data: 36 | - secretKey: credentials.json 37 | remoteRef: 38 | key: linkding-cloudflare-tunnel 39 | -------------------------------------------------------------------------------- /apps/base/linkding/storage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: linkding-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 500Mi 11 | -------------------------------------------------------------------------------- /apps/base/n8n/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: n8n 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: n8n 10 | 11 | template: 12 | metadata: 13 | labels: 14 | app: n8n 15 | try: "0" 16 | policy-type: "app" 17 | 18 | spec: 19 | securityContext: 20 | runAsUser: 1000 21 | runAsGroup: 1000 22 | fsGroup: 1000 23 | 24 | containers: 25 | - name: n8n 26 | image: docker.n8n.io/n8nio/n8n:1.78.0 27 | 28 | securityContext: 29 | allowPrivilegeEscalation: false 30 | 31 | envFrom: 32 | - configMapRef: 33 | name: n8n-configmap 34 | - secretRef: 35 | name: n8n-container-env 36 | 37 | ports: 38 | - containerPort: 3008 39 | protocol: TCP 40 | 41 | volumeMounts: 42 | - mountPath: /home/node/.n8n 43 | name: n8n-data 44 | 45 | restartPolicy: Always 46 | 47 | volumes: 48 | - name: n8n-data 49 | persistentVolumeClaim: 50 | claimName: n8n-data 51 | -------------------------------------------------------------------------------- /apps/base/n8n/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - namespace.yaml 5 | - secrets.yaml 6 | - networking.yaml 7 | - deployment.yaml 8 | - storage.yaml 9 | -------------------------------------------------------------------------------- /apps/base/n8n/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: n8n 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/n8n/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: n8n 5 | spec: 6 | ports: 7 | - port: 3008 8 | selector: 9 | app: n8n 10 | type: ClusterIP 11 | -------------------------------------------------------------------------------- /apps/base/n8n/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: n8n-container-env 5 | namespace: n8n 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: DB_POSTGRESDB_USER 13 | remoteRef: 14 | key: n8n-db-username 15 | - secretKey: DB_POSTGRESDB_PASSWORD 16 | remoteRef: 17 | key: n8n-db-password 18 | # --- 19 | # apiVersion: external-secrets.io/v1beta1 20 | # kind: ExternalSecret 21 | # metadata: 22 | # name: cloudflare-tunnel 23 | # namespace: n8n 24 | # spec: 25 | # refreshInterval: 1h 26 | # secretStoreRef: 27 | # name: azure-kv-store 28 | # kind: ClusterSecretStore 29 | # data: 30 | # - secretKey: tunnel-credentials 31 | # remoteRef: 32 | # key: n8n-cloudflare-tunnel 33 | -------------------------------------------------------------------------------- /apps/base/n8n/storage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: n8n-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi 11 | -------------------------------------------------------------------------------- /apps/base/shelly-api/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: shelly-api 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: shelly-api 10 | template: 11 | metadata: 12 | labels: 13 | app: shelly-api 14 | policy-type: "app" 15 | spec: 16 | # Set security context for the pod 17 | securityContext: 18 | runAsUser: 1000 19 | runAsGroup: 1000 20 | fsGroup: 1000 21 | containers: 22 | - name: shelly-api 23 | image: ghcr.io/mischavandenburg/shelly:v0.3.0 24 | securityContext: 25 | allowPrivilegeEscalation: false 26 | # Ensure the container runs as user 1000 27 | runAsUser: 1000 28 | runAsGroup: 1000 29 | # Drop all capabilities for better security 30 | capabilities: 31 | drop: 32 | - ALL 33 | # Ensure the container is non-root 34 | runAsNonRoot: true 35 | 36 | envFrom: 37 | - configMapRef: 38 | name: shelly-configmap 39 | - secretRef: 40 | name: shelly-container-env 41 | 42 | restartPolicy: Always 43 | -------------------------------------------------------------------------------- /apps/base/shelly-api/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - secrets.yaml 5 | - deployment.yaml 6 | -------------------------------------------------------------------------------- /apps/base/shelly-api/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: shelly-api 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/shelly-api/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: shelly-container-env 5 | namespace: shelly 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: MQTT_USERNAME 13 | remoteRef: 14 | key: shelly-mqtt-username 15 | - secretKey: MQTT_PASSWORD 16 | remoteRef: 17 | key: shelly-mqtt-password 18 | - secretKey: PG_USER 19 | remoteRef: 20 | key: shelly-db-username 21 | - secretKey: PG_PASSWORD 22 | remoteRef: 23 | key: shelly-db-password 24 | -------------------------------------------------------------------------------- /apps/base/wallabag/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: wallabag 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: wallabag 10 | 11 | template: 12 | metadata: 13 | labels: 14 | app: wallabag 15 | try: "0" 16 | policy-type: "app" 17 | 18 | spec: 19 | # envsubst in the entrypoint sh needs to write to /etc/php 20 | # securityContext: 21 | # runAsUser: 1000 22 | # runAsGroup: 3000 23 | # fsGroup: 2000 24 | 25 | containers: 26 | - name: wallabag 27 | image: ghcr.io/wallabag/docker:2.6.10 28 | 29 | securityContext: 30 | allowPrivilegeEscalation: false 31 | 32 | envFrom: 33 | - configMapRef: 34 | name: wallabag-configmap 35 | - secretRef: 36 | name: wallabag-container-env 37 | 38 | ports: 39 | - containerPort: 80 40 | protocol: TCP 41 | 42 | restartPolicy: Always 43 | -------------------------------------------------------------------------------- /apps/base/wallabag/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: wallabag 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - networking.yaml 8 | - deployment.yaml 9 | -------------------------------------------------------------------------------- /apps/base/wallabag/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: wallabag 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/wallabag/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: wallabag 5 | spec: 6 | ports: 7 | - protocol: TCP 8 | port: 8083 9 | targetPort: 80 10 | name: http 11 | selector: 12 | app: wallabag 13 | type: ClusterIP 14 | -------------------------------------------------------------------------------- /apps/base/wallabag/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: wallabag-container-env 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: POSTGRES_PASSWORD 12 | remoteRef: 13 | key: wallabag-db-password 14 | - secretKey: SYMFONY__ENV__DATABASE_PASSWORD 15 | remoteRef: 16 | key: wallabag-symphony-password 17 | --- 18 | apiVersion: external-secrets.io/v1beta1 19 | kind: ExternalSecret 20 | metadata: 21 | name: cloudflare-tunnel 22 | spec: 23 | refreshInterval: 1h 24 | secretStoreRef: 25 | name: azure-kv-store 26 | kind: ClusterSecretStore 27 | data: 28 | - secretKey: credentials.json 29 | remoteRef: 30 | key: wallabag-cloudflare-tunnel 31 | -------------------------------------------------------------------------------- /apps/base/zettelkasten-tracker/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: zettelkasten-tracker 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: zettelkasten-tracker 10 | template: 11 | metadata: 12 | labels: 13 | app: zettelkasten-tracker 14 | try: "1" 15 | policy-type: "app" 16 | spec: 17 | # securityContext: 18 | # runAsUser: 1000 19 | # runAsGroup: 3000 20 | # fsGroup: 2000 21 | containers: 22 | - name: zettelkasten-tracker 23 | image: ghcr.io/mischavandenburg/zettelkasten-tracker:v0.3.4 # {"$imagepolicy": "flux-system:zettelkasten-tracker"} 24 | # securityContext: 25 | # allowPrivilegeEscalation: false 26 | envFrom: 27 | - configMapRef: 28 | name: zettelkasten-tracker-configmap 29 | - secretRef: 30 | name: zettelkasten-tracker-container-env 31 | ports: 32 | - containerPort: 3009 33 | protocol: TCP 34 | 35 | restartPolicy: Always 36 | -------------------------------------------------------------------------------- /apps/base/zettelkasten-tracker/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: zettelkasten-tracker 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - networking.yaml 8 | - deployment.yaml 9 | -------------------------------------------------------------------------------- /apps/base/zettelkasten-tracker/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: zettelkasten-tracker 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /apps/base/zettelkasten-tracker/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: zettelkasten-tracker 5 | spec: 6 | ports: 7 | - port: 3009 8 | selector: 9 | app: zettelkasten-tracker 10 | type: ClusterIP 11 | -------------------------------------------------------------------------------- /apps/base/zettelkasten-tracker/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: zettelkasten-tracker-container-env 5 | namespace: zettelkasten-tracker 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: DB_USER 13 | remoteRef: 14 | key: zettelkasten-tracker-db-username 15 | - secretKey: DB_PASSWORD 16 | remoteRef: 17 | key: zettelkasten-tracker-db-password 18 | -------------------------------------------------------------------------------- /apps/jotunheim/commafeed/cloudflare.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: cloudflared 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: cloudflared 9 | replicas: 2 10 | template: 11 | metadata: 12 | labels: 13 | app: cloudflared 14 | policy-type: "app" 15 | spec: 16 | containers: 17 | - name: cloudflared 18 | image: cloudflare/cloudflared:2025.2.1 19 | args: 20 | - tunnel 21 | - --config 22 | - /etc/cloudflared/config/config.yaml 23 | - run 24 | livenessProbe: 25 | httpGet: 26 | path: /ready 27 | port: 2000 28 | failureThreshold: 1 29 | initialDelaySeconds: 10 30 | periodSeconds: 10 31 | 32 | ports: 33 | - containerPort: 2000 34 | name: http-metrics 35 | 36 | volumeMounts: 37 | - name: config 38 | mountPath: /etc/cloudflared/config 39 | readOnly: true 40 | - name: creds 41 | mountPath: /etc/cloudflared/creds/ 42 | readOnly: true 43 | 44 | restartPolicy: Always 45 | 46 | volumes: 47 | - name: config 48 | configMap: 49 | name: cloudflared 50 | items: 51 | - key: config.yaml 52 | path: config.yaml 53 | - name: creds 54 | secret: 55 | secretName: cloudflare-tunnel 56 | --- 57 | apiVersion: v1 58 | kind: ConfigMap 59 | metadata: 60 | name: cloudflared 61 | namespace: commafeed 62 | data: 63 | config.yaml: | 64 | # Name of the tunnel you want to run 65 | tunnel: commafeed 66 | credentials-file: /etc/cloudflared/creds/credentials.json 67 | 68 | metrics: 0.0.0.0:2000 69 | 70 | no-autoupdate: true 71 | 72 | ingress: 73 | - hostname: commafeed.mischavandenburg.net 74 | service: http://commafeed:8082 75 | 76 | - hostname: hello.mischavandenburg.net 77 | service: hello_world 78 | 79 | # This rule matches any traffic which didn't match a previous rule, and responds with HTTP 404. 80 | - service: http_status:404 81 | -------------------------------------------------------------------------------- /apps/jotunheim/commafeed/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: commafeed-configmap 5 | data: 6 | CF_APP_PUBLICURL: "https://commafeed.mischavandenburg.net" 7 | CF_DATABASE_DRIVERCLASS: "org.postgresql.Driver" 8 | CF_DATABASE_URL: "jdbc:postgresql://commafeed-db.mischavandenburg.net:5432/commafeed" 9 | -------------------------------------------------------------------------------- /apps/jotunheim/commafeed/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: commafeed 4 | resources: 5 | - configmap.yaml 6 | - ../../base/commafeed/ 7 | - cloudflare.yaml 8 | # - networkpolicy.yaml 9 | -------------------------------------------------------------------------------- /apps/jotunheim/commafeed/networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cilium.io/v2 2 | kind: CiliumNetworkPolicy 3 | metadata: 4 | name: commafeed-app 5 | spec: 6 | endpointSelector: 7 | matchLabels: 8 | policy-type: "app" 9 | ingress: 10 | - fromEndpoints: 11 | - {} # Allow traffic from all pods in the same namespace 12 | - fromEndpoints: 13 | - matchLabels: 14 | io.kubernetes.pod.namespace: monitoring 15 | - fromEndpoints: 16 | - matchLabels: 17 | io.kubernetes.pod.namespace: postgresql-operator-system 18 | egress: 19 | - toEndpoints: 20 | - matchLabels: 21 | io.kubernetes.pod.namespace: kube-system 22 | k8s-app: kube-dns 23 | toPorts: 24 | - ports: 25 | - port: "53" 26 | protocol: UDP 27 | rules: 28 | dns: 29 | - matchPattern: "*" 30 | - toEndpoints: 31 | - {} # Allow traffic to all pods in the same namespace 32 | - toEntities: 33 | - world 34 | toPorts: 35 | - ports: 36 | - port: "443" 37 | - ports: 38 | - port: "80" 39 | - ports: 40 | - port: "7844" 41 | --- 42 | apiVersion: cilium.io/v2 43 | kind: CiliumNetworkPolicy 44 | metadata: 45 | name: commafeed-database 46 | namespace: commafeed 47 | spec: 48 | endpointSelector: 49 | matchLabels: 50 | policy-type: "database" 51 | ingress: 52 | - fromEndpoints: 53 | - {} 54 | - fromEndpoints: 55 | - matchLabels: 56 | io.kubernetes.pod.namespace: monitoring 57 | - fromEndpoints: 58 | - matchLabels: 59 | io.kubernetes.pod.namespace: postgresql-operator-system 60 | egress: 61 | - toEndpoints: 62 | - {} 63 | - toEndpoints: 64 | - matchLabels: 65 | io.kubernetes.pod.namespace: kube-system 66 | k8s-app: kube-dns 67 | toPorts: 68 | - ports: 69 | - port: "53" 70 | protocol: UDP 71 | rules: 72 | dns: 73 | - matchPattern: "*" 74 | 75 | - toEntities: 76 | - kube-apiserver 77 | toPorts: 78 | - ports: 79 | - port: "6443" 80 | 81 | - toEntities: 82 | - world 83 | toPorts: 84 | - ports: 85 | - port: "443" 86 | --- 87 | apiVersion: cilium.io/v2 88 | kind: CiliumNetworkPolicy 89 | metadata: 90 | name: deny-all 91 | namespace: commafeed 92 | spec: 93 | endpointSelector: {} 94 | ingress: 95 | - {} 96 | egress: 97 | - {} 98 | -------------------------------------------------------------------------------- /apps/jotunheim/health-api/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: health-api-configmap 5 | data: 6 | DB_HOST: health-db.mischavandenburg.net 7 | DB_NAME: health 8 | -------------------------------------------------------------------------------- /apps/jotunheim/health-api/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | external-dns.alpha.kubernetes.io/hostname: health.mischavandenburg.net 6 | name: health-api 7 | spec: 8 | ingressClassName: cilium 9 | rules: 10 | - host: health.mischavandenburg.net 11 | http: 12 | paths: 13 | - backend: 14 | service: 15 | name: health-api 16 | port: 17 | number: 8000 18 | path: / 19 | pathType: Prefix 20 | -------------------------------------------------------------------------------- /apps/jotunheim/health-api/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: health-api 4 | resources: 5 | - configmap.yaml 6 | - ../../base/health-api/ 7 | - ingress.yaml 8 | -------------------------------------------------------------------------------- /apps/jotunheim/homepage/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/homepage/ 5 | -------------------------------------------------------------------------------- /apps/jotunheim/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - commafeed 5 | - health-api 6 | - homepage 7 | - linkding 8 | - n8n 9 | - shelly-api 10 | - wallabag 11 | - zettelkasten-tracker 12 | -------------------------------------------------------------------------------- /apps/jotunheim/linkding/cloudflare.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: cloudflared 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: cloudflared 9 | replicas: 2 10 | template: 11 | metadata: 12 | labels: 13 | app: cloudflared 14 | policy-type: "app" 15 | spec: 16 | containers: 17 | - name: cloudflared 18 | image: cloudflare/cloudflared:2025.2.1 19 | args: 20 | - tunnel 21 | - --config 22 | - /etc/cloudflared/config/config.yaml 23 | - run 24 | livenessProbe: 25 | httpGet: 26 | path: /ready 27 | port: 2000 28 | failureThreshold: 1 29 | initialDelaySeconds: 10 30 | periodSeconds: 10 31 | 32 | ports: 33 | - containerPort: 2000 34 | name: http-metrics 35 | 36 | volumeMounts: 37 | - name: config 38 | mountPath: /etc/cloudflared/config 39 | readOnly: true 40 | - name: creds 41 | mountPath: /etc/cloudflared/creds/ 42 | readOnly: true 43 | 44 | restartPolicy: Always 45 | 46 | volumes: 47 | - name: creds 48 | secret: 49 | secretName: cloudflare-tunnel 50 | - name: config 51 | configMap: 52 | name: cloudflared 53 | items: 54 | - key: config.yaml 55 | path: config.yaml 56 | --- 57 | apiVersion: v1 58 | kind: ConfigMap 59 | metadata: 60 | name: cloudflared 61 | namespace: linkding 62 | data: 63 | config.yaml: | 64 | tunnel: linkding 65 | credentials-file: /etc/cloudflared/creds/credentials.json 66 | 67 | metrics: 0.0.0.0:2000 68 | 69 | no-autoupdate: true 70 | ingress: 71 | - hostname: linkding.mischavandenburg.net 72 | service: http://linkding:3003 73 | 74 | - hostname: hello.example.com 75 | service: hello_world 76 | - service: http_status:404 77 | -------------------------------------------------------------------------------- /apps/jotunheim/linkding/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: linkding-configmap 5 | data: 6 | LD_SERVER_PORT: "3003" 7 | LD_DB_ENGINE: postgres 8 | LD_DB_DATABASE: linkding 9 | LD_DB_PORT: "5432" 10 | LD_DB_HOST: linkding-pg.mischavandenburg.net 11 | -------------------------------------------------------------------------------- /apps/jotunheim/linkding/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: linkding 4 | resources: 5 | - configmap.yaml 6 | # - database-cnpg.yaml 7 | # - scheduled-backup-cnpg.yaml 8 | - ../../base/linkding/ 9 | - cloudflare.yaml 10 | # - networkpolicy.yaml 11 | 12 | patches: 13 | - target: 14 | kind: SecretProviderClass 15 | patch: |- 16 | - op: replace 17 | path: /spec/parameters/keyvaultName 18 | value: k8s-homelab-production 19 | -------------------------------------------------------------------------------- /apps/jotunheim/linkding/networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cilium.io/v2 2 | kind: CiliumNetworkPolicy 3 | metadata: 4 | name: linkding-app 5 | spec: 6 | endpointSelector: 7 | matchLabels: 8 | policy-type: "app" 9 | ingress: 10 | - fromEndpoints: 11 | - {} 12 | - fromEndpoints: 13 | - matchLabels: 14 | io.kubernetes.pod.namespace: monitoring 15 | - fromEndpoints: 16 | - matchLabels: 17 | io.kubernetes.pod.namespace: postgresql-operator-system 18 | egress: 19 | - toEndpoints: 20 | - matchLabels: 21 | io.kubernetes.pod.namespace: kube-system 22 | k8s-app: kube-dns 23 | toPorts: 24 | - ports: 25 | - port: "53" 26 | protocol: UDP 27 | rules: 28 | dns: 29 | - matchPattern: "*" 30 | - toEndpoints: 31 | - {} 32 | - toEntities: 33 | - world 34 | toPorts: 35 | - ports: 36 | - port: "443" 37 | - ports: 38 | - port: "80" 39 | - ports: 40 | - port: "7844" 41 | --- 42 | apiVersion: cilium.io/v2 43 | kind: CiliumNetworkPolicy 44 | metadata: 45 | name: linkding-database 46 | namespace: linkding 47 | spec: 48 | endpointSelector: 49 | matchLabels: 50 | policy-type: "database" 51 | ingress: 52 | - fromEndpoints: 53 | - {} 54 | - fromEndpoints: 55 | - matchLabels: 56 | io.kubernetes.pod.namespace: monitoring 57 | - fromEndpoints: 58 | - matchLabels: 59 | io.kubernetes.pod.namespace: postgresql-operator-system 60 | egress: 61 | - toEndpoints: 62 | - {} 63 | - toEndpoints: 64 | - matchLabels: 65 | io.kubernetes.pod.namespace: kube-system 66 | k8s-app: kube-dns 67 | toPorts: 68 | - ports: 69 | - port: "53" 70 | protocol: UDP 71 | rules: 72 | dns: 73 | - matchPattern: "*" 74 | 75 | - toEntities: 76 | - kube-apiserver 77 | toPorts: 78 | - ports: 79 | - port: "6443" 80 | 81 | - toEntities: 82 | - world 83 | toPorts: 84 | - ports: 85 | - port: "443" 86 | --- 87 | apiVersion: cilium.io/v2 88 | kind: CiliumNetworkPolicy 89 | metadata: 90 | name: deny-all 91 | namespace: linkding 92 | spec: 93 | endpointSelector: {} 94 | ingress: 95 | - {} 96 | egress: 97 | - {} 98 | -------------------------------------------------------------------------------- /apps/jotunheim/n8n/cloudflare.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: cloudflared 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: cloudflared 9 | replicas: 2 # You could also consider elastic scaling for this deployment 10 | template: 11 | metadata: 12 | labels: 13 | app: cloudflared 14 | policy-type: "app" 15 | spec: 16 | containers: 17 | - name: cloudflared 18 | image: cloudflare/cloudflared:2025.2.1 19 | args: 20 | - tunnel 21 | # Points cloudflared to the config file, which configures what 22 | # cloudflared will actually do. This file is created by a ConfigMap 23 | # below. 24 | - --config 25 | - /etc/cloudflared/config/config.yaml 26 | - run 27 | livenessProbe: 28 | httpGet: 29 | # Cloudflared has a /ready endpoint which returns 200 if and only if 30 | # it has an active connection to the edge. 31 | path: /ready 32 | port: 2000 33 | failureThreshold: 1 34 | initialDelaySeconds: 10 35 | periodSeconds: 10 36 | 37 | ports: 38 | - containerPort: 2000 39 | name: http-metrics 40 | 41 | volumeMounts: 42 | - name: config 43 | mountPath: /etc/cloudflared/config 44 | readOnly: true 45 | # Each tunnel has an associated "credentials file" which authorizes machines 46 | # to run the tunnel. cloudflared will read this file from its local filesystem, 47 | # and it'll be stored in a k8s secret. 48 | - name: creds 49 | mountPath: /etc/cloudflared/creds/ 50 | readOnly: true 51 | - name: origin-cert 52 | mountPath: "/etc/cloudflared" 53 | readOnly: true 54 | 55 | restartPolicy: Always 56 | 57 | volumes: 58 | - name: creds 59 | csi: 60 | driver: secrets-store.csi.k8s.io 61 | readOnly: true 62 | volumeAttributes: 63 | secretProviderClass: azure-kv-cloudflare 64 | nodePublishSecretRef: # Only required when using service principal mode 65 | name: secrets-store-creds # Only required when using service principal mode 66 | 67 | # Create a config.yaml file from the ConfigMap below. 68 | - name: config 69 | configMap: 70 | name: cloudflared 71 | items: 72 | - key: config.yaml 73 | path: config.yaml 74 | # mount the origin cert 75 | - name: origin-cert 76 | secret: 77 | secretName: origin-cert 78 | --- 79 | # This ConfigMap is just a way to define the cloudflared config.yaml file in k8s. 80 | # It's useful to define it in k8s, rather than as a stand-alone .yaml file, because 81 | # this lets you use various k8s templating solutions (e.g. Helm charts) to 82 | # parameterize your config, instead of just using string literals. 83 | apiVersion: v1 84 | kind: ConfigMap 85 | metadata: 86 | name: cloudflared 87 | namespace: wallabag 88 | data: 89 | config.yaml: | 90 | # Name of the tunnel you want to run 91 | tunnel: wallabag 92 | credentials-file: /etc/cloudflared/creds/wallabag-cloudflare-tunnel 93 | # Serves the metrics server under /metrics and the readiness server under /ready 94 | metrics: 0.0.0.0:2000 95 | # Autoupdates applied in a k8s pod will be lost when the pod is removed or restarted, so 96 | # autoupdate doesn't make sense in Kubernetes. However, outside of Kubernetes, we strongly 97 | # recommend using autoupdate. 98 | no-autoupdate: true 99 | # The `ingress` block tells cloudflared which local service to route incoming 100 | # requests to. For more about ingress rules, see 101 | # https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ingress 102 | # 103 | # Remember, these rules route traffic from cloudflared to a local service. To route traffic 104 | # from the internet to cloudflared, run `cloudflared tunnel route dns `. 105 | # E.g. `cloudflared tunnel route dns example-tunnel tunnel.example.com`. 106 | ingress: 107 | # The first rule proxies traffic to the httpbin sample Service defined in app.yaml 108 | - hostname: wallabag.mischavandenburg.net 109 | service: http://wallabag:8083 110 | # This rule sends traffic to the built-in hello-world HTTP server. This can help debug connectivity 111 | # issues. If hello.example.com resolves and tunnel.example.com does not, then the problem is 112 | # in the connection from cloudflared to your local service, not from the internet to cloudflared. 113 | - hostname: hello.mischavandenburg.net 114 | service: hello_world 115 | # This rule matches any traffic which didn't match a previous rule, and responds with HTTP 404. 116 | - service: http_status:404 117 | -------------------------------------------------------------------------------- /apps/jotunheim/n8n/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: n8n-configmap 5 | data: 6 | N8N_PORT: "3008" 7 | N8N_PROTOCOL: "http" 8 | DB_TYPE: "postgresdb" 9 | DB_POSTGRESDB_HOST: "n8n-db.mischavandenburg.net" 10 | DB_POSTGRESDB_PORT: "5432" 11 | DB_POSTGRESDB_DATABASE: "n8n" 12 | N8N_SECURE_COOKIE: "false" 13 | -------------------------------------------------------------------------------- /apps/jotunheim/n8n/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | external-dns.alpha.kubernetes.io/hostname: n8n.mischavandenburg.net 6 | name: n8n 7 | spec: 8 | ingressClassName: cilium 9 | rules: 10 | - host: n8n.mischavandenburg.net 11 | http: 12 | paths: 13 | - backend: 14 | service: 15 | name: n8n 16 | port: 17 | number: 3008 18 | path: / 19 | pathType: Prefix 20 | -------------------------------------------------------------------------------- /apps/jotunheim/n8n/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: n8n 4 | resources: 5 | - ../../base/n8n/ 6 | - configmap.yaml 7 | - ingress.yaml 8 | -------------------------------------------------------------------------------- /apps/jotunheim/shelly-api/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: shelly-configmap 5 | data: 6 | MQTT_BROKER: "192.168.120.186" 7 | MQTT_PORT: "1883" 8 | PG_HOST: home-iot-db.mischavandenburg.net 9 | PG_DATABASE: shelly 10 | -------------------------------------------------------------------------------- /apps/jotunheim/shelly-api/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: shelly-api 4 | resources: 5 | - configmap.yaml 6 | - ../../base/shelly-api/ 7 | -------------------------------------------------------------------------------- /apps/jotunheim/wallabag/cloudflare.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: cloudflared 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: cloudflared 9 | replicas: 2 10 | template: 11 | metadata: 12 | labels: 13 | app: cloudflared 14 | policy-type: "app" 15 | spec: 16 | containers: 17 | - name: cloudflared 18 | image: cloudflare/cloudflared:2025.2.1 19 | args: 20 | - tunnel 21 | - --config 22 | - /etc/cloudflared/config/config.yaml 23 | - run 24 | livenessProbe: 25 | httpGet: 26 | path: /ready 27 | port: 2000 28 | failureThreshold: 1 29 | initialDelaySeconds: 10 30 | periodSeconds: 10 31 | 32 | ports: 33 | - containerPort: 2000 34 | name: http-metrics 35 | 36 | volumeMounts: 37 | - name: config 38 | mountPath: /etc/cloudflared/config 39 | readOnly: true 40 | - name: creds 41 | mountPath: /etc/cloudflared/creds/ 42 | readOnly: true 43 | 44 | restartPolicy: Always 45 | 46 | volumes: 47 | - name: creds 48 | secret: 49 | secretName: cloudflare-tunnel 50 | - name: config 51 | configMap: 52 | name: cloudflared 53 | items: 54 | - key: config.yaml 55 | path: config.yaml 56 | --- 57 | apiVersion: v1 58 | kind: ConfigMap 59 | metadata: 60 | name: cloudflared 61 | namespace: wallabag 62 | data: 63 | config.yaml: | 64 | tunnel: wallabag 65 | credentials-file: /etc/cloudflared/creds/credentials.json 66 | 67 | metrics: 0.0.0.0:2000 68 | 69 | no-autoupdate: true 70 | 71 | ingress: 72 | - hostname: wallabag.mischavandenburg.net 73 | service: http://wallabag:8083 74 | - hostname: hello.mischavandenburg.net 75 | service: hello_world 76 | - service: http_status:404 77 | -------------------------------------------------------------------------------- /apps/jotunheim/wallabag/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: wallabag-configmap 5 | data: 6 | POSTGRES_USER: wallabag 7 | SYMFONY__ENV__DATABASE_DRIVER: pdo_pgsql 8 | SYMFONY__ENV__DATABASE_HOST: wallabag-db.mischavandenburg.net 9 | SYMFONY__ENV__DATABASE_PORT: "5432" 10 | SYMFONY__ENV__DATABASE_NAME: wallabag 11 | SYMFONY__ENV__DATABASE_USER: wallabag 12 | SYMFONY__ENV__DOMAIN_NAME: "https://wallabag.mischavandenburg.net" 13 | -------------------------------------------------------------------------------- /apps/jotunheim/wallabag/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: wallabag 4 | resources: 5 | - configmap.yaml 6 | - ../../base/wallabag/ 7 | - cloudflare.yaml 8 | # - networkpolicy.yaml 9 | -------------------------------------------------------------------------------- /apps/jotunheim/wallabag/networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cilium.io/v2 2 | kind: CiliumNetworkPolicy 3 | metadata: 4 | name: wallabag-app 5 | namespace: wallabag 6 | spec: 7 | endpointSelector: 8 | matchLabels: 9 | policy-type: "app" 10 | ingress: 11 | - fromEndpoints: 12 | - {} # Allow traffic from all pods in the same namespace 13 | - fromEndpoints: 14 | - matchLabels: 15 | io.kubernetes.pod.namespace: monitoring 16 | - fromEndpoints: 17 | - matchLabels: 18 | io.kubernetes.pod.namespace: postgresql-operator-system 19 | egress: 20 | - toEndpoints: 21 | - matchLabels: 22 | io.kubernetes.pod.namespace: kube-system 23 | k8s-app: kube-dns 24 | toPorts: 25 | - ports: 26 | - port: "53" 27 | protocol: UDP 28 | rules: 29 | dns: 30 | - matchPattern: "*" 31 | - toEndpoints: 32 | - {} # Allow traffic to all pods in the same namespace 33 | - toEntities: 34 | - world 35 | toPorts: 36 | - ports: 37 | - port: "443" 38 | - ports: 39 | - port: "80" 40 | - ports: 41 | - port: "7844" 42 | --- 43 | apiVersion: cilium.io/v2 44 | kind: CiliumNetworkPolicy 45 | metadata: 46 | name: wallabag-database 47 | namespace: wallabag 48 | spec: 49 | endpointSelector: 50 | matchLabels: 51 | policy-type: "database" 52 | ingress: 53 | - fromEndpoints: 54 | - {} 55 | - fromEndpoints: 56 | - matchLabels: 57 | io.kubernetes.pod.namespace: monitoring 58 | - fromEndpoints: 59 | - matchLabels: 60 | io.kubernetes.pod.namespace: postgresql-operator-system 61 | egress: 62 | - toEndpoints: 63 | - {} 64 | - toEndpoints: 65 | - matchLabels: 66 | io.kubernetes.pod.namespace: kube-system 67 | k8s-app: kube-dns 68 | toPorts: 69 | - ports: 70 | - port: "53" 71 | protocol: UDP 72 | rules: 73 | dns: 74 | - matchPattern: "*" 75 | 76 | - toEntities: 77 | - kube-apiserver 78 | toPorts: 79 | - ports: 80 | - port: "6443" 81 | 82 | - toEntities: 83 | - world 84 | toPorts: 85 | - ports: 86 | - port: "443" 87 | --- 88 | apiVersion: cilium.io/v2 89 | kind: CiliumNetworkPolicy 90 | metadata: 91 | name: deny-all 92 | namespace: wallabag 93 | spec: 94 | endpointSelector: {} 95 | ingress: 96 | - {} 97 | egress: 98 | - {} 99 | -------------------------------------------------------------------------------- /apps/jotunheim/zettelkasten-tracker/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: zettelkasten-tracker-configmap 5 | data: 6 | DB_HOST: quantified-self-db.mischavandenburg.net 7 | DB_NAME: zettelkasten-tracker 8 | -------------------------------------------------------------------------------- /apps/jotunheim/zettelkasten-tracker/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | external-dns.alpha.kubernetes.io/hostname: zktracker.mischavandenburg.net 6 | name: zettelkasten-tracker 7 | spec: 8 | ingressClassName: cilium 9 | rules: 10 | - host: zktracker.mischavandenburg.net 11 | http: 12 | paths: 13 | - backend: 14 | service: 15 | name: zettelkasten-tracker 16 | port: 17 | number: 3009 18 | path: / 19 | pathType: Prefix 20 | -------------------------------------------------------------------------------- /apps/jotunheim/zettelkasten-tracker/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: zettelkasten-tracker 4 | resources: 5 | - configmap.yaml 6 | - ../../base/zettelkasten-tracker/ 7 | - podmonitor.yaml 8 | - ingress.yaml 9 | -------------------------------------------------------------------------------- /apps/jotunheim/zettelkasten-tracker/podmonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: PodMonitor 3 | metadata: 4 | name: zettelkasten-tracker-monitor 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: zettelkasten-tracker 9 | podMetricsEndpoints: 10 | - port: "3009" 11 | interval: 30s 12 | -------------------------------------------------------------------------------- /clusters/data/databases.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: databases 5 | namespace: flux-system 6 | spec: 7 | interval: 1h 8 | retryInterval: 1m 9 | timeout: 5m 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./databases/data 14 | prune: true 15 | -------------------------------------------------------------------------------- /clusters/data/flux-system/gotk-sync.yaml: -------------------------------------------------------------------------------- 1 | # This manifest was generated by flux. DO NOT EDIT. 2 | --- 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: GitRepository 5 | metadata: 6 | name: flux-system 7 | namespace: flux-system 8 | spec: 9 | interval: 1m0s 10 | ref: 11 | branch: main 12 | secretRef: 13 | name: flux-system 14 | url: ssh://git@github.com/mischavandenburg/homelab 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: flux-system 20 | namespace: flux-system 21 | spec: 22 | interval: 10m0s 23 | path: ./clusters/data 24 | prune: true 25 | sourceRef: 26 | kind: GitRepository 27 | name: flux-system 28 | -------------------------------------------------------------------------------- /clusters/data/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - gotk-components.yaml 5 | - gotk-sync.yaml 6 | -------------------------------------------------------------------------------- /clusters/data/infrastructure.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infra-controllers 5 | namespace: flux-system 6 | spec: 7 | interval: 1h 8 | retryInterval: 1m 9 | timeout: 5m 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./infrastructure/controllers/data 14 | prune: true 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: infra-configs 20 | namespace: flux-system 21 | spec: 22 | dependsOn: 23 | - name: infra-controllers 24 | interval: 1h 25 | retryInterval: 1m 26 | timeout: 5m 27 | sourceRef: 28 | kind: GitRepository 29 | name: flux-system 30 | path: ./infrastructure/configs/data 31 | prune: true 32 | -------------------------------------------------------------------------------- /clusters/data/monitoring.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: monitoring-controllers 5 | namespace: flux-system 6 | spec: 7 | dependsOn: 8 | - name: infra-configs 9 | interval: 1h 10 | retryInterval: 1m 11 | timeout: 5m 12 | sourceRef: 13 | kind: GitRepository 14 | name: flux-system 15 | path: ./monitoring/controllers/data 16 | prune: true 17 | # --- 18 | # apiVersion: kustomize.toolkit.fluxcd.io/v1 19 | # kind: Kustomization 20 | # metadata: 21 | # name: monitoring-configs 22 | # namespace: flux-system 23 | # spec: 24 | # dependsOn: 25 | # - name: monitoring-controllers 26 | # interval: 1h 27 | # retryInterval: 1m 28 | # timeout: 5m 29 | # sourceRef: 30 | # kind: GitRepository 31 | # name: flux-system 32 | # path: ./monitoring/configs/jotunheim 33 | # prune: true 34 | -------------------------------------------------------------------------------- /clusters/jotunheim/apps.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: apps 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | # dependsOn: 9 | # - name: infra-configs 10 | retryInterval: 1m 11 | timeout: 5m 12 | sourceRef: 13 | kind: GitRepository 14 | name: flux-system 15 | path: ./apps/jotunheim 16 | prune: true 17 | -------------------------------------------------------------------------------- /clusters/jotunheim/flux-system/gotk-sync.yaml: -------------------------------------------------------------------------------- 1 | # This manifest was generated by flux. DO NOT EDIT. 2 | --- 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: GitRepository 5 | metadata: 6 | name: flux-system 7 | namespace: flux-system 8 | spec: 9 | interval: 1m0s 10 | ref: 11 | branch: main 12 | secretRef: 13 | name: flux-system 14 | url: ssh://git@github.com/mischavandenburg/homelab 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: flux-system 20 | namespace: flux-system 21 | spec: 22 | interval: 10m0s 23 | path: ./clusters/jotunheim 24 | prune: true 25 | sourceRef: 26 | kind: GitRepository 27 | name: flux-system 28 | -------------------------------------------------------------------------------- /clusters/jotunheim/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - gotk-components.yaml 5 | - gotk-sync.yaml 6 | -------------------------------------------------------------------------------- /clusters/jotunheim/infrastructure.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infra-controllers 5 | namespace: flux-system 6 | spec: 7 | interval: 1h 8 | retryInterval: 1m 9 | timeout: 5m 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./infrastructure/controllers/jotunheim 14 | prune: true 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: infra-configs 20 | namespace: flux-system 21 | spec: 22 | dependsOn: 23 | - name: infra-controllers 24 | interval: 1h 25 | retryInterval: 1m 26 | timeout: 5m 27 | sourceRef: 28 | kind: GitRepository 29 | name: flux-system 30 | path: ./infrastructure/configs/jotunheim 31 | prune: true 32 | -------------------------------------------------------------------------------- /clusters/jotunheim/monitoring.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: monitoring-controllers 5 | namespace: flux-system 6 | spec: 7 | dependsOn: 8 | - name: infra-configs 9 | interval: 1h 10 | retryInterval: 1m 11 | timeout: 5m 12 | sourceRef: 13 | kind: GitRepository 14 | name: flux-system 15 | path: ./monitoring/controllers/jotunheim 16 | prune: true 17 | --- 18 | apiVersion: kustomize.toolkit.fluxcd.io/v1 19 | kind: Kustomization 20 | metadata: 21 | name: monitoring-configs 22 | namespace: flux-system 23 | spec: 24 | dependsOn: 25 | - name: monitoring-controllers 26 | interval: 1h 27 | retryInterval: 1m 28 | timeout: 5m 29 | sourceRef: 30 | kind: GitRepository 31 | name: flux-system 32 | path: ./monitoring/configs/jotunheim 33 | prune: true 34 | -------------------------------------------------------------------------------- /databases/data/commafeed/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: commafeed-db-production-cnpg-v1 5 | spec: 6 | description: Postgres cluster for the commafeed application 7 | imageName: quay.io/enterprisedb/postgresql:16.1 8 | instances: 3 9 | 10 | monitoring: 11 | enablePodMonitor: true 12 | 13 | inheritedMetadata: 14 | labels: 15 | app: commafeed-database 16 | policy-type: "database" 17 | 18 | storage: 19 | size: 5Gi 20 | 21 | bootstrap: 22 | recovery: 23 | source: clusterBackup 24 | database: commafeed 25 | owner: commafeed 26 | 27 | secret: 28 | name: commafeed-db-creds 29 | 30 | externalClusters: 31 | - name: clusterBackup 32 | barmanObjectStore: 33 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/commafeed 34 | serverName: commafeed-db-production-cnpg-v0 35 | azureCredentials: 36 | storageAccount: 37 | name: azure-creds 38 | key: commafeed-storage-account-name 39 | storageSasToken: 40 | name: azure-creds 41 | key: commafeed-blob-sas 42 | 43 | backup: 44 | barmanObjectStore: 45 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/commafeed 46 | azureCredentials: 47 | storageAccount: 48 | name: azure-creds 49 | key: commafeed-storage-account-name 50 | storageSasToken: 51 | name: azure-creds 52 | key: commafeed-blob-sas 53 | wal: 54 | compression: gzip 55 | data: 56 | compression: gzip 57 | retentionPolicy: 14d 58 | 59 | # handy: resources can be used to trigger a redeploy 60 | resources: 61 | requests: 62 | memory: 600Mi 63 | 64 | managed: 65 | services: 66 | disabledDefaultServices: 67 | - ro 68 | - r 69 | additional: 70 | - selectorType: rw 71 | serviceTemplate: 72 | metadata: 73 | name: commafeed-db-lb 74 | labels: 75 | cilium-lb-pool: postgres-pool 76 | annotations: 77 | external-dns.alpha.kubernetes.io/hostname: commafeed-db.mischavandenburg.net 78 | spec: 79 | type: LoadBalancer 80 | -------------------------------------------------------------------------------- /databases/data/commafeed/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: commafeed 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/commafeed/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: commafeed 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/commafeed/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: commafeed-db-production 5 | spec: 6 | immediate: true 7 | schedule: "0 0 3 * * *" 8 | backupOwnerReference: cluster 9 | cluster: 10 | name: commafeed-db-production-cnpg-v1 11 | -------------------------------------------------------------------------------- /databases/data/commafeed/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: azure-creds 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: commafeed-connection-string 12 | remoteRef: 13 | key: commafeed-connection-string 14 | - secretKey: commafeed-storage-account-name 15 | remoteRef: 16 | key: commafeed-storage-account-name 17 | - secretKey: commafeed-blob-sas 18 | remoteRef: 19 | key: commafeed-blob-sas 20 | --- 21 | apiVersion: external-secrets.io/v1beta1 22 | kind: ExternalSecret 23 | metadata: 24 | name: commafeed-db-creds 25 | namespace: commafeed 26 | spec: 27 | refreshInterval: 1h 28 | secretStoreRef: 29 | name: azure-kv-store 30 | kind: ClusterSecretStore 31 | target: 32 | template: 33 | type: kubernetes.io/basic-auth 34 | data: 35 | - secretKey: username 36 | remoteRef: 37 | key: commafeed-db-username 38 | - secretKey: password 39 | remoteRef: 40 | key: commafeed-db-password 41 | -------------------------------------------------------------------------------- /databases/data/health-api/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: health-db-production-cnpg-v2 5 | spec: 6 | description: Postgres cluster for the health application 7 | imageName: quay.io/enterprisedb/postgresql:16.1 8 | inheritedMetadata: 9 | labels: 10 | app: health-database 11 | policy-type: database 12 | 13 | instances: 3 14 | 15 | monitoring: 16 | enablePodMonitor: true 17 | 18 | bootstrap: 19 | recovery: 20 | source: clusterBackup 21 | database: health 22 | owner: health 23 | 24 | secret: 25 | name: health-db-creds 26 | 27 | externalClusters: 28 | - name: clusterBackup 29 | barmanObjectStore: 30 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/health 31 | 32 | # had to restore from 0 instead of 1. 33 | serverName: health-db-production-cnpg-v0 34 | azureCredentials: 35 | storageAccount: 36 | name: health-azure-creds 37 | key: health-storage-account-name 38 | storageSasToken: 39 | name: health-azure-creds 40 | key: health-blob-sas 41 | 42 | backup: 43 | barmanObjectStore: 44 | azureCredentials: 45 | storageAccount: 46 | key: health-storage-account-name 47 | name: health-azure-creds 48 | storageSasToken: 49 | key: health-blob-sas 50 | name: health-azure-creds 51 | data: 52 | compression: gzip 53 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/health 54 | wal: 55 | compression: gzip 56 | retentionPolicy: 14d 57 | 58 | resources: 59 | requests: 60 | memory: 100Mi 61 | storage: 62 | size: 1Gi 63 | 64 | managed: 65 | services: 66 | disabledDefaultServices: 67 | - ro 68 | - r 69 | additional: 70 | - selectorType: rw 71 | serviceTemplate: 72 | metadata: 73 | name: health-db-lb 74 | labels: 75 | cilium-lb-pool: postgres-pool 76 | annotations: 77 | external-dns.alpha.kubernetes.io/hostname: health-db.mischavandenburg.net 78 | spec: 79 | type: LoadBalancer 80 | -------------------------------------------------------------------------------- /databases/data/health-api/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: health-api 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/health-api/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: health-api 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/health-api/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: health-db-production 5 | namespace: health 6 | spec: 7 | backupOwnerReference: cluster 8 | cluster: 9 | name: health-db-production-cnpg-v2 10 | immediate: true 11 | schedule: 0 0 4 * * 12 | -------------------------------------------------------------------------------- /databases/data/health-api/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: health-azure-creds 5 | namespace: health 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: health-connection-string 13 | remoteRef: 14 | key: health-connection-string 15 | - secretKey: health-storage-account-name 16 | remoteRef: 17 | key: health-storage-account-name 18 | - secretKey: health-blob-sas 19 | remoteRef: 20 | key: health-blob-sas 21 | --- 22 | apiVersion: external-secrets.io/v1beta1 23 | kind: ExternalSecret 24 | metadata: 25 | name: health-db-creds 26 | namespace: health 27 | spec: 28 | refreshInterval: 1h 29 | secretStoreRef: 30 | name: azure-kv-store 31 | kind: ClusterSecretStore 32 | target: 33 | template: 34 | type: kubernetes.io/basic-auth 35 | data: 36 | - secretKey: username 37 | remoteRef: 38 | key: health-db-username 39 | - secretKey: password 40 | remoteRef: 41 | key: health-db-password 42 | -------------------------------------------------------------------------------- /databases/data/home-iot/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: home-iot-production-v0 5 | spec: 6 | backup: 7 | barmanObjectStore: 8 | azureCredentials: 9 | storageAccount: 10 | key: shelly-storage-account-name 11 | name: azure-creds 12 | storageSasToken: 13 | key: shelly-blob-sas 14 | name: azure-creds 15 | data: 16 | compression: gzip 17 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/shelly 18 | wal: 19 | compression: gzip 20 | retentionPolicy: 14d 21 | 22 | bootstrap: 23 | recovery: 24 | source: clusterBackup 25 | database: shelly 26 | owner: shelly 27 | secret: 28 | name: shelly-db-creds 29 | 30 | externalClusters: 31 | - name: clusterBackup 32 | barmanObjectStore: 33 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/shelly 34 | serverName: shelly-db-production-cnpg-v0 35 | azureCredentials: 36 | storageAccount: 37 | name: azure-creds 38 | key: shelly-storage-account-name 39 | storageSasToken: 40 | name: azure-creds 41 | key: shelly-blob-sas 42 | 43 | description: Postgres cluster for home-iot & shelly application 44 | imageName: quay.io/enterprisedb/postgresql:16.1 45 | inheritedMetadata: 46 | labels: 47 | app: shelly-database 48 | policy-type: database 49 | instances: 3 50 | monitoring: 51 | enablePodMonitor: true 52 | resources: 53 | requests: 54 | memory: 100Mi 55 | storage: 56 | size: 1Gi 57 | 58 | managed: 59 | services: 60 | disabledDefaultServices: 61 | - ro 62 | - r 63 | additional: 64 | - selectorType: rw 65 | serviceTemplate: 66 | metadata: 67 | name: home-iot-db-lb 68 | labels: 69 | cilium-lb-pool: postgres-pool 70 | annotations: 71 | external-dns.alpha.kubernetes.io/hostname: home-iot-db.mischavandenburg.net 72 | spec: 73 | type: LoadBalancer 74 | -------------------------------------------------------------------------------- /databases/data/home-iot/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: home-iot 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/home-iot/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: home-iot 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/home-iot/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: home-iot-production-v0 5 | spec: 6 | backupOwnerReference: cluster 7 | cluster: 8 | name: home-iot-production-v0 9 | immediate: true 10 | schedule: 0 0 4 * * 11 | -------------------------------------------------------------------------------- /databases/data/home-iot/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: azure-creds 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: shelly-connection-string 12 | remoteRef: 13 | key: shelly-connection-string 14 | - secretKey: shelly-storage-account-name 15 | remoteRef: 16 | key: shelly-storage-account-name 17 | - secretKey: shelly-blob-sas 18 | remoteRef: 19 | key: shelly-blob-sas 20 | --- 21 | apiVersion: external-secrets.io/v1beta1 22 | kind: ExternalSecret 23 | metadata: 24 | name: shelly-db-creds 25 | namespace: shelly 26 | spec: 27 | refreshInterval: 1h 28 | secretStoreRef: 29 | name: azure-kv-store 30 | kind: ClusterSecretStore 31 | target: 32 | template: 33 | type: kubernetes.io/basic-auth 34 | data: 35 | - secretKey: username 36 | remoteRef: 37 | key: shelly-db-username 38 | - secretKey: password 39 | remoteRef: 40 | key: shelly-db-password 41 | -------------------------------------------------------------------------------- /databases/data/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - commafeed 5 | - health-api 6 | - home-iot 7 | - linkding 8 | - n8n 9 | - pgadmin 10 | - quantified-self 11 | - wallabag 12 | -------------------------------------------------------------------------------- /databases/data/linkding/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: linkding-db-production-cnpg-v1 5 | spec: 6 | description: Postgres cluster for the linkding application 7 | imageName: quay.io/enterprisedb/postgresql:16.1 8 | instances: 3 9 | 10 | inheritedMetadata: 11 | labels: 12 | app: linkding-database 13 | policy-type: "database" 14 | 15 | monitoring: 16 | enablePodMonitor: true 17 | 18 | storage: 19 | size: 1Gi 20 | 21 | bootstrap: 22 | recovery: 23 | source: clusterBackup 24 | database: linkding 25 | owner: linkding 26 | secret: 27 | name: linkding-db-creds 28 | 29 | externalClusters: 30 | - name: clusterBackup 31 | barmanObjectStore: 32 | destinationPath: "https://hldatabaseproduction.blob.core.windows.net/linkding" 33 | serverName: linkding-db-production-cnpg-v0 34 | azureCredentials: 35 | storageAccount: 36 | name: azure-creds 37 | key: linkding-storage-account-name 38 | storageSasToken: 39 | name: azure-creds 40 | key: linkding-blob-sas 41 | backup: 42 | barmanObjectStore: 43 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/linkding 44 | azureCredentials: 45 | storageAccount: 46 | name: azure-creds 47 | key: linkding-storage-account-name 48 | storageSasToken: 49 | name: azure-creds 50 | key: linkding-blob-sas 51 | wal: 52 | compression: gzip 53 | data: 54 | compression: gzip 55 | retentionPolicy: 14d 56 | 57 | # handy: resources can be used to trigger a redeploy 58 | resources: 59 | requests: 60 | memory: 320Mi 61 | 62 | managed: 63 | services: 64 | disabledDefaultServices: 65 | - ro 66 | - r 67 | additional: 68 | - selectorType: rw 69 | serviceTemplate: 70 | metadata: 71 | name: linkding-db-lb 72 | labels: 73 | cilium-lb-pool: postgres-pool 74 | annotations: 75 | external-dns.alpha.kubernetes.io/hostname: linkding-pg.mischavandenburg.net 76 | spec: 77 | type: LoadBalancer 78 | -------------------------------------------------------------------------------- /databases/data/linkding/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: linkding 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/linkding/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: health-api 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/linkding/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: linkding-db-production 5 | spec: 6 | immediate: true 7 | schedule: "0 0 3 * * *" 8 | backupOwnerReference: cluster 9 | cluster: 10 | name: linkding-db-production-cnpg-v1 11 | -------------------------------------------------------------------------------- /databases/data/linkding/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: azure-creds 5 | namespace: linkding 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: linkding-connection-string 13 | remoteRef: 14 | key: linkding-connection-string 15 | - secretKey: linkding-storage-account-name 16 | remoteRef: 17 | key: linkding-storage-account-name 18 | - secretKey: linkding-blob-sas 19 | remoteRef: 20 | key: linkding-blob-sas 21 | --- 22 | apiVersion: external-secrets.io/v1beta1 23 | kind: ExternalSecret 24 | metadata: 25 | name: linkding-db-creds 26 | namespace: linkding 27 | spec: 28 | refreshInterval: 1h 29 | secretStoreRef: 30 | name: azure-kv-store 31 | kind: ClusterSecretStore 32 | target: 33 | template: 34 | type: kubernetes.io/basic-auth 35 | data: 36 | - secretKey: username 37 | remoteRef: 38 | key: linkding-db-username 39 | - secretKey: password 40 | remoteRef: 41 | key: linkding-db-password 42 | -------------------------------------------------------------------------------- /databases/data/n8n/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: n8n-db-production-v1 5 | namespace: n8n 6 | spec: 7 | description: Postgres cluster for the n8n application 8 | 9 | imageName: quay.io/enterprisedb/postgresql:16.1 10 | inheritedMetadata: 11 | labels: 12 | app: n8n-database 13 | policy-type: database 14 | instances: 3 15 | monitoring: 16 | enablePodMonitor: true 17 | 18 | bootstrap: 19 | recovery: 20 | source: clusterBackup 21 | database: n8n 22 | owner: n8n 23 | secret: 24 | name: n8n-db-creds 25 | 26 | backup: 27 | barmanObjectStore: 28 | azureCredentials: 29 | storageAccount: 30 | key: n8n-storage-account-name 31 | name: azure-creds 32 | storageSasToken: 33 | key: n8n-blob-sas 34 | name: azure-creds 35 | data: 36 | compression: gzip 37 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/n8n 38 | wal: 39 | compression: gzip 40 | retentionPolicy: 14d 41 | 42 | externalClusters: 43 | - name: clusterBackup 44 | barmanObjectStore: 45 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/n8n 46 | serverName: n8n-db-production-v0 47 | azureCredentials: 48 | storageAccount: 49 | name: azure-creds 50 | key: n8n-storage-account-name 51 | storageSasToken: 52 | name: azure-creds 53 | key: n8n-blob-sas 54 | 55 | resources: 56 | requests: 57 | memory: 100Mi 58 | storage: 59 | size: 1Gi 60 | 61 | managed: 62 | services: 63 | disabledDefaultServices: 64 | - ro 65 | - r 66 | additional: 67 | - selectorType: rw 68 | serviceTemplate: 69 | metadata: 70 | name: n8n-db-lb 71 | labels: 72 | cilium-lb-pool: postgres-pool 73 | annotations: 74 | external-dns.alpha.kubernetes.io/hostname: n8n-db.mischavandenburg.net 75 | spec: 76 | type: LoadBalancer 77 | -------------------------------------------------------------------------------- /databases/data/n8n/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: n8n 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/n8n/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: n8n 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/n8n/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: n8n-db-production 5 | namespace: n8n 6 | spec: 7 | backupOwnerReference: cluster 8 | cluster: 9 | name: n8n-db-production-v1 10 | immediate: true 11 | schedule: 0 0 3 * * 12 | -------------------------------------------------------------------------------- /databases/data/n8n/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: azure-creds 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: n8n-connection-string 12 | remoteRef: 13 | key: n8n-connection-string 14 | - secretKey: n8n-storage-account-name 15 | remoteRef: 16 | key: n8n-storage-account-name 17 | - secretKey: n8n-blob-sas 18 | remoteRef: 19 | key: n8n-blob-sas 20 | --- 21 | apiVersion: external-secrets.io/v1beta1 22 | kind: ExternalSecret 23 | metadata: 24 | name: n8n-db-creds 25 | namespace: n8n 26 | spec: 27 | refreshInterval: 1h 28 | secretStoreRef: 29 | name: azure-kv-store 30 | kind: ClusterSecretStore 31 | target: 32 | template: 33 | type: kubernetes.io/basic-auth 34 | data: 35 | - secretKey: username 36 | remoteRef: 37 | key: n8n-db-username 38 | - secretKey: password 39 | remoteRef: 40 | key: n8n-db-password 41 | -------------------------------------------------------------------------------- /databases/data/pgadmin/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: pgadmin-configmap 5 | data: 6 | PGADMIN_LISTEN_PORT: "3004" 7 | -------------------------------------------------------------------------------- /databases/data/pgadmin/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: pgadmin 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: pgadmin 10 | template: 11 | metadata: 12 | labels: 13 | app: pgadmin 14 | policy-type: "app" 15 | spec: 16 | securityContext: 17 | runAsUser: 5050 18 | runAsGroup: 5050 19 | fsGroup: 5050 20 | 21 | containers: 22 | - name: pgadmin 23 | image: dpage/pgadmin4:9.1 24 | 25 | securityContext: 26 | allowPrivilegeEscalation: false 27 | 28 | ports: 29 | - containerPort: 3004 30 | protocol: TCP 31 | 32 | envFrom: 33 | - configMapRef: 34 | name: pgadmin-configmap 35 | - secretRef: 36 | name: pgadmin-container-env 37 | 38 | volumeMounts: 39 | - mountPath: /var/lib/pgadmin 40 | name: pgadmin-data 41 | 42 | restartPolicy: Always 43 | 44 | volumes: 45 | - name: pgadmin-data 46 | persistentVolumeClaim: 47 | claimName: pgadmin-data 48 | -------------------------------------------------------------------------------- /databases/data/pgadmin/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | cert-manager.io/cluster-issuer: letsencrypt-production 6 | name: pgadmin 7 | spec: 8 | ingressClassName: traefik 9 | rules: 10 | - host: pgadmin.mischavandenburg.net 11 | http: 12 | paths: 13 | - backend: 14 | service: 15 | name: pgadmin 16 | port: 17 | number: 3004 18 | path: / 19 | pathType: Prefix 20 | tls: 21 | - hosts: 22 | - pgadmin.mischavandenburg.net 23 | secretName: pgadmin-ingress-production 24 | -------------------------------------------------------------------------------- /databases/data/pgadmin/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: pgadmin 4 | resources: 5 | - namespace.yaml 6 | - configmap.yaml 7 | # - ingress.yaml 8 | - secrets.yaml 9 | - networking.yaml 10 | - storage.yaml 11 | - deployment.yaml 12 | -------------------------------------------------------------------------------- /databases/data/pgadmin/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: pgadmin 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/pgadmin/networking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pgadmin 5 | annotations: 6 | omni-kube-service-exposer.sidero.dev/port: "50083" 7 | omni-kube-service-exposer.sidero.dev/label: PgAdmin 8 | spec: 9 | ports: 10 | - port: 3004 11 | selector: 12 | app: pgadmin 13 | type: ClusterIP 14 | -------------------------------------------------------------------------------- /databases/data/pgadmin/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: pgadmin-container-env 5 | namespace: pgadmin 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: azure-kv-store 10 | kind: ClusterSecretStore 11 | data: 12 | - secretKey: PGADMIN_DEFAULT_EMAIL 13 | remoteRef: 14 | key: pgadmin-email 15 | - secretKey: PGADMIN_DEFAULT_PASSWORD 16 | remoteRef: 17 | key: pgadmin-password 18 | -------------------------------------------------------------------------------- /databases/data/pgadmin/storage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: pgadmin-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi 11 | -------------------------------------------------------------------------------- /databases/data/quantified-self/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: quantified-self-production-v0 5 | spec: 6 | description: Postgres cluster for tracking my obsessions 7 | imageName: quay.io/enterprisedb/postgresql:16.1 8 | instances: 3 9 | 10 | monitoring: 11 | enablePodMonitor: true 12 | 13 | inheritedMetadata: 14 | labels: 15 | app: zettelkasten-tracker-database 16 | policy-type: database 17 | 18 | storage: 19 | size: 1Gi 20 | 21 | bootstrap: 22 | recovery: 23 | source: clusterBackup 24 | database: zettelkasten-tracker 25 | owner: zktracker 26 | secret: 27 | name: zettelkasten-tracker-db-creds 28 | 29 | externalClusters: 30 | - name: clusterBackup 31 | barmanObjectStore: 32 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/zettelkasten-tracker 33 | serverName: zettelkasten-tracker-db-production-v3 34 | azureCredentials: 35 | storageAccount: 36 | name: azure-creds 37 | key: zettelkasten-tracker-storage-account-name 38 | storageSasToken: 39 | name: azure-creds 40 | key: zettelkasten-tracker-blob-sas 41 | 42 | backup: 43 | barmanObjectStore: 44 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/zettelkasten-tracker 45 | azureCredentials: 46 | storageAccount: 47 | name: azure-creds 48 | key: zettelkasten-tracker-storage-account-name 49 | storageSasToken: 50 | name: azure-creds 51 | key: zettelkasten-tracker-blob-sas 52 | wal: 53 | compression: gzip 54 | data: 55 | compression: gzip 56 | retentionPolicy: 7d 57 | 58 | # handy: resources can be used to trigger a redeploy 59 | resources: 60 | requests: 61 | memory: 201Mi 62 | cpu: 200m 63 | 64 | managed: 65 | services: 66 | disabledDefaultServices: 67 | - ro 68 | - r 69 | additional: 70 | - selectorType: rw 71 | serviceTemplate: 72 | metadata: 73 | name: quantified-self-db-lb 74 | labels: 75 | cilium-lb-pool: postgres-pool 76 | annotations: 77 | external-dns.alpha.kubernetes.io/hostname: quantified-self-db.mischavandenburg.net 78 | spec: 79 | type: LoadBalancer 80 | -------------------------------------------------------------------------------- /databases/data/quantified-self/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: quantified-self 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/quantified-self/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: quantified-self 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/quantified-self/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: quantified-self-production-v0 5 | spec: 6 | immediate: true 7 | schedule: "0 0 3 * * *" 8 | backupOwnerReference: cluster 9 | cluster: 10 | name: quantified-self-production-v0 11 | -------------------------------------------------------------------------------- /databases/data/quantified-self/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: azure-creds 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: zettelkasten-tracker-connection-string 12 | remoteRef: 13 | key: zettelkasten-tracker-connection-string 14 | - secretKey: zettelkasten-tracker-storage-account-name 15 | remoteRef: 16 | key: zettelkasten-tracker-storage-account-name 17 | - secretKey: zettelkasten-tracker-blob-sas 18 | remoteRef: 19 | key: zettelkasten-tracker-blob-sas 20 | --- 21 | apiVersion: external-secrets.io/v1beta1 22 | kind: ExternalSecret 23 | metadata: 24 | name: zettelkasten-tracker-db-creds 25 | namespace: zettelkasten-tracker 26 | spec: 27 | refreshInterval: 1h 28 | secretStoreRef: 29 | name: azure-kv-store 30 | kind: ClusterSecretStore 31 | target: 32 | template: 33 | type: kubernetes.io/basic-auth 34 | data: 35 | - secretKey: username 36 | remoteRef: 37 | key: zettelkasten-tracker-db-username 38 | - secretKey: password 39 | remoteRef: 40 | key: zettelkasten-tracker-db-password 41 | -------------------------------------------------------------------------------- /databases/data/wallabag/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: wallabag-db-production-cnpg-v1 5 | spec: 6 | description: Postgres cluster for the wallabag application 7 | imageName: quay.io/enterprisedb/postgresql:16.1 8 | instances: 3 9 | 10 | monitoring: 11 | enablePodMonitor: true 12 | 13 | inheritedMetadata: 14 | labels: 15 | app: wallabag-database 16 | policy-type: "database" 17 | 18 | storage: 19 | size: 2Gi 20 | 21 | bootstrap: 22 | recovery: 23 | source: clusterBackup 24 | database: wallabag 25 | owner: wallabag 26 | secret: 27 | name: wallabag-db-creds 28 | 29 | externalClusters: 30 | - name: clusterBackup 31 | barmanObjectStore: 32 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/wallabag 33 | serverName: wallabag-db-production-cnpg-v0 34 | azureCredentials: 35 | storageAccount: 36 | name: azure-creds 37 | key: wallabag-storage-account-name 38 | storageSasToken: 39 | name: azure-creds 40 | key: wallabag-blob-sas 41 | 42 | backup: 43 | barmanObjectStore: 44 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/wallabag 45 | azureCredentials: 46 | storageAccount: 47 | name: azure-creds 48 | key: wallabag-storage-account-name 49 | storageSasToken: 50 | name: azure-creds 51 | key: wallabag-blob-sas 52 | wal: 53 | compression: gzip 54 | data: 55 | compression: gzip 56 | retentionPolicy: 14d 57 | 58 | # handy: resources can be used to trigger a redeploy 59 | resources: 60 | requests: 61 | memory: 300Mi 62 | 63 | managed: 64 | services: 65 | disabledDefaultServices: 66 | - ro 67 | - r 68 | additional: 69 | - selectorType: rw 70 | serviceTemplate: 71 | metadata: 72 | name: home-iot-db-lb 73 | labels: 74 | cilium-lb-pool: postgres-pool 75 | annotations: 76 | external-dns.alpha.kubernetes.io/hostname: wallabag-db.mischavandenburg.net 77 | spec: 78 | type: LoadBalancer 79 | -------------------------------------------------------------------------------- /databases/data/wallabag/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: wallabag 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - database.yaml 8 | - scheduled-backup.yaml 9 | -------------------------------------------------------------------------------- /databases/data/wallabag/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: home-iot 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /databases/data/wallabag/scheduled-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: ScheduledBackup 3 | metadata: 4 | name: wallabag-db-production-cnpg 5 | namespace: wallabag 6 | spec: 7 | immediate: true 8 | schedule: "0 0 3 * * *" 9 | backupOwnerReference: cluster 10 | cluster: 11 | name: wallabag-db-production-cnpg-v1 12 | -------------------------------------------------------------------------------- /databases/data/wallabag/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: azure-creds 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: wallabag-connection-string 12 | remoteRef: 13 | key: wallabag-connection-string 14 | - secretKey: wallabag-storage-account-name 15 | remoteRef: 16 | key: wallabag-storage-account-name 17 | - secretKey: wallabag-blob-sas 18 | remoteRef: 19 | key: wallabag-blob-sas 20 | --- 21 | apiVersion: external-secrets.io/v1beta1 22 | kind: ExternalSecret 23 | metadata: 24 | name: wallabag-db-creds 25 | namespace: wallabag 26 | spec: 27 | refreshInterval: 1h 28 | secretStoreRef: 29 | name: azure-kv-store 30 | kind: ClusterSecretStore 31 | target: 32 | template: 33 | type: kubernetes.io/basic-auth 34 | data: 35 | - secretKey: username 36 | remoteRef: 37 | key: wallabag-db-user 38 | - secretKey: password 39 | remoteRef: 40 | key: wallabag-db-password 41 | -------------------------------------------------------------------------------- /images/homelab-feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mischavandenburg/homelab/59a7b52a215c35f8fcfbccaef1ed3d20a097086c/images/homelab-feedback.png -------------------------------------------------------------------------------- /infrastructure/configs/base/cert-manager/clusterissuer-production.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt-production 5 | spec: 6 | acme: 7 | # The ACME server URL 8 | server: https://acme-v02.api.letsencrypt.org/directory 9 | # Email address used for ACME registration 10 | email: mischa@mischavandenburg.nl 11 | # Name of a secret used to store the ACME account private key 12 | privateKeySecretRef: 13 | name: letsencrypt-production 14 | # Using the DNS01 challenge provider instead of HTTP-01 15 | solvers: 16 | - dns01: 17 | cloudflare: 18 | apiTokenSecretRef: 19 | name: cloudflare-api-token-secret 20 | key: api-token 21 | -------------------------------------------------------------------------------- /infrastructure/configs/base/cert-manager/clusterissuer-staging.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt-staging 5 | spec: 6 | acme: 7 | # The ACME server URL 8 | server: https://acme-staging-v02.api.letsencrypt.org/directory 9 | # Email address used for ACME registration 10 | email: mischa@mischavandenburg.nl 11 | # Name of a secret used to store the ACME account private key 12 | privateKeySecretRef: 13 | name: letsencrypt-staging 14 | # Using the DNS01 challenge provider instead of HTTP-01 15 | solvers: 16 | - dns01: 17 | cloudflare: 18 | apiTokenSecretRef: 19 | name: cloudflare-api-token-secret 20 | key: api-token 21 | -------------------------------------------------------------------------------- /infrastructure/configs/base/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - clusterissuer-staging.yaml 5 | - clusterissuer-production.yaml 6 | -------------------------------------------------------------------------------- /infrastructure/configs/base/external-secrets/cluster-secret-store.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ClusterSecretStore 3 | metadata: 4 | name: azure-kv-store 5 | spec: 6 | provider: 7 | azurekv: 8 | authType: ServicePrincipal 9 | vaultUrl: https://k8s-homelab-production.vault.azure.net 10 | tenantId: 6ddecc48-41b1-48de-bfde-2efd29fae9c7 11 | authSecretRef: 12 | clientId: 13 | name: azure-creds 14 | key: ClientID 15 | namespace: external-secrets 16 | clientSecret: 17 | name: azure-creds 18 | key: ClientSecret 19 | namespace: external-secrets 20 | -------------------------------------------------------------------------------- /infrastructure/configs/base/external-secrets/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - cluster-secret-store.yaml 5 | -------------------------------------------------------------------------------- /infrastructure/configs/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - external-secrets 5 | -------------------------------------------------------------------------------- /infrastructure/configs/data/cilium/ippool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumLoadBalancerIPPool 3 | metadata: 4 | name: postgres-pool 5 | namespace: kube-system 6 | spec: 7 | blocks: 8 | # 192.168.100.160 - 192.168.100.190 9 | - cidr: "192.168.100.160/27" 10 | serviceSelector: 11 | matchLabels: 12 | cilium-lb-pool: postgres-pool 13 | -------------------------------------------------------------------------------- /infrastructure/configs/data/cilium/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ippool.yaml 5 | - l2policy.yaml 6 | -------------------------------------------------------------------------------- /infrastructure/configs/data/cilium/l2policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumL2AnnouncementPolicy 3 | metadata: 4 | name: postgres-policy 5 | namespace: kube-system 6 | spec: 7 | serviceSelector: 8 | matchLabels: 9 | cilium-lb-pool: postgres-pool 10 | 11 | # IMPORTANT: if externalIPss is desired, externalIPs.enabled=true must be set in values.yaml 12 | # externalIPs: true 13 | loadBalancerIPs: true 14 | -------------------------------------------------------------------------------- /infrastructure/configs/data/external-secrets/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/external-secrets/ 5 | -------------------------------------------------------------------------------- /infrastructure/configs/data/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - cilium 5 | - external-secrets 6 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/cilium/apps-l2policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumL2AnnouncementPolicy 3 | metadata: 4 | name: apps-policy 5 | namespace: kube-system 6 | spec: 7 | serviceSelector: 8 | matchLabels: 9 | cilium-lb-pool: apps-pool 10 | 11 | # IMPORTANT: if externalIPss is desired, externalIPs.enabled=true must be set in values.yaml 12 | # externalIPs: true 13 | loadBalancerIPs: true 14 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/cilium/apps-pool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumLoadBalancerIPPool 3 | metadata: 4 | name: apps-pool 5 | namespace: kube-system 6 | spec: 7 | blocks: 8 | - start: "192.168.100.196" 9 | stop: "192.168.100.210" 10 | 11 | serviceSelector: 12 | matchLabels: 13 | cilium-lb-pool: apps-pool 14 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/cilium/ingress-l2policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumL2AnnouncementPolicy 3 | metadata: 4 | name: ingress-policy 5 | namespace: kube-system 6 | spec: 7 | serviceSelector: 8 | matchLabels: 9 | cilium.io/ingress: "true" 10 | 11 | # IMPORTANT: if externalIPss is desired, externalIPs.enabled=true must be set in values.yaml 12 | # externalIPs: true 13 | loadBalancerIPs: true 14 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/cilium/ingress-pool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumLoadBalancerIPPool 3 | metadata: 4 | name: ingress-pool 5 | namespace: kube-system 6 | spec: 7 | blocks: 8 | - start: "192.168.100.192" 9 | stop: "192.168.100.195" 10 | 11 | serviceSelector: 12 | matchLabels: 13 | cilium.io/ingress: "true" 14 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/cilium/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - apps-pool.yaml 5 | - ingress-pool.yaml 6 | - apps-l2policy.yaml 7 | - ingress-l2policy.yaml 8 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/external-secrets/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/external-secrets/ 5 | -------------------------------------------------------------------------------- /infrastructure/configs/jotunheim/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - cilium 5 | - external-secrets 6 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - repository.yaml 5 | - release.yaml 6 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cert-manager/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: cert-manager 5 | namespace: cert-manager 6 | spec: 7 | interval: 30m 8 | chart: 9 | spec: 10 | chart: cert-manager 11 | version: "1.*" 12 | sourceRef: 13 | kind: HelmRepository 14 | name: jetstack 15 | namespace: cert-manager 16 | interval: 12h 17 | values: 18 | installCRDs: true 19 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cert-manager/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: jetstack 5 | namespace: cert-manager 6 | spec: 7 | interval: 24h 8 | url: https://charts.jetstack.io 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cloudnativepg/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - repository.yaml 5 | - release.yaml 6 | - namespace.yaml 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cloudnativepg/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cnpg-system 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cloudnativepg/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: cnpg 5 | namespace: cnpg-system 6 | spec: 7 | interval: 30m 8 | chart: 9 | spec: 10 | chart: cloudnative-pg 11 | version: "0.23.0" 12 | sourceRef: 13 | kind: HelmRepository 14 | name: cnpg 15 | namespace: cnpg-system 16 | interval: 12h 17 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/cloudnativepg/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: cnpg 5 | namespace: cnpg-system 6 | spec: 7 | interval: 24h 8 | url: https://cloudnative-pg.github.io/charts/ 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: external-dns 4 | resources: 5 | - namespace.yaml 6 | - repository.yaml 7 | - release.yaml 8 | - secret.yaml 9 | 10 | configMapGenerator: 11 | - name: external-dns-values 12 | files: 13 | - values.yaml=values.yaml 14 | 15 | configurations: 16 | - kustomizeconfig.yaml 17 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | nameReference: 2 | - kind: ConfigMap 3 | version: v1 4 | fieldSpecs: 5 | - path: spec/valuesFrom/name 6 | kind: HelmRelease 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: external-dns 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: external-dns 5 | spec: 6 | interval: 1h 7 | chart: 8 | spec: 9 | chart: external-dns 10 | version: "v1.15.0" 11 | sourceRef: 12 | kind: HelmRepository 13 | name: external-dns 14 | namespace: external-dns 15 | 16 | valuesFrom: 17 | - kind: ConfigMap 18 | name: external-dns-values 19 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: external-dns 5 | spec: 6 | interval: 1h 7 | url: https://kubernetes-sigs.github.io/external-dns/ 8 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: cloudflare-dns-api-token 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: cloudflare-dns-api-token 12 | remoteRef: 13 | key: cloudflare-dns-api-token 14 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-dns/values.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | name: cloudflare 3 | 4 | policy: upsert-only 5 | 6 | env: 7 | - name: CF_API_TOKEN 8 | valueFrom: 9 | secretKeyRef: 10 | name: cloudflare-dns-api-token 11 | key: cloudflare-dns-api-token 12 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-secrets/deployment-crds.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: external-secrets-crds 5 | namespace: flux-system 6 | spec: 7 | interval: 10m 8 | path: ./deploy/crds 9 | prune: true 10 | sourceRef: 11 | kind: GitRepository 12 | name: external-secrets 13 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-secrets/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - namespace.yaml 5 | - repositories.yaml 6 | - deployment-crds.yaml 7 | - release.yaml 8 | # - cluster-secret-store.yaml 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-secrets/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: external-secrets 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-secrets/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: external-secrets 5 | namespace: flux-system 6 | spec: 7 | releaseName: external-secrets 8 | targetNamespace: external-secrets 9 | interval: 10m 10 | chart: 11 | spec: 12 | chart: external-secrets 13 | version: 0.13.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: external-secrets 17 | namespace: flux-system 18 | values: 19 | installCRDs: false 20 | 21 | install: 22 | createNamespace: true 23 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/external-secrets/repositories.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: external-secrets 5 | namespace: flux-system 6 | spec: 7 | interval: 10m 8 | url: https://charts.external-secrets.io 9 | --- 10 | apiVersion: source.toolkit.fluxcd.io/v1 11 | kind: GitRepository 12 | metadata: 13 | name: external-secrets 14 | namespace: flux-system 15 | spec: 16 | interval: 10m 17 | ref: 18 | tag: v0.13.0 19 | url: https://github.com/external-secrets/external-secrets 20 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/local-path/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - github.com/rancher/local-path-provisioner/deploy?ref=v0.0.31 5 | patches: 6 | - patch: |- 7 | kind: ConfigMap 8 | apiVersion: v1 9 | metadata: 10 | name: local-path-config 11 | namespace: local-path-storage 12 | data: 13 | config.json: |- 14 | { 15 | "nodePathMap":[ 16 | { 17 | "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES", 18 | "paths":["/var/local-path-provisioner"] 19 | } 20 | ] 21 | } 22 | 23 | - patch: |- 24 | apiVersion: storage.k8s.io/v1 25 | kind: StorageClass 26 | metadata: 27 | name: local-path 28 | annotations: 29 | storageclass.kubernetes.io/is-default-class: "true" 30 | 31 | - patch: |- 32 | apiVersion: v1 33 | kind: Namespace 34 | metadata: 35 | name: local-path-storage 36 | labels: 37 | pod-security.kubernetes.io/enforce: privileged 38 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/renovate/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: renovate-configmap 5 | namespace: renovate 6 | data: 7 | RENOVATE_AUTODISCOVER: "false" 8 | RENOVATE_GIT_AUTHOR: "Renovate Bot " 9 | RENOVATE_PLATFORM: "github" 10 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/renovate/cronjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: renovate 5 | namespace: renovate 6 | spec: 7 | schedule: "@hourly" 8 | successfulJobsHistoryLimit: 2 9 | failedJobsHistoryLimit: 2 10 | concurrencyPolicy: Forbid 11 | jobTemplate: 12 | spec: 13 | template: 14 | spec: 15 | containers: 16 | - name: renovate 17 | image: renovate/renovate:latest 18 | args: 19 | - mischavandenburg/homelab 20 | 21 | envFrom: 22 | - secretRef: 23 | name: renovate-container-env 24 | - configMapRef: 25 | name: renovate-configmap 26 | 27 | restartPolicy: Never 28 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/renovate/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - namespace.yaml 5 | - secrets.yaml 6 | - configmap.yaml 7 | - cronjob.yaml 8 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/renovate/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: renovate 5 | labels: 6 | app.kubernetes.io/component: monitoring 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/renovate/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: renovate-container-env 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: RENOVATE_TOKEN 12 | remoteRef: 13 | key: renovate-token 14 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/synology-csi/csi-driver.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1 2 | kind: CSIDriver 3 | metadata: 4 | name: csi.san.synology.com 5 | spec: 6 | attachRequired: true # Indicates the driver requires an attach operation (TODO: ControllerPublishVolume should be implemented) 7 | podInfoOnMount: true 8 | volumeLifecycleModes: 9 | - Persistent 10 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/synology-csi/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - controller.yaml 5 | - csi-driver.yaml 6 | - node.yaml 7 | - storage-class.yaml 8 | - storage-class-retain.yaml 9 | - storage-class-smb.yaml 10 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/synology-csi/node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: csi-node-sa 5 | namespace: synology-csi 6 | 7 | --- 8 | kind: ClusterRole 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | metadata: 11 | name: synology-csi-node-role 12 | rules: 13 | - apiGroups: [""] 14 | resources: ["secrets"] 15 | verbs: ["get", "list"] 16 | - apiGroups: [""] 17 | resources: ["nodes"] 18 | verbs: ["get", "list", "update"] 19 | - apiGroups: [""] 20 | resources: ["namespaces"] 21 | verbs: ["get", "list"] 22 | - apiGroups: [""] 23 | resources: ["persistentvolumes"] 24 | verbs: ["get", "list", "watch", "update"] 25 | - apiGroups: ["storage.k8s.io"] 26 | resources: ["volumeattachments"] 27 | verbs: ["get", "list", "watch", "update"] 28 | 29 | --- 30 | kind: ClusterRoleBinding 31 | apiVersion: rbac.authorization.k8s.io/v1 32 | metadata: 33 | name: synology-csi-node-role 34 | namespace: synology-csi 35 | subjects: 36 | - kind: ServiceAccount 37 | name: csi-node-sa 38 | namespace: synology-csi 39 | roleRef: 40 | kind: ClusterRole 41 | name: synology-csi-node-role 42 | apiGroup: rbac.authorization.k8s.io 43 | 44 | --- 45 | kind: DaemonSet 46 | apiVersion: apps/v1 47 | metadata: 48 | name: synology-csi-node 49 | namespace: synology-csi 50 | spec: 51 | selector: 52 | matchLabels: 53 | app: synology-csi-node 54 | template: 55 | metadata: 56 | labels: 57 | app: synology-csi-node 58 | spec: 59 | hostPID: true 60 | serviceAccount: csi-node-sa 61 | hostNetwork: true 62 | containers: 63 | - name: csi-driver-registrar 64 | securityContext: 65 | privileged: true 66 | imagePullPolicy: Always 67 | image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.13.0 68 | args: 69 | - --v=5 70 | - --csi-address=$(ADDRESS) # the csi socket path inside the pod 71 | - --kubelet-registration-path=$(REGISTRATION_PATH) # the csi socket path on the host node 72 | env: 73 | - name: ADDRESS 74 | value: /csi/csi.sock 75 | - name: REGISTRATION_PATH 76 | value: /var/lib/kubelet/plugins/csi.san.synology.com/csi.sock 77 | - name: KUBE_NODE_NAME 78 | valueFrom: 79 | fieldRef: 80 | fieldPath: spec.nodeName 81 | volumeMounts: 82 | - name: plugin-dir 83 | mountPath: /csi 84 | - name: registration-dir 85 | mountPath: /registration 86 | - name: csi-plugin 87 | securityContext: 88 | privileged: true 89 | imagePullPolicy: IfNotPresent 90 | image: ghcr.io/mischavandenburg/synology-csi:v1.1.4 91 | args: 92 | - --nodeid=$(KUBE_NODE_NAME) 93 | - --endpoint=$(CSI_ENDPOINT) 94 | - --client-info 95 | - /etc/synology/client-info.yml 96 | - --log-level=info 97 | env: 98 | - name: CSI_ENDPOINT 99 | value: unix://csi/csi.sock 100 | - name: KUBE_NODE_NAME 101 | valueFrom: 102 | fieldRef: 103 | fieldPath: spec.nodeName 104 | volumeMounts: 105 | - name: kubelet-dir 106 | mountPath: /var/lib/kubelet 107 | mountPropagation: "Bidirectional" 108 | - name: plugin-dir 109 | mountPath: /csi 110 | - name: client-info 111 | mountPath: /etc/synology 112 | readOnly: true 113 | - name: host-root 114 | mountPath: /host 115 | - name: device-dir 116 | mountPath: /dev 117 | 118 | imagePullSecrets: 119 | - name: ghcr-login-secret 120 | 121 | volumes: 122 | - name: kubelet-dir 123 | hostPath: 124 | path: /var/lib/kubelet 125 | type: Directory 126 | - name: plugin-dir 127 | hostPath: 128 | path: /var/lib/kubelet/plugins/csi.san.synology.com/ 129 | type: DirectoryOrCreate 130 | - name: registration-dir 131 | hostPath: 132 | path: /var/lib/kubelet/plugins_registry 133 | type: Directory 134 | - name: client-info 135 | secret: 136 | secretName: client-info-secret 137 | - name: host-root 138 | hostPath: 139 | path: / 140 | type: Directory 141 | - name: device-dir 142 | hostPath: 143 | path: /dev 144 | type: Directory 145 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/synology-csi/storage-class-retain.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1 2 | kind: StorageClass 3 | metadata: 4 | name: synology-iscsi-retain 5 | annotations: 6 | provisioner: csi.san.synology.com 7 | parameters: 8 | dsm: "192.168.120.186" 9 | location: "/volume1" 10 | protocol: iscsi 11 | csi.storage.k8s.io/fstype: ext4 12 | reclaimPolicy: Retain 13 | allowVolumeExpansion: true 14 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/synology-csi/storage-class-smb.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1 2 | kind: StorageClass 3 | metadata: 4 | name: synology-smb-retain 5 | annotations: 6 | provisioner: csi.san.synology.com 7 | parameters: 8 | dsm: "192.168.120.186" 9 | location: "/volume1" 10 | csi.storage.k8s.io/node-stage-secret-name: cifs-csi-credentials 11 | csi.storage.k8s.io/node-stage-secret-namespace: synology-csi 12 | protocol: smb 13 | reclaimPolicy: Retain 14 | allowVolumeExpansion: true 15 | -------------------------------------------------------------------------------- /infrastructure/controllers/base/synology-csi/storage-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1 2 | kind: StorageClass 3 | metadata: 4 | name: synology-iscsi 5 | annotations: 6 | storageclass.kubernetes.io/is-default-class: "true" 7 | provisioner: csi.san.synology.com 8 | parameters: 9 | dsm: "192.168.120.186" 10 | location: "/volume1" 11 | protocol: iscsi 12 | csi.storage.k8s.io/fstype: ext4 13 | reclaimPolicy: Delete 14 | allowVolumeExpansion: true 15 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/cilium/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: kube-system 4 | resources: 5 | - release.yaml 6 | - repository.yaml 7 | 8 | configMapGenerator: 9 | - name: cilium-values 10 | files: 11 | - values.yaml=values.yaml 12 | 13 | configurations: 14 | - kustomizeconfig.yaml 15 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/cilium/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | nameReference: 2 | - kind: ConfigMap 3 | version: v1 4 | fieldSpecs: 5 | - path: spec/valuesFrom/name 6 | kind: HelmRelease 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/cilium/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: cilium 5 | namespace: kube-system 6 | 7 | spec: 8 | releaseName: cilium 9 | interval: 15m 10 | chart: 11 | spec: 12 | chart: cilium 13 | version: 1.16.6 14 | sourceRef: 15 | kind: HelmRepository 16 | name: cilium 17 | namespace: kube-system 18 | 19 | valuesFrom: 20 | - kind: ConfigMap 21 | name: cilium-values 22 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/cilium/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: cilium 5 | namespace: kube-system 6 | spec: 7 | interval: 1h 8 | url: https://helm.cilium.io/ 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/cilium/values.yaml: -------------------------------------------------------------------------------- 1 | ipam: 2 | mode: kubernetes 3 | 4 | kubeProxyReplacement: true 5 | 6 | securityContext: 7 | capabilities: 8 | ciliumAgent: 9 | - CHOWN 10 | - KILL 11 | - NET_ADMIN 12 | - NET_RAW 13 | - IPC_LOCK 14 | - SYS_ADMIN 15 | - SYS_RESOURCE 16 | - DAC_OVERRIDE 17 | - FOWNER 18 | - SETGID 19 | - SETUID 20 | cleanCiliumState: 21 | - NET_ADMIN 22 | - SYS_ADMIN 23 | - SYS_RESOURCE 24 | 25 | cgroup: 26 | autoMount: 27 | enabled: false 28 | hostRoot: /sys/fs/cgroup 29 | 30 | k8sServiceHost: localhost 31 | k8sServicePort: 7445 32 | 33 | hubble: 34 | relay: 35 | enabled: true 36 | ui: 37 | enabled: true 38 | service: 39 | annotations: 40 | omni-kube-service-exposer.sidero.dev/port: "50080" 41 | omni-kube-service-exposer.sidero.dev/label: Hubble 42 | 43 | l2announcements: 44 | enabled: true 45 | 46 | # Calculated based on 30 services 47 | # https://docs.cilium.io/en/latest/network/l2-announcements/#sizing-client-rate-limit 48 | 49 | k8sClientRateLimit: 50 | qps: 15 51 | burst: 20 52 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/cloudnativepg/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: cnpg-system 4 | resources: 5 | - ../../base/cloudnativepg/ 6 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/external-dns/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/external-dns 5 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/external-secrets/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/external-secrets/ 5 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - cilium 5 | - cloudnativepg 6 | - external-dns 7 | - external-secrets 8 | - local-path 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/data/local-path/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/local-path 5 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/cilium/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: kube-system 4 | resources: 5 | - release.yaml 6 | - repository.yaml 7 | 8 | configMapGenerator: 9 | - name: cilium-values 10 | files: 11 | - values.yaml=values.yaml 12 | 13 | configurations: 14 | - kustomizeconfig.yaml 15 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/cilium/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | nameReference: 2 | - kind: ConfigMap 3 | version: v1 4 | fieldSpecs: 5 | - path: spec/valuesFrom/name 6 | kind: HelmRelease 7 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/cilium/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: cilium 5 | namespace: kube-system 6 | 7 | spec: 8 | releaseName: cilium 9 | interval: 15m 10 | chart: 11 | spec: 12 | chart: cilium 13 | version: 1.16.6 14 | sourceRef: 15 | kind: HelmRepository 16 | name: cilium 17 | namespace: kube-system 18 | 19 | valuesFrom: 20 | - kind: ConfigMap 21 | name: cilium-values 22 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/cilium/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: cilium 5 | namespace: kube-system 6 | spec: 7 | interval: 1h 8 | url: https://helm.cilium.io/ 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/cilium/values.yaml: -------------------------------------------------------------------------------- 1 | ipam: 2 | mode: kubernetes 3 | 4 | kubeProxyReplacement: true 5 | 6 | securityContext: 7 | capabilities: 8 | ciliumAgent: 9 | - CHOWN 10 | - KILL 11 | - NET_ADMIN 12 | - NET_RAW 13 | - IPC_LOCK 14 | - SYS_ADMIN 15 | - SYS_RESOURCE 16 | - DAC_OVERRIDE 17 | - FOWNER 18 | - SETGID 19 | - SETUID 20 | cleanCiliumState: 21 | - NET_ADMIN 22 | - SYS_ADMIN 23 | - SYS_RESOURCE 24 | 25 | cgroup: 26 | autoMount: 27 | enabled: false 28 | hostRoot: /sys/fs/cgroup 29 | 30 | k8sServiceHost: localhost 31 | k8sServicePort: 7445 32 | 33 | hubble: 34 | relay: 35 | enabled: true 36 | ui: 37 | enabled: true 38 | service: 39 | annotations: 40 | omni-kube-service-exposer.sidero.dev/port: "50080" 41 | omni-kube-service-exposer.sidero.dev/label: Hubble 42 | 43 | l2announcements: 44 | enabled: true 45 | 46 | # Calculated based on 30 services 47 | # https://docs.cilium.io/en/latest/network/l2-announcements/#sizing-client-rate-limit 48 | 49 | k8sClientRateLimit: 50 | qps: 15 51 | burst: 20 52 | 53 | ingressController: 54 | enabled: true 55 | loadbalancerMode: shared 56 | default: true 57 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/external-dns/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/external-dns 5 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/external-secrets/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/external-secrets/ 5 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - cilium 5 | - external-dns 6 | - external-secrets 7 | - local-path 8 | - renovate 9 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/local-path/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/local-path 5 | -------------------------------------------------------------------------------- /infrastructure/controllers/jotunheim/renovate/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: renovate 4 | resources: 5 | - ../../base/renovate/ 6 | -------------------------------------------------------------------------------- /monitoring/configs/base/dashboards/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: monitoring 4 | configMapGenerator: 5 | - name: default-dashboards 6 | files: 7 | - standard-dashboards/alertmanager-overview.json 8 | - standard-dashboards/apiserver.json 9 | - standard-dashboards/cluster-total.json 10 | - standard-dashboards/controller-manager.json 11 | - standard-dashboards/etcd.json 12 | - standard-dashboards/grafana-overview.json 13 | - standard-dashboards/k8s-coredns.json 14 | - standard-dashboards/k8s-resources-cluster.json 15 | - standard-dashboards/k8s-resources-multicluster.json 16 | - standard-dashboards/k8s-resources-namespace.json 17 | - standard-dashboards/k8s-resources-node.json 18 | - standard-dashboards/k8s-resources-pod.json 19 | - standard-dashboards/k8s-resources-workload.json 20 | - standard-dashboards/k8s-resources-workloads-namespace.json 21 | - standard-dashboards/kubelet.json 22 | - standard-dashboards/namespace-by-pod.json 23 | - standard-dashboards/namespace-by-workload.json 24 | - standard-dashboards/node-cluster-rsrc-use.json 25 | - standard-dashboards/node-rsrc-use.json 26 | - standard-dashboards/nodes-darwin.json 27 | - standard-dashboards/nodes.json 28 | - standard-dashboards/persistentvolumesusage.json 29 | - standard-dashboards/pod-total.json 30 | - standard-dashboards/prometheus.json 31 | - standard-dashboards/proxy.json 32 | - standard-dashboards/scheduler.json 33 | - standard-dashboards/workload-total.json 34 | options: 35 | labels: 36 | grafana_dashboard: "1" 37 | - name: extra-dashboards 38 | files: 39 | - extra-dashboards/edb-postgres.json # https://github.com/EnterpriseDB/docs/blob/main/product_docs/docs/postgres_for_kubernetes/1/samples/monitoring/grafana-dashboard.json 40 | # - extra-dashboards/node-exporter-full.json 41 | - extra-dashboards/node-exporter-nodename.json 42 | - extra-dashboards/cloudflared.json 43 | options: 44 | labels: 45 | grafana_dashboard: "1" 46 | - name: personal-dashboards 47 | files: 48 | - personal-dashboards/running-this-week.json 49 | - personal-dashboards/running-this-year.json 50 | 51 | # keeping this unprovisioned for now 52 | # - personal-dashboards/zettelkasten-count.json 53 | options: 54 | labels: 55 | grafana_dashboard: "1" 56 | -------------------------------------------------------------------------------- /monitoring/configs/base/dashboards/personal-dashboards/zettelkasten-count.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "type": "dashboard" 15 | } 16 | ] 17 | }, 18 | "editable": true, 19 | "fiscalYearStartMonth": 0, 20 | "graphTooltip": 0, 21 | "id": 33, 22 | "links": [], 23 | "panels": [ 24 | { 25 | "datasource": { 26 | "type": "grafana-postgresql-datasource", 27 | "uid": "bdkaqflt5vdogb" 28 | }, 29 | "fieldConfig": { 30 | "defaults": { 31 | "color": { 32 | "mode": "palette-classic" 33 | }, 34 | "custom": { 35 | "axisBorderShow": false, 36 | "axisCenteredZero": false, 37 | "axisColorMode": "text", 38 | "axisLabel": "", 39 | "axisPlacement": "auto", 40 | "barAlignment": 0, 41 | "drawStyle": "line", 42 | "fillOpacity": 0, 43 | "gradientMode": "none", 44 | "hideFrom": { 45 | "legend": false, 46 | "tooltip": false, 47 | "viz": false 48 | }, 49 | "insertNulls": false, 50 | "lineInterpolation": "linear", 51 | "lineWidth": 1, 52 | "pointSize": 5, 53 | "scaleDistribution": { 54 | "type": "linear" 55 | }, 56 | "showPoints": "auto", 57 | "spanNulls": false, 58 | "stacking": { 59 | "group": "A", 60 | "mode": "none" 61 | }, 62 | "thresholdsStyle": { 63 | "mode": "off" 64 | } 65 | }, 66 | "mappings": [], 67 | "thresholds": { 68 | "mode": "absolute", 69 | "steps": [ 70 | { 71 | "color": "green", 72 | "value": null 73 | }, 74 | { 75 | "color": "red", 76 | "value": 80 77 | } 78 | ] 79 | } 80 | }, 81 | "overrides": [] 82 | }, 83 | "gridPos": { 84 | "h": 8, 85 | "w": 12, 86 | "x": 0, 87 | "y": 0 88 | }, 89 | "id": 1, 90 | "options": { 91 | "legend": { 92 | "calcs": [], 93 | "displayMode": "list", 94 | "placement": "bottom", 95 | "showLegend": true 96 | }, 97 | "tooltip": { 98 | "mode": "single", 99 | "sort": "none" 100 | } 101 | }, 102 | "pluginVersion": "10.4.1", 103 | "targets": [ 104 | { 105 | "datasource": { 106 | "type": "grafana-postgresql-datasource", 107 | "uid": "bdkaqflt5vdogb" 108 | }, 109 | "editorMode": "code", 110 | "format": "time_series", 111 | "key": "Q-764060a2-b711-4170-8ecd-4432656108f8-0", 112 | "rawQuery": true, 113 | "rawSql": "SELECT count, \"timestamp\" AS \"time\" FROM markdown_counts", 114 | "refId": "A", 115 | "sql": { 116 | "columns": [ 117 | { 118 | "parameters": [ 119 | { 120 | "name": "count", 121 | "type": "functionParameter" 122 | } 123 | ], 124 | "type": "function" 125 | }, 126 | { 127 | "alias": "\"time\"", 128 | "parameters": [ 129 | { 130 | "name": "\"timestamp\"", 131 | "type": "functionParameter" 132 | } 133 | ], 134 | "type": "function" 135 | } 136 | ], 137 | "groupBy": [], 138 | "limit": 50 139 | }, 140 | "table": "markdown_counts" 141 | } 142 | ], 143 | "title": "Note Count", 144 | "type": "timeseries" 145 | } 146 | ], 147 | "schemaVersion": 39, 148 | "tags": [], 149 | "templating": { 150 | "list": [] 151 | }, 152 | "time": { 153 | "from": "now-90d", 154 | "to": "now" 155 | }, 156 | "timepicker": {}, 157 | "timezone": "browser", 158 | "title": "Zettelkasten Tracker", 159 | "uid": "ddkaqtaxm8t8ga", 160 | "version": 5, 161 | "weekStart": "" 162 | } 163 | -------------------------------------------------------------------------------- /monitoring/configs/base/grafana/datasources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: grafana-datasources 5 | namespace: monitoring 6 | labels: 7 | grafana_datasource: "1" # default value for: sidecar.datasources.label 8 | data: 9 | datasource.yaml: |- 10 | apiVersion: 1 11 | datasources: 12 | - name: Prometheus 13 | type: prometheus 14 | uid: prometheus 15 | url: http://kube-prometheus-stack-prometheus.monitoring:9090/ 16 | # access: proxy 17 | isDefault: true 18 | jsonData: 19 | httpMethod: POST 20 | timeInterval: 30s 21 | - name: Alertmanager 22 | type: alertmanager 23 | uid: alertmanager 24 | url: http://prometheus-stack-kube-prom-alertmanager.monitoring:9093/ 25 | access: proxy 26 | jsonData: 27 | handleGrafanaManagedAlerts: false 28 | implementation: prometheus 29 | - name: Loki 30 | type: loki 31 | uid: loki 32 | url: http://loki.monitoring:3100/ 33 | -------------------------------------------------------------------------------- /monitoring/configs/base/grafana/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | # - loadbalancer.yaml 5 | - datasources.yaml 6 | -------------------------------------------------------------------------------- /monitoring/configs/base/grafana/loadbalancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: grafana-loadbalancer 5 | namespace: monitoring 6 | spec: 7 | ports: 8 | - port: 3000 9 | protocol: TCP 10 | targetPort: 3000 11 | selector: 12 | app.kubernetes.io/instance: kube-prometheus-stack 13 | app.kubernetes.io/name: grafana 14 | type: LoadBalancer 15 | -------------------------------------------------------------------------------- /monitoring/configs/base/prometheus/cloudflare.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: PodMonitor 3 | metadata: 4 | name: cloudflare-tunnel 5 | namespace: linkding 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: cloudflared 10 | podMetricsEndpoints: 11 | - port: http-metrics 12 | -------------------------------------------------------------------------------- /monitoring/configs/base/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - cloudflare.yaml 5 | -------------------------------------------------------------------------------- /monitoring/configs/base/prometheus/postgresql-operator-default-alerts.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: PrometheusRule 3 | metadata: 4 | name: postgresql-operator-default-alerts 5 | namespace: monitoring 6 | spec: 7 | groups: 8 | - name: cnp-default.rules 9 | rules: 10 | - alert: LongRunningTransaction 11 | annotations: 12 | description: Pod {{ $labels.pod }} is taking more than 5 minutes (300 seconds) for a query. 13 | summary: A query is taking longer than 5 minutes. 14 | expr: |- 15 | cnp_backends_max_tx_duration_seconds > 300 16 | for: 1m 17 | labels: 18 | severity: warning 19 | - alert: BackendsWaiting 20 | annotations: 21 | description: Pod {{ $labels.pod }} has been waiting for longer than 5 minutes 22 | summary: If a backend is waiting for longer than 5 minutes 23 | expr: |- 24 | cnp_backends_waiting_total > 300 25 | for: 1m 26 | labels: 27 | severity: warning 28 | - alert: PGDatabase 29 | annotations: 30 | description: Over 150,000,000 transactions from frozen xid on pod {{ $labels.pod }} 31 | summary: Number of transactions from the frozen XID to the current one 32 | expr: |- 33 | cnp_pg_database_xid_age > 150000000 34 | for: 1m 35 | labels: 36 | severity: warning 37 | - alert: PGReplication 38 | annotations: 39 | description: Standby is lagging behind by over 300 seconds (5 minutes) 40 | summary: The standby is lagging behind the primary 41 | expr: |- 42 | cnp_pg_replication_lag > 300 43 | for: 1m 44 | labels: 45 | severity: warning 46 | - alert: LastFailedArchiveTime 47 | annotations: 48 | description: Archiving failed for {{ $labels.pod }} 49 | summary: Checks the last time archiving failed. Will be < 0 when it has not failed. 50 | expr: |- 51 | (cnp_pg_stat_archiver_last_failed_time - cnp_pg_stat_archiver_last_archived_time) > 1 52 | for: 1m 53 | labels: 54 | severity: warning 55 | telegram: "true" 56 | - alert: DatabaseDeadlockConflicts 57 | annotations: 58 | description: There are over 10 deadlock conflicts in {{ $labels.pod }} 59 | summary: Checks the number of database conflicts 60 | expr: |- 61 | cnp_pg_stat_database_deadlocks > 10 62 | for: 1m 63 | labels: 64 | severity: warning 65 | - alert: ReplicaFailingReplication 66 | annotations: 67 | description: Replica {{ $labels.pod }} is failing to replicate 68 | summary: Checks if the replica is failing to replicate 69 | expr: |- 70 | cnp_pg_replication_in_recovery > cnp_pg_replication_is_wal_receiver_up 71 | for: 1m 72 | labels: 73 | severity: warning 74 | -------------------------------------------------------------------------------- /monitoring/configs/jotunheim/grafana/dashboards/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: monitoring 4 | configMapGenerator: 5 | - name: personal-dashboards 6 | files: 7 | - quantified-self/zettelkasten.json 8 | - quantified-self/health.json 9 | - quantified-self/sleep.json 10 | options: 11 | labels: 12 | grafana_dashboard: "1" 13 | 14 | - name: home-iot-dashboards 15 | files: 16 | - home-iot/home-iot.json 17 | options: 18 | labels: 19 | grafana_dashboard: "1" 20 | -------------------------------------------------------------------------------- /monitoring/configs/jotunheim/grafana/dashboards/quantified-self/zettelkasten.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "type": "dashboard" 15 | } 16 | ] 17 | }, 18 | "editable": true, 19 | "fiscalYearStartMonth": 0, 20 | "graphTooltip": 0, 21 | "id": 33, 22 | "links": [], 23 | "panels": [ 24 | { 25 | "datasource": { 26 | "type": "grafana-postgresql-datasource", 27 | "uid": "quantified-self-db" 28 | }, 29 | "fieldConfig": { 30 | "defaults": { 31 | "color": { 32 | "mode": "palette-classic" 33 | }, 34 | "custom": { 35 | "axisBorderShow": false, 36 | "axisCenteredZero": false, 37 | "axisColorMode": "text", 38 | "axisLabel": "", 39 | "axisPlacement": "auto", 40 | "barAlignment": 0, 41 | "drawStyle": "line", 42 | "fillOpacity": 0, 43 | "gradientMode": "none", 44 | "hideFrom": { 45 | "legend": false, 46 | "tooltip": false, 47 | "viz": false 48 | }, 49 | "insertNulls": false, 50 | "lineInterpolation": "linear", 51 | "lineWidth": 1, 52 | "pointSize": 5, 53 | "scaleDistribution": { 54 | "type": "linear" 55 | }, 56 | "showPoints": "auto", 57 | "spanNulls": false, 58 | "stacking": { 59 | "group": "A", 60 | "mode": "none" 61 | }, 62 | "thresholdsStyle": { 63 | "mode": "off" 64 | } 65 | }, 66 | "mappings": [], 67 | "thresholds": { 68 | "mode": "absolute", 69 | "steps": [ 70 | { 71 | "color": "green", 72 | "value": null 73 | }, 74 | { 75 | "color": "red", 76 | "value": 80 77 | } 78 | ] 79 | } 80 | }, 81 | "overrides": [] 82 | }, 83 | "gridPos": { 84 | "h": 8, 85 | "w": 12, 86 | "x": 0, 87 | "y": 0 88 | }, 89 | "id": 1, 90 | "options": { 91 | "legend": { 92 | "calcs": [], 93 | "displayMode": "list", 94 | "placement": "bottom", 95 | "showLegend": true 96 | }, 97 | "tooltip": { 98 | "mode": "single", 99 | "sort": "none" 100 | } 101 | }, 102 | "pluginVersion": "10.4.1", 103 | "targets": [ 104 | { 105 | "datasource": { 106 | "type": "grafana-postgresql-datasource", 107 | "uid": "quantified-self-db" 108 | }, 109 | "editorMode": "code", 110 | "format": "time_series", 111 | "key": "Q-764060a2-b711-4170-8ecd-4432656108f8-0", 112 | "rawQuery": true, 113 | "rawSql": "SELECT count, \"timestamp\" AS \"time\" FROM markdown_counts", 114 | "refId": "A", 115 | "sql": { 116 | "columns": [ 117 | { 118 | "parameters": [ 119 | { 120 | "name": "count", 121 | "type": "functionParameter" 122 | } 123 | ], 124 | "type": "function" 125 | }, 126 | { 127 | "alias": "\"time\"", 128 | "parameters": [ 129 | { 130 | "name": "\"timestamp\"", 131 | "type": "functionParameter" 132 | } 133 | ], 134 | "type": "function" 135 | } 136 | ], 137 | "groupBy": [], 138 | "limit": 50 139 | }, 140 | "table": "markdown_counts" 141 | } 142 | ], 143 | "title": "Note Count", 144 | "type": "timeseries" 145 | } 146 | ], 147 | "schemaVersion": 39, 148 | "tags": [], 149 | "templating": { 150 | "list": [] 151 | }, 152 | "time": { 153 | "from": "now-90d", 154 | "to": "now" 155 | }, 156 | "timepicker": {}, 157 | "timezone": "browser", 158 | "title": "Zettelkasten Tracker", 159 | "uid": "ddkaqtaxm8t8ga", 160 | "version": 6, 161 | "weekStart": "" 162 | } 163 | -------------------------------------------------------------------------------- /monitoring/controllers/base/grafana/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - repository.yaml 5 | - release.yaml 6 | -------------------------------------------------------------------------------- /monitoring/controllers/base/grafana/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: grafana 5 | namespace: monitoring 6 | spec: 7 | interval: 30m 8 | chart: 9 | spec: 10 | chart: grafana 11 | version: "7.3.9" 12 | sourceRef: 13 | kind: HelmRepository 14 | name: grafana 15 | namespace: monitoring 16 | interval: 12h 17 | values: 18 | # configuration to make dashboard configmaps discoverable 19 | sidecar: 20 | datasources: 21 | enabled: true 22 | label: grafana_datasource 23 | labelValue: "1" 24 | dashboards: 25 | enabled: true 26 | label: grafana_dashboard 27 | labelValue: "1" 28 | # Allow discovery in all namespaces for dashboards 29 | searchNamespace: ALL 30 | provider: 31 | allowUiUpdates: true 32 | 33 | persistence: 34 | enabled: true 35 | type: pvc 36 | accessModes: 37 | - ReadWriteOnce 38 | size: 4Gi 39 | 40 | # plugins: 41 | # - grafana-strava-datasource 1.6.1 42 | -------------------------------------------------------------------------------- /monitoring/controllers/base/grafana/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: grafana 5 | namespace: monitoring 6 | spec: 7 | interval: 24h 8 | url: https://grafana.github.io/helm-charts 9 | -------------------------------------------------------------------------------- /monitoring/controllers/base/kube-prometheus-stack/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - repository.yaml 5 | - release.yaml 6 | -------------------------------------------------------------------------------- /monitoring/controllers/base/kube-prometheus-stack/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: kube-prometheus-stack 5 | namespace: monitoring 6 | spec: 7 | interval: 30m 8 | chart: 9 | spec: 10 | chart: kube-prometheus-stack 11 | version: "58.x" 12 | sourceRef: 13 | kind: HelmRepository 14 | name: kube-prometheus-stack 15 | namespace: monitoring 16 | interval: 12h 17 | install: 18 | crds: Create 19 | upgrade: 20 | crds: CreateReplace 21 | driftDetection: 22 | mode: enabled 23 | ignore: 24 | # Ignore "validated" annotation which is not inserted during install 25 | - paths: ["/metadata/annotations/prometheus-operator-validated"] 26 | target: 27 | kind: PrometheusRule 28 | values: 29 | grafana: 30 | enabled: false 31 | prometheus: 32 | prometheusSpec: 33 | podMonitorNamespaceSelector: 34 | matchLabels: 35 | podmonitorscrape: "true" 36 | podMonitorSelectorNilUsesHelmValues: false 37 | probeSelectorNilUsesHelmValues: false 38 | ruleSelectorNilUsesHelmValues: false 39 | serviceMonitorSelectorNilUsesHelmValues: false 40 | -------------------------------------------------------------------------------- /monitoring/controllers/base/kube-prometheus-stack/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: kube-prometheus-stack 5 | namespace: monitoring 6 | spec: 7 | interval: 24h 8 | url: https://prometheus-community.github.io/helm-charts 9 | -------------------------------------------------------------------------------- /monitoring/controllers/base/loki/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - release.yaml 5 | -------------------------------------------------------------------------------- /monitoring/controllers/base/loki/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: loki 5 | namespace: monitoring 6 | spec: 7 | interval: 30m 8 | chart: 9 | spec: 10 | chart: loki 11 | version: "6.10.0" 12 | sourceRef: 13 | kind: HelmRepository 14 | name: grafana 15 | namespace: monitoring 16 | interval: 12h 17 | values: 18 | deploymentMode: SingleBinary 19 | loki: 20 | auth_enabled: false 21 | commonConfig: 22 | replication_factor: 1 23 | storage: 24 | type: "filesystem" 25 | schemaConfig: 26 | configs: 27 | - from: "2024-01-01" 28 | store: tsdb 29 | index: 30 | prefix: loki_index_ 31 | period: 24h 32 | object_store: filesystem # we're storing on filesystem so there's no real persistence here. 33 | schema: v13 34 | singleBinary: 35 | replicas: 1 36 | read: 37 | replicas: 0 38 | backend: 39 | replicas: 0 40 | write: 41 | replicas: 0 42 | monitoring: 43 | selfMonitoring: 44 | enabled: true 45 | grafanaAgent: 46 | installOperator: false 47 | # dashboards: 48 | # enabled: false 49 | # rules: 50 | # enabled: false 51 | # alerting: false 52 | -------------------------------------------------------------------------------- /monitoring/controllers/base/promtail/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - promtail.yaml 5 | -------------------------------------------------------------------------------- /monitoring/controllers/base/promtail/promtail.yaml: -------------------------------------------------------------------------------- 1 | --- # Daemonset.yaml 2 | apiVersion: apps/v1 3 | kind: DaemonSet 4 | metadata: 5 | name: promtail-daemonset 6 | namespace: monitoring 7 | spec: 8 | selector: 9 | matchLabels: 10 | name: promtail 11 | template: 12 | metadata: 13 | labels: 14 | name: promtail 15 | spec: 16 | serviceAccount: promtail-serviceaccount 17 | containers: 18 | - name: promtail-container 19 | image: grafana/promtail 20 | args: 21 | - -config.file=/etc/promtail/promtail.yaml 22 | env: 23 | - name: "HOSTNAME" # needed when using kubernetes_sd_configs 24 | valueFrom: 25 | fieldRef: 26 | fieldPath: "spec.nodeName" 27 | volumeMounts: 28 | - name: logs 29 | mountPath: /var/log 30 | - name: promtail-config 31 | mountPath: /etc/promtail 32 | - mountPath: /var/lib/docker/containers 33 | name: varlibdockercontainers 34 | readOnly: true 35 | volumes: 36 | - name: logs 37 | hostPath: 38 | path: /var/log 39 | - name: varlibdockercontainers 40 | hostPath: 41 | path: /var/lib/docker/containers 42 | - name: promtail-config 43 | configMap: 44 | name: promtail-config 45 | --- # configmap.yaml 46 | apiVersion: v1 47 | kind: ConfigMap 48 | metadata: 49 | name: promtail-config 50 | namespace: monitoring 51 | data: 52 | promtail.yaml: | 53 | server: 54 | http_listen_port: 9080 55 | grpc_listen_port: 0 56 | 57 | clients: 58 | - url: http://loki.monitoring:3100/loki/api/v1/push 59 | 60 | positions: 61 | filename: /tmp/positions.yaml 62 | target_config: 63 | sync_period: 10s 64 | scrape_configs: 65 | - job_name: pod-logs 66 | kubernetes_sd_configs: 67 | - role: pod 68 | pipeline_stages: 69 | - docker: {} 70 | relabel_configs: 71 | - source_labels: 72 | - __meta_kubernetes_pod_node_name 73 | target_label: __host__ 74 | - action: labelmap 75 | regex: __meta_kubernetes_pod_label_(.+) 76 | - action: replace 77 | replacement: $1 78 | separator: / 79 | source_labels: 80 | - __meta_kubernetes_namespace 81 | - __meta_kubernetes_pod_name 82 | target_label: job 83 | - action: replace 84 | source_labels: 85 | - __meta_kubernetes_namespace 86 | target_label: namespace 87 | - action: replace 88 | source_labels: 89 | - __meta_kubernetes_pod_name 90 | target_label: pod 91 | - action: replace 92 | source_labels: 93 | - __meta_kubernetes_pod_container_name 94 | target_label: container 95 | - replacement: /var/log/pods/*$1/*.log 96 | separator: / 97 | source_labels: 98 | - __meta_kubernetes_pod_uid 99 | - __meta_kubernetes_pod_container_name 100 | target_label: __path__ 101 | 102 | --- # Clusterrole.yaml 103 | apiVersion: rbac.authorization.k8s.io/v1 104 | kind: ClusterRole 105 | metadata: 106 | name: promtail-clusterrole 107 | namespace: monitoring 108 | rules: 109 | - apiGroups: [""] 110 | resources: 111 | - nodes 112 | - services 113 | - pods 114 | verbs: 115 | - get 116 | - watch 117 | - list 118 | 119 | --- # ServiceAccount.yaml 120 | apiVersion: v1 121 | kind: ServiceAccount 122 | metadata: 123 | name: promtail-serviceaccount 124 | namespace: monitoring 125 | --- # Rolebinding.yaml 126 | apiVersion: rbac.authorization.k8s.io/v1 127 | kind: ClusterRoleBinding 128 | metadata: 129 | name: promtail-clusterrolebinding 130 | subjects: 131 | - kind: ServiceAccount 132 | name: promtail-serviceaccount 133 | namespace: monitoring 134 | roleRef: 135 | kind: ClusterRole 136 | name: promtail-clusterrole 137 | apiGroup: rbac.authorization.k8s.io 138 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: monitoring 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - release.yaml 8 | - repository.yaml 9 | 10 | configMapGenerator: 11 | - name: kube-prometheus-stack-values 12 | files: 13 | - values.yaml=values.yaml 14 | configurations: 15 | - kustomizeconfig.yaml 16 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | nameReference: 2 | - kind: ConfigMap 3 | version: v1 4 | fieldSpecs: 5 | - path: spec/valuesFrom/name 6 | kind: HelmRelease 7 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: monitoring 5 | labels: 6 | pod-security.kubernetes.io/enforce: privileged 7 | app.kubernetes.io/component: monitoring 8 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: kube-prometheus-stack 5 | spec: 6 | interval: 30m 7 | chart: 8 | spec: 9 | chart: kube-prometheus-stack 10 | version: "68.x" 11 | sourceRef: 12 | kind: HelmRepository 13 | name: kube-prometheus-stack 14 | namespace: monitoring 15 | interval: 12h 16 | 17 | valuesFrom: 18 | - kind: ConfigMap 19 | name: kube-prometheus-stack-values 20 | 21 | install: 22 | crds: Create 23 | upgrade: 24 | crds: CreateReplace 25 | driftDetection: 26 | mode: enabled 27 | ignore: 28 | # Ignore "validated" annotation which is not inserted during install 29 | - paths: ["/metadata/annotations/prometheus-operator-validated"] 30 | target: 31 | kind: PrometheusRule 32 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: kube-prometheus-stack 5 | namespace: monitoring 6 | spec: 7 | interval: 24h 8 | url: https://prometheus-community.github.io/helm-charts 9 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: grafana-env 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: HEALTH_DB_PASSWORD 12 | remoteRef: 13 | key: health-db-password 14 | - secretKey: HOME_IOT_DB_PASSWORD 15 | remoteRef: 16 | key: shelly-db-password 17 | - secretKey: QF_DB_PASSWORD 18 | remoteRef: 19 | key: zettelkasten-tracker-db-password 20 | -------------------------------------------------------------------------------- /monitoring/controllers/data/kube-prometheus-stack/values.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: These must be set manually to the control plane IP addresses 2 | # kubeEtcd: 3 | # endpoints: 4 | # - x.y.z.a 5 | # - x.y.z.b 6 | # - x.y.z.c 7 | kubeControllerManager: 8 | service: 9 | selector: 10 | k8s-app: kube-controller-manager 11 | kubeScheduler: 12 | service: 13 | selector: 14 | k8s-app: kube-scheduler 15 | 16 | alertmanager: 17 | enabled: false 18 | 19 | prometheus: 20 | prometheusSpec: 21 | podMonitorNamespaceSelector: 22 | matchLabels: 23 | app.kubernetes.io/component: monitoring 24 | 25 | # Discover all PodMonitors, Probes, PrometheusRules and ServiceMonitors 26 | podMonitorSelectorNilUsesHelmValues: false 27 | probeSelectorNilUsesHelmValues: false 28 | ruleSelectorNilUsesHelmValues: false 29 | serviceMonitorSelectorNilUsesHelmValues: false 30 | 31 | grafana: 32 | grafana.ini: 33 | date_formats: 34 | default_week_start: monday 35 | 36 | # envFromSecret: grafana-env 37 | 38 | sidecar: 39 | datasources: 40 | enabled: true 41 | label: grafana_datasource 42 | labelValue: "1" 43 | dashboards: 44 | enabled: true 45 | label: grafana_dashboard 46 | labelValue: "1" 47 | searchNamespace: monitoring 48 | provider: 49 | allowUiUpdates: true 50 | 51 | # Choosing to add data sources here instead of separate configmaps. 52 | # Reason: Adding here will trigger a redeploy. 53 | # Future improvement: utilize Kustomizeconfig to allow separate configmap files. 54 | # datasources: 55 | # datasources.yaml: 56 | # apiVersion: 1 57 | # datasources: 58 | # - name: Health DB 59 | # type: postgres 60 | # url: health-db.mischavandenburg.net:5432 61 | # user: health 62 | # uid: health-db 63 | # secureJsonData: 64 | # password: $HEALTH_DB_PASSWORD 65 | # jsonData: 66 | # database: health 67 | # sslmode: disable 68 | # maxOpenConns: 100 69 | # maxIdleConns: 100 70 | # maxLifetime: 14400 71 | # minIdleTimeoutMinutes: 1 72 | # connMaxLifetime: 14400 73 | # postgresVersion: 1500 74 | # timescaledb: false 75 | # version: 1 76 | # editable: true 77 | # - name: Home IoT DB 78 | # type: postgres 79 | # url: home-iot-db.mischavandenburg.net:5432 80 | # user: shelly 81 | # uid: home-iot-db 82 | # secureJsonData: 83 | # password: $HOME_IOT_DB_PASSWORD 84 | # jsonData: 85 | # database: shelly 86 | # sslmode: disable 87 | # maxOpenConns: 100 88 | # maxIdleConns: 100 89 | # maxLifetime: 14400 90 | # minIdleTimeoutMinutes: 1 91 | # connMaxLifetime: 14400 92 | # postgresVersion: 1500 93 | # timescaledb: false 94 | # version: 1 95 | # editable: true 96 | # - name: Quantified Self DB 97 | # type: postgres 98 | # url: quantified-self-db.mischavandenburg.net:5432 99 | # user: zktracker 100 | # uid: quantified-self-db 101 | # secureJsonData: 102 | # password: $QF_DB_PASSWORD 103 | # jsonData: 104 | # database: zettelkasten-tracker 105 | # sslmode: disable 106 | # maxOpenConns: 100 107 | # maxIdleConns: 100 108 | # maxLifetime: 14400 109 | # minIdleTimeoutMinutes: 1 110 | # connMaxLifetime: 14400 111 | # postgresVersion: 1500 112 | # timescaledb: false 113 | # version: 1 114 | # editable: true 115 | # 116 | # deleteDatasources: 117 | # - name: Health DB 118 | # orgId: 1 119 | # - name: Home IoT DB 120 | # orgId: 1 121 | # - name: Quatified Self DB 122 | # orgId: 1 123 | 124 | persistence: 125 | enabled: true 126 | type: pvc 127 | accessModes: 128 | - ReadWriteOnce 129 | size: 4Gi 130 | 131 | # Allow Omni Workload Proxying for this service 132 | service: 133 | annotations: 134 | omni-kube-service-exposer.sidero.dev/port: "50082" 135 | omni-kube-service-exposer.sidero.dev/label: Grafana 136 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: monitoring 4 | resources: 5 | - namespace.yaml 6 | - secrets.yaml 7 | - release.yaml 8 | - repository.yaml 9 | 10 | configMapGenerator: 11 | - name: kube-prometheus-stack-values 12 | files: 13 | - values.yaml=values.yaml 14 | configurations: 15 | - kustomizeconfig.yaml 16 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | nameReference: 2 | - kind: ConfigMap 3 | version: v1 4 | fieldSpecs: 5 | - path: spec/valuesFrom/name 6 | kind: HelmRelease 7 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: monitoring 5 | labels: 6 | pod-security.kubernetes.io/enforce: privileged 7 | app.kubernetes.io/component: monitoring 8 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2 2 | kind: HelmRelease 3 | metadata: 4 | name: kube-prometheus-stack 5 | spec: 6 | interval: 30m 7 | chart: 8 | spec: 9 | chart: kube-prometheus-stack 10 | version: "68.x" 11 | sourceRef: 12 | kind: HelmRepository 13 | name: kube-prometheus-stack 14 | namespace: monitoring 15 | interval: 12h 16 | 17 | valuesFrom: 18 | - kind: ConfigMap 19 | name: kube-prometheus-stack-values 20 | 21 | install: 22 | crds: Create 23 | upgrade: 24 | crds: CreateReplace 25 | driftDetection: 26 | mode: enabled 27 | ignore: 28 | # Ignore "validated" annotation which is not inserted during install 29 | - paths: ["/metadata/annotations/prometheus-operator-validated"] 30 | target: 31 | kind: PrometheusRule 32 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1 2 | kind: HelmRepository 3 | metadata: 4 | name: kube-prometheus-stack 5 | namespace: monitoring 6 | spec: 7 | interval: 24h 8 | url: https://prometheus-community.github.io/helm-charts 9 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: grafana-env 5 | spec: 6 | refreshInterval: 1h 7 | secretStoreRef: 8 | name: azure-kv-store 9 | kind: ClusterSecretStore 10 | data: 11 | - secretKey: HEALTH_DB_PASSWORD 12 | remoteRef: 13 | key: health-db-password 14 | - secretKey: HOME_IOT_DB_PASSWORD 15 | remoteRef: 16 | key: shelly-db-password 17 | - secretKey: QF_DB_PASSWORD 18 | remoteRef: 19 | key: zettelkasten-tracker-db-password 20 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kube-prometheus-stack/values.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: These must be set manually to the control plane IP addresses 2 | # kubeEtcd: 3 | # endpoints: 4 | # - x.y.z.a 5 | # - x.y.z.b 6 | # - x.y.z.c 7 | kubeControllerManager: 8 | service: 9 | selector: 10 | k8s-app: kube-controller-manager 11 | kubeScheduler: 12 | service: 13 | selector: 14 | k8s-app: kube-scheduler 15 | 16 | alertmanager: 17 | enabled: false 18 | 19 | prometheus: 20 | prometheusSpec: 21 | podMonitorNamespaceSelector: 22 | matchLabels: 23 | app.kubernetes.io/component: monitoring 24 | 25 | # Discover all PodMonitors, Probes, PrometheusRules and ServiceMonitors 26 | podMonitorSelectorNilUsesHelmValues: false 27 | probeSelectorNilUsesHelmValues: false 28 | ruleSelectorNilUsesHelmValues: false 29 | serviceMonitorSelectorNilUsesHelmValues: false 30 | 31 | grafana: 32 | grafana.ini: 33 | date_formats: 34 | default_week_start: monday 35 | 36 | envFromSecret: grafana-env 37 | 38 | sidecar: 39 | datasources: 40 | enabled: true 41 | label: grafana_datasource 42 | labelValue: "1" 43 | dashboards: 44 | enabled: true 45 | label: grafana_dashboard 46 | labelValue: "1" 47 | searchNamespace: monitoring 48 | provider: 49 | allowUiUpdates: true 50 | 51 | # Choosing to add data sources here instead of separate configmaps. 52 | # Reason: Adding here will trigger a redeploy. 53 | # Future improvement: utilize Kustomizeconfig to allow separate configmap files. 54 | datasources: 55 | datasources.yaml: 56 | apiVersion: 1 57 | datasources: 58 | - name: Health DB 59 | type: postgres 60 | url: health-db.mischavandenburg.net:5432 61 | user: health 62 | uid: health-db 63 | secureJsonData: 64 | password: $HEALTH_DB_PASSWORD 65 | jsonData: 66 | database: health 67 | sslmode: disable 68 | maxOpenConns: 100 69 | maxIdleConns: 100 70 | maxLifetime: 14400 71 | minIdleTimeoutMinutes: 1 72 | connMaxLifetime: 14400 73 | postgresVersion: 1500 74 | timescaledb: false 75 | version: 1 76 | editable: true 77 | - name: Home IoT DB 78 | type: postgres 79 | url: home-iot-db.mischavandenburg.net:5432 80 | user: shelly 81 | uid: home-iot-db 82 | secureJsonData: 83 | password: $HOME_IOT_DB_PASSWORD 84 | jsonData: 85 | database: shelly 86 | sslmode: disable 87 | maxOpenConns: 100 88 | maxIdleConns: 100 89 | maxLifetime: 14400 90 | minIdleTimeoutMinutes: 1 91 | connMaxLifetime: 14400 92 | postgresVersion: 1500 93 | timescaledb: false 94 | version: 1 95 | editable: true 96 | - name: Quantified Self DB 97 | type: postgres 98 | url: quantified-self-db.mischavandenburg.net:5432 99 | user: zktracker 100 | uid: quantified-self-db 101 | secureJsonData: 102 | password: $QF_DB_PASSWORD 103 | jsonData: 104 | database: zettelkasten-tracker 105 | sslmode: disable 106 | maxOpenConns: 100 107 | maxIdleConns: 100 108 | maxLifetime: 14400 109 | minIdleTimeoutMinutes: 1 110 | connMaxLifetime: 14400 111 | postgresVersion: 1500 112 | timescaledb: false 113 | version: 1 114 | editable: true 115 | 116 | deleteDatasources: 117 | - name: Health DB 118 | orgId: 1 119 | - name: Home IoT DB 120 | orgId: 1 121 | - name: Quatified Self DB 122 | orgId: 1 123 | 124 | persistence: 125 | enabled: true 126 | type: pvc 127 | accessModes: 128 | - ReadWriteOnce 129 | size: 4Gi 130 | 131 | # Allow Omni Workload Proxying for this service 132 | service: 133 | annotations: 134 | omni-kube-service-exposer.sidero.dev/port: "50082" 135 | omni-kube-service-exposer.sidero.dev/label: Grafana 136 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - kube-prometheus-stack 5 | # - loki 6 | # - promtail 7 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/loki/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/loki 5 | -------------------------------------------------------------------------------- /monitoring/controllers/jotunheim/promtail/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base/promtail 5 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "kubernetes": { 4 | "fileMatch": [ 5 | "\\.yaml$" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | # Util 2 | 3 | Collection of scripts and tools I use to set up or maintain my homelab 4 | 5 | # Grafana 6 | 7 | The kube-prometheus-stack helm chart comes with a very good set of default dashboards. 8 | 9 | When I decided to split off my Grafana deployment, these were not included. 10 | 11 | I use scripts to extract them from the ConfigMaps and then I copy them to a persistent volume I have mounted into the Grafana container. 12 | 13 | The Grafana directory contains all the default dashboards which are included in the kube-prometheus-stack helm chart. 14 | -------------------------------------------------------------------------------- /utils/dashboards/configmapcollector: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # collects grafana dashboard json files from config maps 3 | 4 | # Set the namespace if required, or use default 5 | NAMESPACE="prometheus-stack" 6 | 7 | # Get list of ConfigMap names 8 | CONFIGMAPS=$(kubectl get configmap -n "$NAMESPACE" -o jsonpath='{.items[*].metadata.name}') 9 | 10 | # Iterate through each ConfigMap 11 | for cm in $CONFIGMAPS; do 12 | # Get the ConfigMap data 13 | DATA=$(kubectl get configmap "$cm" -n "$NAMESPACE" -o json) 14 | 15 | # Extract keys from the data section that end with .json 16 | KEYS=$(echo "$DATA" | jq -r '.data | keys[] | select(endswith(".json"))') 17 | 18 | # Check if there are any .json keys 19 | if [ -z "$KEYS" ]; then 20 | echo "No .json data in ConfigMap $cm" 21 | continue 22 | fi 23 | 24 | # Iterate over each .json key 25 | for key in $KEYS; do 26 | # Extract the JSON value of the key 27 | JSON_VALUE=$(echo "$DATA" | jq -r --arg KEY "$key" '.data[$KEY]') 28 | 29 | # Sort the JSON value 30 | SORTED_JSON=$(echo "$JSON_VALUE" | jq -S .) 31 | 32 | # Check if jq sorted the data successfully 33 | if [ $? -ne 0 ]; then 34 | echo "Error sorting JSON data for key $key in ConfigMap $cm" 35 | continue 36 | fi 37 | 38 | # Save to file named after the key 39 | echo "$SORTED_JSON" >"$key" 40 | echo "Saved key $key of ConfigMap $cm to $key" 41 | done 42 | done 43 | -------------------------------------------------------------------------------- /utils/dashboards/dashboardcopy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # script to copy dashboards as json files to the grafana podname 3 | # the path is the mount pount of the persistent volume defined in the helm install 4 | 5 | podname=grafana-59bc875f84-vvzzx 6 | 7 | # for file in ./standard-dashboards/*json; do 8 | for file in ./personal-dashboards/*json; do 9 | kubectl cp "$file" monitoring/"$podname":/var/lib/grafana/dashboards/default 10 | done 11 | 12 | for file in ./standard-dashboards/*json; do 13 | kubectl cp "$file" monitoring/"$podname":/var/lib/grafana/dashboards/default 14 | done 15 | -------------------------------------------------------------------------------- /utils/dashboards/oct-2024/zk-tracker.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "type": "dashboard" 15 | } 16 | ] 17 | }, 18 | "editable": true, 19 | "fiscalYearStartMonth": 0, 20 | "graphTooltip": 0, 21 | "id": 33, 22 | "links": [], 23 | "panels": [ 24 | { 25 | "datasource": { 26 | "type": "grafana-postgresql-datasource", 27 | "uid": "bdkaqflt5vdogb" 28 | }, 29 | "fieldConfig": { 30 | "defaults": { 31 | "color": { 32 | "mode": "palette-classic" 33 | }, 34 | "custom": { 35 | "axisBorderShow": false, 36 | "axisCenteredZero": false, 37 | "axisColorMode": "text", 38 | "axisLabel": "", 39 | "axisPlacement": "auto", 40 | "barAlignment": 0, 41 | "drawStyle": "line", 42 | "fillOpacity": 0, 43 | "gradientMode": "none", 44 | "hideFrom": { 45 | "legend": false, 46 | "tooltip": false, 47 | "viz": false 48 | }, 49 | "insertNulls": false, 50 | "lineInterpolation": "linear", 51 | "lineWidth": 1, 52 | "pointSize": 5, 53 | "scaleDistribution": { 54 | "type": "linear" 55 | }, 56 | "showPoints": "auto", 57 | "spanNulls": false, 58 | "stacking": { 59 | "group": "A", 60 | "mode": "none" 61 | }, 62 | "thresholdsStyle": { 63 | "mode": "off" 64 | } 65 | }, 66 | "mappings": [], 67 | "thresholds": { 68 | "mode": "absolute", 69 | "steps": [ 70 | { 71 | "color": "green", 72 | "value": null 73 | }, 74 | { 75 | "color": "red", 76 | "value": 80 77 | } 78 | ] 79 | } 80 | }, 81 | "overrides": [] 82 | }, 83 | "gridPos": { 84 | "h": 8, 85 | "w": 12, 86 | "x": 0, 87 | "y": 0 88 | }, 89 | "id": 1, 90 | "options": { 91 | "legend": { 92 | "calcs": [], 93 | "displayMode": "list", 94 | "placement": "bottom", 95 | "showLegend": true 96 | }, 97 | "tooltip": { 98 | "mode": "single", 99 | "sort": "none" 100 | } 101 | }, 102 | "pluginVersion": "10.4.1", 103 | "targets": [ 104 | { 105 | "datasource": { 106 | "type": "grafana-postgresql-datasource", 107 | "uid": "bdkaqflt5vdogb" 108 | }, 109 | "editorMode": "code", 110 | "format": "time_series", 111 | "key": "Q-764060a2-b711-4170-8ecd-4432656108f8-0", 112 | "rawQuery": true, 113 | "rawSql": "SELECT count, \"timestamp\" AS \"time\" FROM markdown_counts", 114 | "refId": "A", 115 | "sql": { 116 | "columns": [ 117 | { 118 | "parameters": [ 119 | { 120 | "name": "count", 121 | "type": "functionParameter" 122 | } 123 | ], 124 | "type": "function" 125 | }, 126 | { 127 | "alias": "\"time\"", 128 | "parameters": [ 129 | { 130 | "name": "\"timestamp\"", 131 | "type": "functionParameter" 132 | } 133 | ], 134 | "type": "function" 135 | } 136 | ], 137 | "groupBy": [], 138 | "limit": 50 139 | }, 140 | "table": "markdown_counts" 141 | } 142 | ], 143 | "title": "Note Count", 144 | "type": "timeseries" 145 | } 146 | ], 147 | "schemaVersion": 39, 148 | "tags": [], 149 | "templating": { 150 | "list": [] 151 | }, 152 | "time": { 153 | "from": "now-90d", 154 | "to": "now" 155 | }, 156 | "timepicker": {}, 157 | "timezone": "browser", 158 | "title": "Zettelkasten Tracker", 159 | "uid": "ddkaqtaxm8t8ga", 160 | "version": 6, 161 | "weekStart": "" 162 | } 163 | -------------------------------------------------------------------------------- /utils/dashboards/personal-dashboards/dashboardcopy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # script to copy dashboards as json files to the grafana podname 3 | # the path is the mount pount of the persistent volume defined in the helm install 4 | 5 | podname=grafana-59bc875f84-hdlxn 6 | 7 | for file in *json; do 8 | kubectl cp "$file" monitoring/"$podname":/var/lib/grafana/dashboards/default 9 | done 10 | -------------------------------------------------------------------------------- /utils/database/README.md: -------------------------------------------------------------------------------- 1 | # Database Helper Program 2 | 3 | This will create the secrets in my keyvault, set up a container in my storage account, and generate the YAML for the database cluster. 4 | 5 | To do: 6 | 7 | - Turn into CLI using Typer 8 | - Ask if I want to generate template for Deployment 9 | 10 | The Deployment template might be necessary as a dummy because the secret needs to be mounted somewhere in order to be synced. 11 | 12 | HL_AZ_CONN_STRING string is the connection string of the storage account, Security & networking > Access keys > key1 > connection string. 13 | 14 | ## Running 15 | 16 | ```bash 17 | az login --use-device-code 18 | poetry shell 19 | poetry install 20 | python3 main.py 21 | ``` 22 | -------------------------------------------------------------------------------- /utils/database/backups/backup-mealie.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.k8s.enterprisedb.io/v1 2 | kind: Backup 3 | metadata: 4 | name: mealie-backup-on-demand-7 5 | spec: 6 | method: barmanObjectStore 7 | cluster: 8 | name: mealie-db-production 9 | -------------------------------------------------------------------------------- /utils/database/backups/test-restore-mealie.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.k8s.enterprisedb.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: cluster-restore-mealie 5 | spec: 6 | description: "test recovery mealie db" 7 | imageName: quay.io/enterprisedb/postgresql:16.1 8 | instances: 3 9 | 10 | storage: 11 | storageClass: local-path 12 | size: 1Gi 13 | 14 | bootstrap: 15 | recovery: 16 | source: clusterBackup 17 | database: mealie 18 | owner: mealie 19 | secret: 20 | name: mealie-db-creds 21 | 22 | externalClusters: 23 | - name: clusterBackup 24 | barmanObjectStore: 25 | destinationPath: "https://mischahomelabk8sbackups.blob.core.windows.net/mealie" 26 | azureCredentials: 27 | storageAccount: 28 | name: azure-creds 29 | key: mealie-storage-account-name 30 | storageSasToken: 31 | name: azure-creds 32 | key: mealie-blob-sas 33 | wal: 34 | maxParallel: 8 35 | -------------------------------------------------------------------------------- /utils/database/delete.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from azure.identity import DefaultAzureCredential 4 | from azure.keyvault.secrets import SecretClient 5 | from azure.storage.blob import BlobServiceClient 6 | from azure.core.exceptions import ResourceNotFoundError, AzureError 7 | 8 | logging.basicConfig( 9 | level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" 10 | ) 11 | 12 | 13 | def delete_key_vault_secrets(prefix, key_vault_name): 14 | try: 15 | secret_client = SecretClient( 16 | vault_url=f"https://{key_vault_name}.vault.azure.net/", 17 | credential=DefaultAzureCredential(), 18 | ) 19 | secret_names = [ 20 | f"{prefix}-db-username", 21 | f"{prefix}-db-password", 22 | f"{prefix}-storage-account-name", 23 | f"{prefix}-blob-sas", 24 | f"{prefix}-connection-string", 25 | ] 26 | for name in secret_names: 27 | try: 28 | secret_client.begin_delete_secret(name) 29 | logging.info(f"Secret '{name}' deletion initiated in Key Vault") 30 | except ResourceNotFoundError: 31 | logging.info(f"Secret '{name}' not found in Key Vault") 32 | 33 | logging.info( 34 | f"All secrets deletion initiated in Azure Key Vault for prefix: {prefix}" 35 | ) 36 | except AzureError as e: 37 | logging.error(f"Azure operation failed: {str(e)}") 38 | raise 39 | 40 | 41 | def delete_blob_container(conn_string, prefix): 42 | try: 43 | blob_service = BlobServiceClient.from_connection_string(conn_string) 44 | container_name = prefix.lower() 45 | container_client = blob_service.get_container_client(container_name) 46 | 47 | container_client.delete_container() 48 | logging.info(f"Container '{container_name}' deleted.") 49 | except ResourceNotFoundError: 50 | logging.info(f"Container '{container_name}' not found.") 51 | except AzureError as e: 52 | logging.error(f"Azure operation failed: {str(e)}") 53 | raise 54 | 55 | 56 | def main(): 57 | try: 58 | prefix = input("Enter the prefix of the database to delete: ").strip() 59 | if not prefix: 60 | raise ValueError("Prefix cannot be empty") 61 | 62 | key_vault_name = os.getenv("AZURE_KEY_VAULT_NAME", "k8s-homelab-production") 63 | conn_string = os.getenv("HL_AZ_CONN_STRING") 64 | if not conn_string: 65 | raise ValueError("HL_AZ_CONN_STRING environment variable is not set") 66 | 67 | delete_key_vault_secrets(prefix, key_vault_name) 68 | delete_blob_container(conn_string, prefix) 69 | 70 | logging.info(f"Deletion process completed for prefix: {prefix}") 71 | except ValueError as e: 72 | logging.error(str(e)) 73 | except Exception as e: 74 | logging.error(f"An unexpected error occurred: {str(e)}") 75 | 76 | 77 | if __name__ == "__main__": 78 | main() 79 | -------------------------------------------------------------------------------- /utils/database/deployment_example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mount-holder 5 | namespace: skool 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: mount-holder 11 | 12 | template: 13 | metadata: 14 | labels: 15 | app: mount-holder 16 | try: "0" 17 | policy-type: "app" 18 | 19 | spec: 20 | containers: 21 | - name: mount-holder 22 | image: nginx 23 | 24 | securityContext: 25 | allowPrivilegeEscalation: false 26 | 27 | volumeMounts: 28 | - name: secrets-store-inline 29 | mountPath: /mnt/secrets-store 30 | readOnly: true 31 | 32 | restartPolicy: Always 33 | 34 | volumes: 35 | - name: secrets-store-inline 36 | csi: 37 | driver: secrets-store.csi.k8s.io 38 | readOnly: true 39 | volumeAttributes: 40 | secretProviderClass: azure-kv-secrets 41 | nodePublishSecretRef: # Only required when using service principal mode 42 | name: secrets-store-creds # Only required when using service principal mode 43 | -------------------------------------------------------------------------------- /utils/database/generated-configs/health_kubernetes_configs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.k8s.enterprisedb.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: health-db-production-v0 5 | namespace: health 6 | spec: 7 | backup: 8 | barmanObjectStore: 9 | azureCredentials: 10 | storageAccount: 11 | key: health-storage-account-name 12 | name: azure-creds 13 | storageSasToken: 14 | key: health-blob-sas 15 | name: azure-creds 16 | data: 17 | compression: gzip 18 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/health 19 | wal: 20 | compression: gzip 21 | retentionPolicy: 14d 22 | bootstrap: 23 | initdb: 24 | database: health 25 | owner: health 26 | secret: 27 | name: health-db-creds 28 | description: Postgres cluster for the health application 29 | imageName: quay.io/enterprisedb/postgresql:16.1 30 | inheritedMetadata: 31 | labels: 32 | app: health-database 33 | policy-type: database 34 | instances: 3 35 | monitoring: 36 | enablePodMonitor: true 37 | resources: 38 | requests: 39 | memory: 100Mi 40 | storage: 41 | size: 1Gi 42 | 43 | --- 44 | apiVersion: postgresql.k8s.enterprisedb.io/v1 45 | kind: ScheduledBackup 46 | metadata: 47 | name: health-db-production 48 | namespace: health 49 | spec: 50 | backupOwnerReference: cluster 51 | cluster: 52 | name: health-db-production-v0 53 | immediate: true 54 | schedule: 0 0 3 * * 55 | -------------------------------------------------------------------------------- /utils/database/generated-configs/shelly_kubernetes_configs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.k8s.enterprisedb.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: shelly-db-production-v0 5 | namespace: shelly 6 | spec: 7 | backup: 8 | barmanObjectStore: 9 | azureCredentials: 10 | storageAccount: 11 | key: shelly-storage-account-name 12 | name: azure-creds 13 | storageSasToken: 14 | key: shelly-blob-sas 15 | name: azure-creds 16 | data: 17 | compression: gzip 18 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/shelly 19 | wal: 20 | compression: gzip 21 | retentionPolicy: 14d 22 | bootstrap: 23 | initdb: 24 | database: shelly 25 | owner: shelly 26 | secret: 27 | name: shelly-db-creds 28 | description: Postgres cluster for the shelly application 29 | imageName: quay.io/enterprisedb/postgresql:16.1 30 | inheritedMetadata: 31 | labels: 32 | app: shelly-database 33 | policy-type: database 34 | instances: 3 35 | monitoring: 36 | enablePodMonitor: true 37 | resources: 38 | requests: 39 | memory: 100Mi 40 | storage: 41 | size: 1Gi 42 | 43 | --- 44 | apiVersion: postgresql.k8s.enterprisedb.io/v1 45 | kind: ScheduledBackup 46 | metadata: 47 | name: shelly-db-production 48 | namespace: shelly 49 | spec: 50 | backupOwnerReference: cluster 51 | cluster: 52 | name: shelly-db-production-v0 53 | immediate: true 54 | schedule: 0 0 3 * * 55 | -------------------------------------------------------------------------------- /utils/database/generated-configs/skool_kubernetes_configs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.k8s.enterprisedb.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: skool-db-production-v0 5 | namespace: skool 6 | spec: 7 | backup: 8 | barmanObjectStore: 9 | azureCredentials: 10 | storageAccount: 11 | key: skool-storage-account-name 12 | name: azure-creds 13 | storageSasToken: 14 | key: skool-blob-sas 15 | name: azure-creds 16 | data: 17 | compression: gzip 18 | destinationPath: https://hldatabaseproduction.blob.core.windows.net/skool 19 | wal: 20 | compression: gzip 21 | retentionPolicy: 14d 22 | bootstrap: 23 | initdb: 24 | database: skool 25 | owner: skool 26 | secret: 27 | name: skool-db-creds 28 | description: Postgres cluster for the skool application 29 | imageName: quay.io/enterprisedb/postgresql:16.1 30 | inheritedMetadata: 31 | labels: 32 | app: skool-database 33 | policy-type: database 34 | instances: 3 35 | monitoring: 36 | enablePodMonitor: true 37 | resources: 38 | requests: 39 | memory: 100Mi 40 | storage: 41 | size: 1Gi 42 | 43 | --- 44 | apiVersion: postgresql.k8s.enterprisedb.io/v1 45 | kind: ScheduledBackup 46 | metadata: 47 | name: skool-db-production 48 | namespace: skool 49 | spec: 50 | backupOwnerReference: cluster 51 | cluster: 52 | name: skool-db-production-v0 53 | immediate: true 54 | schedule: 0 0 3 * * 55 | -------------------------------------------------------------------------------- /utils/database/kubernetes_config_template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: postgresql.k8s.enterprisedb.io/v1 3 | kind: Cluster 4 | metadata: 5 | name: "{prefix}-db-production-v0" 6 | namespace: "{prefix}" 7 | spec: 8 | backup: 9 | barmanObjectStore: 10 | azureCredentials: 11 | storageAccount: 12 | key: "{prefix}-storage-account-name" 13 | name: azure-creds 14 | storageSasToken: 15 | key: "{prefix}-blob-sas" 16 | name: azure-creds 17 | data: 18 | compression: gzip 19 | destinationPath: "https://{storage_account}.blob.core.windows.net/{prefix}" 20 | wal: 21 | compression: gzip 22 | retentionPolicy: 14d 23 | bootstrap: 24 | initdb: 25 | database: "{prefix}" 26 | owner: "{prefix}" 27 | secret: 28 | name: "{prefix}-db-creds" 29 | description: "Postgres cluster for the {prefix} application" 30 | imageName: quay.io/enterprisedb/postgresql:16.1 31 | inheritedMetadata: 32 | labels: 33 | app: "{prefix}-database" 34 | policy-type: database 35 | instances: 3 36 | monitoring: 37 | enablePodMonitor: true 38 | resources: 39 | requests: 40 | memory: 100Mi 41 | storage: 42 | size: 1Gi 43 | --- 44 | apiVersion: postgresql.k8s.enterprisedb.io/v1 45 | kind: ScheduledBackup 46 | metadata: 47 | name: "{prefix}-db-production" 48 | namespace: "{prefix}" 49 | spec: 50 | backupOwnerReference: cluster 51 | cluster: 52 | name: "{prefix}-db-production-v0" 53 | immediate: true 54 | schedule: "0 0 3 * *" 55 | -------------------------------------------------------------------------------- /utils/database/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from yaml_generator import create_kubernetes_config 4 | from setup_db import setup_database 5 | 6 | # Set up logging 7 | logging.basicConfig( 8 | level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" 9 | ) 10 | 11 | 12 | def main(): 13 | try: 14 | # Get inputs 15 | prefix = input("Enter the prefix for the new database: ").strip() 16 | key_vault_name = os.getenv("AZURE_KEY_VAULT_NAME", "k8s-homelab-production") 17 | conn_string = os.getenv("HL_AZ_CONN_STRING") 18 | 19 | # Check if conn_string is None or empty 20 | if not conn_string: 21 | raise ValueError( 22 | "HL_AZ_CONN_STRING environment variable is not set or is empty" 23 | ) 24 | 25 | # Setup database and get storage account name 26 | storage_account_name = setup_database(prefix, key_vault_name, conn_string) 27 | 28 | # Generate Kubernetes config 29 | script_dir = os.path.dirname(os.path.abspath(__file__)) 30 | template_path = os.path.join(script_dir, "kubernetes_config_template.yaml") 31 | params = { 32 | "prefix": prefix, 33 | "storage_account": storage_account_name, 34 | } 35 | kubernetes_config_path = create_kubernetes_config( 36 | template_path, params, script_dir 37 | ) 38 | 39 | logging.info( 40 | f"Process completed successfully. Kubernetes config: {kubernetes_config_path}" 41 | ) 42 | 43 | except ValueError as e: 44 | logging.error(f"Value error: {str(e)}") 45 | except Exception as e: 46 | logging.error(f"An error occurred in the main process: {str(e)}") 47 | 48 | 49 | if __name__ == "__main__": 50 | main() 51 | -------------------------------------------------------------------------------- /utils/database/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "database" 3 | version = "0.1.0" 4 | description = "Utilities to create databases for my homelab cluster" 5 | authors = ["Your Name "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.12" 10 | pyyaml = "^6.0.2" 11 | azure-core = "^1.30.2" 12 | azure-identity = "^1.17.1" 13 | azure-keyvault-secrets = "^4.8.0" 14 | azure-storage-blob = "^12.22.0" 15 | 16 | 17 | [build-system] 18 | requires = ["poetry-core"] 19 | build-backend = "poetry.core.masonry.api" 20 | -------------------------------------------------------------------------------- /utils/database/secrets_example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: secrets-store.csi.x-k8s.io/v1 2 | kind: SecretProviderClass 3 | metadata: 4 | name: azure-kv-secrets 5 | namespace: skool 6 | spec: 7 | provider: azure 8 | parameters: 9 | keyvaultName: k8s-homelab-production 10 | 11 | objects: | 12 | array: 13 | - | 14 | objectName: skool-connection-string 15 | objectType: secret 16 | - | 17 | objectName: skool-storage-account-name 18 | objectType: secret 19 | - | 20 | objectName: skool-blob-sas 21 | objectType: secret 22 | - | 23 | objectName: skool-db-username 24 | objectType: secret 25 | - | 26 | objectName: skool-db-password 27 | objectType: secret 28 | 29 | tenantId: 6ddecc48-41b1-48de-bfde-2efd29fae9c7 30 | 31 | secretObjects: 32 | - data: 33 | - key: skool-connection-string 34 | objectName: skool-connection-string 35 | - key: skool-storage-account-name 36 | objectName: skool-storage-account-name 37 | - key: skool-blob-sas 38 | objectName: skool-blob-sas 39 | secretName: azure-creds 40 | type: Opaque 41 | 42 | - data: 43 | - key: username 44 | objectName: skool-db-username 45 | - key: password 46 | objectName: skool-db-password 47 | secretName: skool-db-creds 48 | type: kubernetes.io/basic-auth 49 | -------------------------------------------------------------------------------- /utils/database/setup_db.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | import logging 4 | from datetime import datetime, timedelta, timezone 5 | from azure.identity import DefaultAzureCredential 6 | from azure.keyvault.secrets import SecretClient 7 | from azure.storage.blob import ( 8 | BlobServiceClient, 9 | generate_container_sas, 10 | ContainerSasPermissions, 11 | ) 12 | from azure.core.exceptions import AzureError 13 | 14 | 15 | def generate_password(length=16): 16 | return "".join( 17 | random.choice(string.ascii_letters + string.digits) for _ in range(length) 18 | ) 19 | 20 | 21 | def parse_connection_string(conn_string): 22 | return dict(part.split("=", 1) for part in conn_string.split(";") if part) 23 | 24 | 25 | def create_container_and_sas(conn_string, container_name): 26 | try: 27 | blob_service = BlobServiceClient.from_connection_string(conn_string) 28 | blob_service.create_container(container_name) 29 | logging.info(f"Container '{container_name}' created.") 30 | 31 | conn_parts = parse_connection_string(conn_string) 32 | account_name, account_key = ( 33 | conn_parts.get("AccountName"), 34 | conn_parts.get("AccountKey"), 35 | ) 36 | if not account_name or not account_key: 37 | raise ValueError( 38 | "Invalid connection string: missing AccountName or AccountKey" 39 | ) 40 | 41 | return generate_container_sas( 42 | account_name, 43 | container_name, 44 | account_key=account_key, 45 | permission=ContainerSasPermissions( 46 | read=True, 47 | write=True, 48 | delete=True, 49 | list=True, 50 | add=True, 51 | create=True, 52 | delete_previous_version=True, 53 | permanent_delete=True, 54 | set_immutability_policy=True, 55 | tag=True, 56 | ), 57 | expiry=datetime.now(timezone.utc) + timedelta(days=730), 58 | ) 59 | except AzureError as e: 60 | logging.error(f"Azure operation failed: {str(e)}") 61 | raise 62 | 63 | 64 | def create_key_vault_secrets(prefix: str, key_vault_name: str, conn_string: str) -> str: 65 | try: 66 | secret_client = SecretClient( 67 | vault_url=f"https://{key_vault_name}.vault.azure.net/", 68 | credential=DefaultAzureCredential(), 69 | ) 70 | container_name = prefix.lower() 71 | blob_sas = create_container_and_sas(conn_string, container_name) 72 | storage_account_name = parse_connection_string(conn_string).get("AccountName") 73 | 74 | if not storage_account_name: 75 | raise ValueError("Storage account name not found in connection string") 76 | 77 | secrets = { 78 | f"{prefix}-db-username": f"{prefix}", 79 | f"{prefix}-db-password": generate_password(), 80 | f"{prefix}-storage-account-name": storage_account_name, 81 | f"{prefix}-blob-sas": blob_sas, 82 | f"{prefix}-connection-string": conn_string, 83 | } 84 | 85 | for name, value in secrets.items(): 86 | secret_client.set_secret(name, value) 87 | logging.info(f"Secret '{name}' set in Key Vault") 88 | 89 | logging.info(f"All secrets created in Azure Key Vault for prefix: {prefix}") 90 | logging.info(f"Container '{container_name}' created with a 2-year SAS token") 91 | 92 | return storage_account_name 93 | except AzureError as e: 94 | logging.error(f"Azure operation failed: {str(e)}") 95 | raise 96 | except ValueError as e: 97 | logging.error(str(e)) 98 | raise 99 | 100 | 101 | def setup_database(prefix: str, key_vault_name: str, conn_string: str) -> str: 102 | try: 103 | if not prefix: 104 | raise ValueError("Prefix cannot be empty") 105 | if not conn_string: 106 | raise ValueError("Connection string is not provided") 107 | return create_key_vault_secrets(prefix, key_vault_name, conn_string) 108 | except ValueError as e: 109 | logging.error(str(e)) 110 | raise 111 | except Exception as e: 112 | logging.error(f"An unexpected error occurred: {str(e)}") 113 | raise 114 | -------------------------------------------------------------------------------- /utils/database/yaml_generator.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import os 3 | import logging 4 | from typing import List, Any, Dict 5 | from string import Formatter 6 | 7 | 8 | def load_template(file_path: str) -> List[Any]: 9 | try: 10 | with open(file_path, "r") as file: 11 | return list(yaml.safe_load_all(file)) 12 | except FileNotFoundError: 13 | logging.error(f"Template file not found: {file_path}") 14 | raise 15 | except yaml.YAMLError as e: 16 | logging.error(f"Error parsing YAML template: {e}") 17 | raise 18 | 19 | 20 | def process_template( 21 | template: Dict[str, Any], params: Dict[str, Any] 22 | ) -> Dict[str, Any]: 23 | def replace_placeholders(obj: Any) -> Any: 24 | if isinstance(obj, dict): 25 | return {k: replace_placeholders(v) for k, v in obj.items()} 26 | elif isinstance(obj, list): 27 | return [replace_placeholders(item) for item in obj] 28 | elif isinstance(obj, str): 29 | return Formatter().vformat(obj, (), params) 30 | return obj 31 | 32 | return replace_placeholders(template) 33 | 34 | 35 | def generate_yaml_configs(template_path: str, params: Dict[str, Any]) -> str: 36 | try: 37 | templates = load_template(template_path) 38 | processed_configs = [ 39 | process_template(template, params) for template in templates 40 | ] 41 | return "\n---\n".join( 42 | yaml.dump(config, default_flow_style=False) for config in processed_configs 43 | ) 44 | except Exception as e: 45 | logging.error(f"Error generating YAML configs: {e}") 46 | raise 47 | 48 | 49 | def create_kubernetes_config( 50 | template_path: str, params: Dict[str, Any], output_dir: str 51 | ) -> str: 52 | try: 53 | yaml_configs = generate_yaml_configs(template_path, params) 54 | output_path = os.path.join( 55 | output_dir, f"{params['prefix']}_kubernetes_configs.yaml" 56 | ) 57 | with open(output_path, "w") as f: 58 | f.write(yaml_configs) 59 | logging.info(f"YAML configurations have been written to {output_path}") 60 | return output_path 61 | except Exception as e: 62 | logging.error(f"An error occurred: {e}") 63 | raise 64 | --------------------------------------------------------------------------------