<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Homelab on Ceald's Blog</title><link>https://ceald.cc/tags/homelab/</link><description>Recent content in Homelab on Ceald's Blog</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Tue, 11 Aug 2026 09:53:39 -0700</lastBuildDate><atom:link href="https://ceald.cc/tags/homelab/index.xml" rel="self" type="application/rss+xml"/><item><title>Homelab</title><link>https://ceald.cc/posts/homelab/</link><pubDate>Tue, 11 Aug 2026 09:53:39 -0700</pubDate><guid>https://ceald.cc/posts/homelab/</guid><description>&lt;h1 id="background"&gt;Background&lt;/h1&gt;
&lt;p&gt;After messing with K3S for a bit I was kind of sick of dealing with the lack of resources on a PI 4 so I brought it to my laptop.&lt;/p&gt;
&lt;h1 id="so-we-begin"&gt;So We Begin!&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://media1.tenor.com/m/ns6SqcQI7WwAAAAC/here-we-go-joker.gif" alt="alt text"&gt;&lt;/p&gt;
&lt;p&gt;First and most important step is to pick the Kubernetes distribution for the right job, I went with K3S for because it comes with traefik but I&amp;rsquo;d use something like RKE2 or Kubeadm instead because of the customizability next time.&lt;/p&gt;
&lt;p&gt;Second is to pick a CNI/Network plugin for Kubernetes, depending on the distribution it might already come with one or maybe you&amp;rsquo;d want to switch it out. I personally used Cilium because of its observability and Hubble UI. Without a CNI your cluster won&amp;rsquo;t be able to communicate with anything inside or outside it.&lt;/p&gt;
&lt;p&gt;Third is to pick a storage class for persistence, I went with longhorn because of the frontend for it and flexability, longhorn is also distributed/replicated rather than just being local on a node.&lt;/p&gt;
&lt;p&gt;Fourth it&amp;rsquo;d be nice to have a frontend for managing everything. Something like either rancher or headlamp would be best. I went with headlamp because of how lightweight it is. The rest after this is optional but if you want the most Kuber Kubernetes cluster it&amp;rsquo;d be a good idea to set these up too.&lt;/p&gt;
&lt;h2 id="side-note"&gt;Side Note&lt;/h2&gt;
&lt;p&gt;The next services that are going to be setup is overkill for a basic homelab but this is an elitist homelab so nothing is overkill!&lt;/p&gt;
&lt;h2 id="other-services"&gt;Other services&lt;/h2&gt;
&lt;p&gt;Getting metrics with grafana is almost always a must so installing the kubestack is a good idea and loki. These were my values.yaml files:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;# kube-stack-values.yaml
USER-SUPPLIED VALUES:
grafana:
additionalDataSources: []
persistence:
accessModes:
- ReadWriteOnce
enabled: true
size: 10Gi
storageClassName: longhorn
type: pvc
resources:
# Main Grafana container
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
persistence:
storageClass: longhorn
prometheus:
prometheusSpec:
scrapeInterval: 30s
serviceMonitorSelectorNilUsesHelmValues: false
serviceMonitorSelector: {}
serviceMonitorNamespaceSelector: {}
podMonitorSelectorNilUsesHelmValues: false
podMonitorSelector: {}
podMonitorNamespaceSelector: {}
storageSpec:
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: longhorn
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;and loki:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;# loki-values.yaml
loki:
commonConfig:
replication_factor: 1
auth_enabled: false
isDefault: false
schemaConfig:
configs:
- from: &amp;#34;2024-01-01&amp;#34;
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: loki_index_
period: 24h
storage:
type: filesystem
bucketNames:
chunks: chunks
ruler: ruler
admin: admin
limits_config:
allow_structured_metadata: true
deploymentMode: SingleBinary
singleBinary:
replicas: 1
gateway:
enabled: false
read:
replicas: 0
write:
replicas: 0
backend:
replicas: 0
querier:
replicas: 0
queryFrontend:
replicas: 0
queryScheduler:
replicas: 0
ingester:
replicas: 0
distributor:
replicas: 0
compactor:
replicas: 0
lokiCanary:
enabled: false
chunksCache:
enabled: false
resultsCache:
enabled: false
test:
enabled: false
monitoring:
dashboards:
enabled: false
rules:
enabled: false
serviceMonitor:
enabled: false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;another thing that would be great for observability is falco! Here&amp;rsquo;s my values.yaml file:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;# falco-values.yaml
json_output: true
json_include_output_property: true
http_output:
enabled: true
url: &amp;#34;http://falco-falcosidekick:2801/&amp;#34;
falcosidekick:
enabled: true
config:
loki:
hostport: http://loki.monitoring.svc.cluster.local:3100
format: json
falcoctl:
config:
artifact:
install:
refs:
- falco-rules:5
- falco-incubating-rules:2
- falco-sandbox-rules:2
follow:
refs:
- falco-rules:5
- falco-incubating-rules:2
- falco-sandbox-rules:2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;falco is a runtime security tool that detects policy violations like for example reading &lt;code&gt;/etc/shadow&lt;/code&gt; and can return an error early using eBPFs.&lt;/p&gt;
&lt;p&gt;you&amp;rsquo;ll get metrics and logs exported so you can view them in grafana.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s probably apparent that you won&amp;rsquo;t be able to access anything because you don&amp;rsquo;t have any ingresses setup in the cluster yet, that&amp;rsquo;s where kyverno and cert manager will come into play.&lt;/p&gt;
&lt;p&gt;Install kyverno and cert manager then create a cluster issuer and everything else for cert manager like:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: cert-manager
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: orbit-ca
namespace: cert-manager
spec:
isCA: true
commonName: orbit-ca
secretName: orbit-ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: orbit-ca-issuer
spec:
ca:
secretName: orbit-ca-secret&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;this issuer is cluster wide btw so you can get certs signed from this issuer anywhere.&lt;/p&gt;
&lt;p&gt;For kyverno policy:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: force-external-dns-target
spec:
admission: true
background: true
emitWarning: false
mutateExistingOnPolicyUpdate: true
rules:
- match:
any:
- resources:
kinds:
- Ingress
mutate:
patchStrategicMerge:
metadata:
annotations:
cert-manager.io/cluster-issuer: orbit-ca-issuer
external-dns.alpha.kubernetes.io/target: 127.0.0.1
targets:
- apiVersion: networking.k8s.io/v1
kind: Ingress
name: set-target-annotation
skipBackgroundRequests: true
validationFailureAction: Audit&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;You don&amp;rsquo;t need the external dns annotation in the policy if you&amp;rsquo;re not going to use external dns but if you do it&amp;rsquo;d be a good idea to also get etcd or a source for external dns to write to.&lt;/p&gt;
&lt;p&gt;The policy will mutate all your ingresses to have the orbit-ca-issuer added so you don&amp;rsquo;t need to manually add it everytime you make an ingress.&lt;/p&gt;
&lt;p&gt;Now for your first ingress on the cluster, you&amp;rsquo;d write something like this:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
name: prometheus-grafana-ingress
namespace: monitoring
spec:
ingressClassName: traefik
rules:
- host: dashboard.orbit.orbit
http:
paths:
- backend:
service:
name: prometheus-grafana
port:
number: 80
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- dashboard.orbit.orbit
secretName: grafana-tls&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;If you get that ingress like: &lt;code&gt;kubectl get ingress -n monitoring -o yaml&lt;/code&gt; you&amp;rsquo;ll notice that it&amp;rsquo;s been changed! If not then add this cluster role:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno-background-controller-namespace
labels:
rbac.kyverno.io/aggregate-to-background-controller: &amp;#34;true&amp;#34;
rules:
- apiGroups:
- &amp;#34;&amp;#34;
resources:
- namespaces
verbs:
- get
- list
- watch
- update
- patch&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;Nice, not even halfway done yet! I won&amp;rsquo;t post ALL my ingresses because that&amp;rsquo;d be insane.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://media1.tenor.com/m/wGufiBV_pI0AAAAC/hide-the-pain-harold-pain.gif" alt="alt text"&gt;&lt;/p&gt;
&lt;p&gt;Time to get some services for security going for enforcement, security posture, and observability.&lt;/p&gt;
&lt;h3 id="falco"&gt;Falco&lt;/h3&gt;
&lt;p&gt;here&amp;rsquo;s my falco dashboard if you chose to use flaco:
&lt;a href="./homelab/grafana-dash.json"&gt;dashboard file&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="kubescape"&gt;Kubescape&lt;/h3&gt;
&lt;p&gt;Kubescape is a tool for security posture and allows for vulnerability scans on images, here&amp;rsquo;s my helm installation command for it:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;bash&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;helm upgrade --install kubescape kubescape/kubescape-operator \
-n kubescape \
--create-namespace \
--set capabilities.continuousScan=enable \
--set capabilities.prometheusExporter=enable \
--set kubescape.serviceMonitor.enabled=false \
--set clusterName=default&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;you&amp;rsquo;ll notice that the service monitor is disabled that&amp;rsquo;s because it&amp;rsquo;s actually broken and does not work with newer versions of the kubestack, here&amp;rsquo;s the manifest I used to create one:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;# kubescape-monitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: kubescape-prometheus-exporter
namespace: kubescape
spec:
namespaceSelector:
matchNames:
- kubescape
selector:
matchLabels:
app.kubernetes.io/component: prometheus-exporter
app.kubernetes.io/instance: kubescape
app.kubernetes.io/name: kubescape-operator
endpoints:
- targetPort: 8080
path: /metrics
interval: 30s
scrapeTimeout: 10s&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;h3 id="istio"&gt;Istio&lt;/h3&gt;
&lt;p&gt;A service mesh like istio is always nice to have for encrypted communication between pods and services, here&amp;rsquo;s how I set it up:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;bash&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;istioctl install --set components.ingressGateways[0].name=istio-ingressgateway --set components.ingressGateways[0].enabled=false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;then for the grafana dashboards:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;bash&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;istioctl dashboard grafana -n monitoring&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;create a policy:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: PERMISSIVE # normally STRICT is preferred but PERMISSIVE won&amp;#39;t make any breakages if any occur like excluded namespaces communicating to ones that have mtls.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;finally make a cluster policy for applying it to selected namespaces:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-istio-injection-label-all
spec:
mutateExistingOnPolicyUpdate: true
rules:
- name: label-all-namespaces
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
kinds:
- Namespace
names:
- kube-system
- kube-public
- kube-node-lease
- istio-system
- longhorn-system
- cilium-secrets
- cilium-monitoring
- cattle-system
- cert-manager
- kyverno
- kubevirt
- cdi
- homepage
mutate:
targets:
- apiVersion: v1
kind: Namespace
preconditions:
all:
- key: &amp;#34;{{ target.metadata.name }}&amp;#34;
operator: AnyNotIn
value:
- kube-system
- kube-public
- kube-node-lease
- istio-system
- longhorn-system
- cilium-secrets
- cilium-monitoring
- cattle-system
- cert-manager
- kyverno
- kubevirt
- cdi
- homepage
patchStrategicMerge:
metadata:
labels:
istio-injection: enabled
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: remove-istio-injection-label-from-excluded
spec:
mutateExistingOnPolicyUpdate: true
rules:
- name: unlabel-excluded-namespaces
match:
any:
- resources:
kinds:
- Namespace
names:
- kube-system
- kube-public
- kube-node-lease
- istio-system
- longhorn-system
- cilium-secrets
- cilium-monitoring
- cattle-system
- cert-manager
- kyverno
- kubevirt
- cdi
- homepage
mutate:
targets:
- apiVersion: v1
kind: Namespace
preconditions:
all:
- key: &amp;#34;{{ target.metadata.name }}&amp;#34;
operator: AnyIn
value:
- kube-system
- kube-public
- kube-node-lease
- istio-system
- longhorn-system
- cilium-secrets
- cilium-monitoring
- cattle-system
- cert-manager
- kyverno
- kubevirt
- cdi
- homepage
patchStrategicMerge:
metadata:
labels:
istio-injection: null&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;There needs to be one that reverts the mutation because for some reason kyverno doesn&amp;rsquo;t fully exclude namespaces properly&lt;/p&gt;
&lt;h3 id="kubevirt"&gt;Kubevirt&lt;/h3&gt;
&lt;p&gt;The last service I&amp;rsquo;ll be going over is setting up kubevirt with an Azure Linux vm. Azure Linux is essentially trashy Fedora with half the packages.&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;bash&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;export RELEASE=$(curl https://storage.googleapis.com/kubevirt-prow/release/kubevirt/kubevirt/stable.txt)
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-operator.yaml
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-cr.yaml
kubectl -n kubevirt wait kv kubevirt --for condition=Available
export TAG=$(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest)
export VERSION=$(echo ${TAG##*/})
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;p&gt;This script will install kubevirt and now to make a VM!&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;yaml&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
finalizers:
- kubevirt.io/virtualMachineControllerFinalize
name: test
namespace: default
spec:
dataVolumeTemplates:
- metadata:
name: test-boot-volume
spec:
source:
http:
url: https://aka.ms/azurelinux-4.0-x86_64.iso
storage:
resources:
requests:
storage: 1Gi
- metadata:
name: test-target-disk
spec:
source:
blank: {}
storage:
resources:
requests:
storage: 50Gi
storageClassName: longhorn
runStrategy: Halted
template:
metadata:
annotations:
kubevirt.io/pci-topology-version: v3
spec:
architecture: amd64
domain:
cpu:
cores: 2
devices:
autoattachGraphicsDevice: true
disks:
- bootOrder: 300
disk:
bus: sata
name: test-boot-volume
- disk:
bus: virtio
name: cloudinitdisk
- bootOrder: 1
disk:
bus: virtio
name: disk-1
interfaces:
- masquerade: {}
name: default
firmware:
bootloader:
efi:
persistent: true
secureBoot: false
serial: 774cc0a1-2a83-488a-b17a-a6c3e8cc251f
uuid: 21e8b2bb-7b44-4b47-8e5e-5915aa02c562
machine:
type: q35
resources:
requests:
memory: 2Gi
networks:
- name: default
pod: {}
volumes:
- dataVolume:
name: test-boot-volume
name: test-boot-volume
- cloudInitNoCloud:
userData: |
#cloud-config
name: cloudinitdisk
- dataVolume:
name: test-target-disk
name: disk-1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;h3 id="kiali"&gt;Kiali&lt;/h3&gt;
&lt;p&gt;Kiali is a ui for istio&amp;rsquo;s service mesh so you can have observability into pods that have the proxy attached for mtls, here&amp;rsquo;s how to set up:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://ceald.cc/css/style_code.css"&gt;
&lt;div class="code-block"&gt;
&lt;div class="code-header"&gt;bash&lt;/div&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.30/samples/addons/prometheus.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.30/samples/addons/grafana.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.30/samples/addons/kiali.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js" defer&gt;&lt;/script&gt;
&lt;h2 id="final-product"&gt;Final Product!&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a diagram of the final product:&lt;/p&gt;
&lt;div style="
max-width: 100%;
height: 500px;
overflow: auto;
border: 2px solid var(--peach);
border-radius: 2px;
margin: 1.5rem 0;
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.6), 0 0 20px rgba(255, 246, 143, 0.3);
position: relative;
display: block;
box-sizing: border-box;
"&gt;
&lt;pre class="mermaid" style="
display: inline-block;
min-width: 100%;
"&gt;
%%{init: {
&amp;#34;flowchart&amp;#34;: {
&amp;#34;useMaxWidth&amp;#34;: false,
&amp;#34;nodeSpacing&amp;#34;: 100,
&amp;#34;rankSpacing&amp;#34;: 120,
&amp;#34;padding&amp;#34;: 20,
&amp;#34;htmlLabel&amp;#34;: true,
&amp;#34;defaultRenderer&amp;#34;: &amp;#34;dagre&amp;#34;
}
}}%%
flowchart LR
cil[&amp;#34;Cilium eBPF&amp;#34;]
subgraph kube_system[&amp;#34;kube-system namespace&amp;#34;]
h_ui[&amp;#34;Hubble UI&amp;#34;]
cdns[&amp;#34;Core DNS&amp;#34;]
ed[&amp;#34;etcd DNS&amp;#34;]
cagent[&amp;#34;Cilium Agent&amp;#34;]
hl[&amp;#34;headlamp&amp;#34;]
h_ui --&amp;gt;|&amp;#34;reads from&amp;#34;| cagent
cdns --&amp;gt;|&amp;#34;reads from&amp;#34;| ed
end
subgraph scape[&amp;#34;Kubescape namespace&amp;#34;]
kscape[&amp;#34;Kubescape&amp;#34;]
scapeStorage[&amp;#34;Kubescape storage&amp;#34;]
scapeEx[&amp;#34;Kubescape exporter&amp;#34;]
kscape --&amp;gt;|&amp;#34;logs data to&amp;#34;| scapeStorage
scapeEx --&amp;gt;|&amp;#34;reads from&amp;#34;| scapeStorage
end
subgraph monitoring[&amp;#34;monitoring namespace&amp;#34;]
graf[&amp;#34;grafana&amp;#34;]
lki[&amp;#34;loki&amp;#34;]
promo[&amp;#34;prometheus&amp;#34;]
graf --&amp;gt;|&amp;#34;reads from&amp;#34;| promo
graf --&amp;gt;|&amp;#34;reads from&amp;#34;| lki
promo --&amp;gt;|&amp;#34;scrapes metrics&amp;#34;| cdns
promo --&amp;gt;|&amp;#34;scrapes metrics&amp;#34;| cagent
promo --&amp;gt;|&amp;#34;scrapes data&amp;#34;| scapeEx
end
subgraph istio_system[&amp;#34;istio-system namespace&amp;#34;]
istio[&amp;#34;istio&amp;#34;]
isproxy[&amp;#34;istio proxy&amp;#34;]
kiali[&amp;#34;kiali&amp;#34;]
istio --&amp;gt;|&amp;#34;manages injections&amp;#34;| isproxy
kiali --&amp;gt;|&amp;#34;visualizations of&amp;#34;| istio
end
subgraph external_dns[&amp;#34;external-dns namespace&amp;#34;]
eDNS[&amp;#34;external dns&amp;#34;]
eDNS --&amp;gt;|&amp;#34;sends new entries to&amp;#34;| ed
end
subgraph falco_ns[&amp;#34;falco namespace&amp;#34;]
f[&amp;#34;falco&amp;#34;]
fside[&amp;#34;falco sidekick&amp;#34;]
f --&amp;gt;|&amp;#34;outputs to&amp;#34;| fside
promo --&amp;gt;|&amp;#34;scrapes from&amp;#34;| fside
end
subgraph longhorn_ns[&amp;#34;longhorn-system namespace&amp;#34;]
lmanage[&amp;#34;longhorn manager&amp;#34;]
csiPlug[&amp;#34;longhorn csi plugin&amp;#34;]
lui[&amp;#34;longhorn ui&amp;#34;]
csiPlug --&amp;gt;|&amp;#34;longhorn api&amp;#34;| lmanage
lui --&amp;gt;|&amp;#34;longhorn api&amp;#34;| lmanage
end
subgraph kyverno_ns[&amp;#34;kyverno namespace&amp;#34;]
kyv[&amp;#34;kyverno&amp;#34;]
end
subgraph default_ns[&amp;#34;default namespace&amp;#34;]
def_workloads[&amp;#34;Default Workloads&amp;#34;]
end
%% Cross-namespace / Global connections
cil --&amp;gt;|&amp;#34;observes kernel&amp;#34;| cagent &amp;amp; kscape &amp;amp; promo &amp;amp; kyv &amp;amp; def_workloads &amp;amp; eDNS &amp;amp; f &amp;amp; lmanage
isproxy --&amp;gt;|&amp;#34;mTLS sidecar&amp;#34;| kscape &amp;amp; promo &amp;amp; kyv &amp;amp; def_workloads &amp;amp; eDNS &amp;amp; f &amp;amp; lmanage
f --&amp;gt;|&amp;#34;observes syscalls&amp;#34;| cagent &amp;amp; kscape &amp;amp; promo &amp;amp; kyv &amp;amp; def_workloads &amp;amp; eDNS &amp;amp; lmanage
kyv --&amp;gt;|&amp;#34;enforces policies&amp;#34;| cagent &amp;amp; kscape &amp;amp; promo &amp;amp; def_workloads &amp;amp; eDNS &amp;amp; f &amp;amp; lmanage
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you have a homelab better than any Proxmox monolithic + NAS setup and can run everything in the based Kubernetes way with better security!&lt;/p&gt;</description></item></channel></rss>