Deploying Home Assistant on Raspberry Pi 5 using K3s
This guide walks you through deploying Home Assistant on a Raspberry Pi 5. We will use K3s, a lightweight Kubernetes distribution designed for IoT and Edge computing.
 |
| This is Kubernetes concepts. |
Why K3s and Docker?
K3s vs. K8s: Standard Kubernetes can be heavy. K3s is optimized for ARM processors like the Raspberry Pi. In K3s, the "Master" node is called the Server, and the "Worker" node is called the Agent.
Networking: We chose K3s because its networking model is often simpler for beginners to grasp than Minikube.
Runtime: K3s defaults to "containerd". However, for this guide, we will force K3s to use Docker as the container runtime. This is often preferred for Home Assistant to ensure compatibility with certain add-ons and easier image management.
Prerequisites:
Since we want K3s to use Docker, we must install the Docker Engine first.
1.Set up the repository:
# Add Docker's official GPG key:
$sudo apt update
$sudo apt install ca-certificates curl
$sudo install -m 0755 -d /etc/apt/keyrings
$sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
$sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
$sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
$sudo apt update
2.Install the Docker packages.
$sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
3.Verify that the installation is successful by running the hello-world image:
$sudo docker run hello-world
Step 2: Install K3s with Docker Support
We run the installation script with two critical flags:
--docker: Tells K3s to use the Docker daemon we just installed instead of containerd.
--disable traefik Disables the default ingress controller (optional, but saves resources if you aren't using it).
$curl -sfL https://get.k3s.io | sh -s - --docker --disable traefik
Verify K3s is running:
$sudo k3s kubectl get nodes
Show all running pods/services/deployments
$sudo k3s kubectl get all --all-namespaces
Step 3: Create Kubernetes Manifests (YAML)
We need to define the resources for Home Assistant. Save the following blocks into separate .yaml files.
1. Namespace (ha_namespace.yaml)
apiVersion: v1
kind: Namespace
metadata:
name: home-assistant
2. Storage (ha_storage.yaml)
Home Assistant needs a place to save your configuration data so it doesn't vanish when the pod restarts.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ha-config
namespace: home-assistant
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 10Gi
3. Deployment (ha_deployment.yaml)
This is the core logic.
Critical Setting: hostNetwork: true. This allows Home Assistant to see the Raspberry Pi's physical network interface directly, which is required for discovering devices (like Google Cast, HomeKit, etc.) on your network.
apiVersion: apps/v1
kind: Deployment
metadata:
name: home-assistant
namespace: home-assistant
labels:
app: home-assistant
spec:
replicas: 1 # DO NOT CHANGE: Home Assistant does not support horizontal scaling
selector:
matchLabels:
app: home-assistant
template: # This is the pod template
metadata: # Metadata for the pod
labels: # Labels for the pod
app: home-assistant
spec:
# hostNetwork must be FALSE for LoadBalancer logic to apply
# Key setting for IoT discovery
hostNetwork: true
containers:
- name: home-assistant
image: homeassistant/home-assistant:stable
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8123
name: http
volumeMounts:
- name: config
mountPath: /config
- name: localtime
mountPath: /etc/localtime
readOnly: true
env:
- name: TZ
value: "America/Phoenix"
volumes:
- name: config
persistentVolumeClaim:
claimName: ha-config
- name: localtime
hostPath:
path: /etc/localtime
4. Service (ha_service.yaml)
Defines the internal network service.
Note: Because we used hostNetwork: true, the pod is already exposed on the host's IP. This service object is mostly for internal cluster DNS.
apiVersion: v1
kind: Service
metadata:
name: home-assistant-svc
namespace: home-assistant
spec:
type: ClusterIP
selector:
app: home-assistant
ports:
- name: http
protocol: TCP
port: 8123
Step 4: Deploy and Access
1. Apply the configurations:
$kubectl apply -f ha_namespace.yaml
$kubectl apply -f ha_storage.yaml
$kubectl apply -f ha_deployment.yaml
$kubectl apply -f ha_service.yaml
2. Check the status:
Wait for the status to show "Running".
$kubectl get all --all-namespaces
3. Access Home Assistant:
Open your browser and navigate to:
http://<your raspberry pi IP address>:8123
- This is what the home assistant needed when it's installed in a docker compose. I wrote the ha_storage.yaml and ha_deployment.yaml base on the requirement.

In the ha_deployment.yaml , you will notice we set hostNetwork: true. This is different from standard web server deployments.
Standard K8s Networking: Usually, a pod gets its own isolated IP address inside the cluster. You then use a Service or Ingress to expose it.
The IoT Problem: Smart home protocols (like mDNS/Bonjour for HomeKit, or UPnP) rely on broadcasting messages to the local network to "find" devices. If the pod is isolated inside the cluster network, it cannot hear these broadcasts.
The Solution: hostNetwork: true removes the network isolation.
Shares Host IP: The pod uses the Raspberry Pi's actual IP address.
Visible Interfaces: The pod can see eth0 or wlan0 directly, allowing it to scan your home network for smart plugs, bulbs, and switches.
Useful Management Commands
Delete a namespace
$kubectl delete namespace home-assistant
Uninstall K3s entirely:
$/usr/local/bin/k3s-uninstall.sh
Reference:
Comments