Using Devspace to stand up a Kubernetes development environment for HomeAssistant



My aging laptop does not get on with the HomeAssistant devcontainer development environment - it’s a SurfacePro 7 with the i5-1035G4 and 16GB RAM but as much as I like the touch screen, the mobile i5 chip seems underpowered. As an aside, compared to an iPad the SurfacePro doesn’t really work for me as tablet either.

As a result, I was after a lighter way to develop simply dashboards and custom modules for HomeAssistant without using the “production” install.

DevSpace?

Somehow I stumbled across DevSpace and it fits the bill perfectly. As an introduction, DevSpace is a very lightweight, client-only, open-source developer tool for Kubernetes. DevSpace is a Cloud Native Computing Foundation (CNCF) sandbox project contributed by Loft Labs

How does DevSpace work?

DevSpace runs as a single binary CLI tool directly on your computer and ideally, you use it straight from the terminal within your IDE. DevSpace does not require a server-side component as it communicates directly to your Kubernetes cluster using your kube-context, just like kubectl.

Creating a HomeAssistant development environment with DevSpace

There is a great Getting Started guide for DevSpace on their website. Rather than repeating that guide here, I’m going to describe the setting up the environment for HomeAssistant, inside Kubernetes.

Getting started

First, we need to download DevSpace. There are a few options. I’m using yarn to install inside a Windows WSL image. VS Code is my primary development IDE.

yarn global add devspace

Create a new folder to sync with the development container

We can use this folder to synchronise a HomeAssistant dashboard project development container, but this could be any code. To use an existing project, clone a git repo instead

mkdir -p ~/battery-card/src
mkdir -p ~/battery-card/dist
# or: cd ~ && git clone https://git.example.com/battery-card
cd ~/battery-card

Initialize the DevSpace project

Use DevSpace CLI and follow the wizard.

devspace init

The outcome will be a yaml configuration file. During the wizard, we will want to select:

The wizard will create two files. devspace.yaml which is the main configuration file and devspace_start.sh which is the default entry script for the container. We are only going to use devspace.yaml .

Customise the devspace.yaml configuration

I’ll first share the final configuration and then break this configuration down. I’m not touching the pipelines, images or commands sections so consider this a proof of concept post!

version: v2beta1
name: homeassistant

# This is a list of `pipelines` that DevSpace can execute (you can define your own)
pipelines:
  # This is the pipeline for the main command: `devspace dev` (or `devspace run-pipeline dev`)
  dev:
    run: |-
      run_dependencies --all       # 1. Deploy any projects this project needs (see "dependencies")
      ensure_pull_secrets --all    # 2. Ensure pull secrets
      create_deployments --all     # 3. Deploy Helm charts and manifests specfied as "deployments"
      start_dev app                # 4. Start dev mode "app" (see "dev" section)      
  # You can run this pipeline via `devspace deploy` (or `devspace run-pipeline deploy`)
  deploy:
    run: |-
      run_dependencies --all                            # 1. Deploy any projects this project needs (see "dependencies")
      ensure_pull_secrets --all                         # 2. Ensure pull secrets
      build_images --all -t $(git describe --always)    # 3. Build, tag (git commit hash) and push all images (see "images")
      create_deployments --all                          # 4. Deploy Helm charts and manifests specfied as "deployments"      

# This is a list of `images` that DevSpace can build for this project
# We recommend to skip image building during development (devspace dev) as much as possible
images:
  app:
    image: username/app
    dockerfile: ./Dockerfile

# This is a list of `deployments` that DevSpace can create for this project
deployments:
  app:
    # This deployment uses `helm` but you can also define `kubectl` deployments or kustomizations
    helm:
      # We are deploying this project with the Helm chart you provided
      chart:
        name: component-chart
        repo: https://charts.devspace.sh
      # Under `values` we can define the values for this Helm chart used during `helm install/upgrade`
      # You may also use `valuesFiles` to load values from files, e.g. valuesFiles: ["values.yaml"]
      values:
        containers:
          - image: username/app
            volumeMounts:
              - containerPath: /config
                volume:
                  name: hass-config-dev
        service:
          ports:
            - port: 8123
        volumes:
          - name: hass-config-dev
            storageClassName: ceph-filesystem
            size: 1Gi

# This is a list of `dev` containers that are based on the containers created by your deployments
dev:
  app:
    # Search for the container that runs this image
    imageSelector: username/app
    # Replace the container image with this dev-optimized image (allows to skip image building during development)
    devImage: ghcr.io/home-assistant/home-assistant:2024.11
    # Sync files between the local filesystem and the development container
    sync:
      - path: ./dist:/config/www/battery-card
    # Open a terminal and use the following command to start it
    terminal:
      command: /bin/bash
      # https://www.devspace.sh/docs/configuration/dev/modifications/entrypoint
      disableReplace: true
    # Inject a lightweight SSH server into the container (so your IDE can connect to the remote dev env)
    ssh:
      enabled: true
    # Make the following commands from my local machine available inside the dev container
    # proxyCommands:
    #   - command: devspace
    #   - command: kubectl
    #   - command: helm
    #   - gitCredentials: true
    # Forward the following ports to be able access your application via localhost
    ports:
      - port: "8123"
    # Open the following URLs once they return an HTTP status code other than 502 or 503
    open:
      - url: http://localhost:8123

# Use the `commands` section to define repeatable dev workflows for this project 
commands:
  migrate-db:
    command: |-
      echo 'This is a cross-platform, shared command that can be used to codify any kind of dev task.'
      echo 'Anyone using this project can invoke it via "devspace run migrate-db"'

The deployments section in devspace.yaml defines Helm charts, Kubernetes manifests and Kustomizations that can be deployed using the create_deployments function which will be executed when we call devspace dev. This will create one deployment called app. There are only a couple of things to mention:

  1. We can mount a PVC into the container to give persistent storage. For many development environments this might not be needed but here it provides a place to persist the HomeAssistant configuration.
  2. Secondly, a service will be created to expost port 8123. Eventually we will be able to access this from http://localhost:8123 .
# This is a list of `deployments` that DevSpace can create for this project
deployments:
  app:
    # This deployment uses `helm` but you can also define `kubectl` deployments or kustomizations
    helm:
      # We are deploying this project with the Helm chart you provided
      chart:
        name: component-chart
        repo: https://charts.devspace.sh
      # Under `values` we can define the values for this Helm chart used during `helm install/upgrade`
      # You may also use `valuesFiles` to load values from files, e.g. valuesFiles: ["values.yaml"]
      values:
        containers:
          - image: username/app
            volumeMounts:
              - containerPath: /config
                volume:
                  name: hass-config-dev
        service:
          ports:
            - port: 8123
        volumes:
          - name: hass-config-dev
            storageClassName: ceph-filesystem
            size: 1Gi

The dev section defines containers that are central to the DevSpace-based development experience. A dev container serves as a remote work environment that is connected to the local machine via port forwarding, file sync, etc.

There are a few highlights here:

  1. devImage specifies the base container for the development environment. loft provide some default images but we can actually use anything we want here. Before I understood some of the other options in this section I thought I might need to create a custom image but that isn’t the case. We can just use one of the standard home-assistant published images.
  2. sync defines the folders to synchronise between the local filesystem and the development container. More on this in a bit
  3. command defines the entry point for the container. The default will be the devspace_start.sh script, but we can change this to give a bash prompt.

One thing to note here - if you enable the terminal for this dev container, DevSpace will assume that you manually want to start your application using the terminal session and it automatically changes the container entrypoint to sleep. To disable the automatic modifications of the entrypoint for terminal-enabled dev containers, we need to add disableReplace: true under the terminal settings.

# This is a list of `dev` containers that are based on the containers created by your deployments
dev:
  app:
    # Search for the container that runs this image
    imageSelector: username/app
    # Replace the container image with this dev-optimized image (allows to skip image building during development)
    devImage: ghcr.io/home-assistant/home-assistant:2024.11
    # Sync files between the local filesystem and the development container
    sync:
      - path: ./dist:/config/www/battery-card
    # Open a terminal and use the following command to start it
    terminal:
      command: /bin/bash
      # https://www.devspace.sh/docs/configuration/dev/modifications/entrypoint
      disableReplace: true
    # Inject a lightweight SSH server into the container (so your IDE can connect to the remote dev env)
    ssh:
      enabled: true
    # Make the following commands from my local machine available inside the dev container
    # proxyCommands:
    #   - command: devspace
    #   - command: kubectl
    #   - command: helm
    #   - gitCredentials: true
    # Forward the following ports to be able access your application via localhost
    ports:
      - port: "8123"
    # Open the following URLs once they return an HTTP status code other than 502 or 503
    open:
      - url: http://localhost:8123

Preparting to start the Dev Container - Choose Cluster & Namespace

DevSpace will detect available clusters for us to select from. We also need to tell DevSpace which namespace to use.

devspace use context                  # to select the right Kubernetes cluster
devspace use namespace devspace   # will be automatically created during deployment

Start Dev Container

To spin up the dev container we just run:

devspace dev

If needed, this will create the namespace defined above (devspace) and deploy the application containers. Files from ./dist will also be synchronised to /config/www/battery-card and port 8123 will be forwarded from localhost to the container.

We can access this new HomeAssistant instance at http://localhost:8123.

Creating our new HomeAssistant dashboard

This post is already quite long so I am not going go into too much detail about the HomeAssistant dashboard. I’ve been following the tutorial at https://github.com/home-assistant-tutorials/09.toggle-card-lit . In our project directory on localhost we need to install lit so we run:

# Change to the folder if not there already
cd ~/battery-card
npm install --save-dev parcel
npm install lit

A full install of these two modules is over 300MB. Luckily, we don’t actually need ot synchronise everything with the dev container. lit will combine the source files from ./src into ./dist . We actually only need to synchronise ./dist with the container which (in my case) will be two files! The rest of the node_modules install can stay on the local machine.

At this point we are ready to develop - everything we need is up and running.


Hope this is useful, thanks for reading!