Last time, we built a basic LXD container, and then build HomeAssistant inside.
This time, we're going to add a few more elements.
- We're going to do all the steps on the Host instead of diving inside the container. So we're going to use lxc exec and lxc push. The goal is to make spinning up a new container scriptable
- We're going to start/stop the HomeAssistant application using a systemd service
- We're going to keep the data and config outside the container and use an lxd disk device to mount the data. Even if we destroy the container, the data and config survive to be mounted another day.
Preparing LXD
We're going to skip LXD initialization in this example. There's one addition from last time: We're going to add shiftfs, which permits us to chown mounted data. The macvlan profile and shiftfs enablement are persistent -- if you already have them, you don't need to redo them. All of these commands occur on the Host (we have not created the container yet!)
# Create a macvlan profile, so the container will get it's IP Address from # the router instead of the host. This works on ethernet, but often not on wifi ip route show default 0.0.0.0/0 lxc profile copy default lanprofile lxc profile device set lanprofile eth0 nictype macvlan lxc profile device set lanprofile eth0 parent enp3s5 # Test that macvlan networking is set up lxc profile show lanprofile config: {} description: Default LXD profile // Copied. Not really the default devices: eth0: // Name, not real device nictype: macvlan // Correct network type parent: enp3s5 // Correct real device type: nic # Enable shiftfs in LXD so data mounts work properly sudo snap set lxd shiftfs.enable=true sudo systemctl reload snap.lxd.daemon # Test that shiftfs is enabled: Host$ lxc info | grep shiftfs shiftfs: "true"
Create the Container and Initial Configuration
If LXD is already set up, then start here. We will mount the external data location, set the timezone and do all that apt setup. But this time, we will do all the commands on the Host instead of inside the container. We will also create the sources.list file on the host and push it into the container.
# Create the container named "ha" lxc launch -p lanprofile ubuntu:focal ha # Mount the existing HomeAssistant data directory # Skip on the first run, since there won't be anything to mount # Shiftfs is needed, else the mounted data is owned by nobody:nogroup # Chown is needed because shiftfs changes the owner to 'ubuntu' lxc config device add ha data_mount disk source=/somewhere/else/.homeassistant path=/root/ha_data lxc config device set ha data_mount shift=true lxc exec ha -- chown -R root:root /root # Set the timezone non-interactively lxc exec ha -- ln -fs /usr/share/zoneinfo/US/Central /etc/localtime lxc exec ha -- dpkg-reconfigure -f noninteractive tzdata # Reduce apt sources to Main and Universe only # Create the new sources.list file on the host in /tmp # Paste all of these lines at once into the Host terminal cat <<EOF > /tmp/container-sources.list deb http://us.archive.ubuntu.com/ubuntu/ focal main universe deb http://us.archive.ubuntu.com/ubuntu/ focal-updates main universe deb http://security.ubuntu.com/ubuntu focal-security main universe EOF # Push the file into the container lxc file push /tmp/container-sources.list ha/etc/apt/sources.list # Apt removals and additions lxc exec ha -- apt autoremove openssh-server lxc exec ha -- apt update lxc exec ha -- apt upgrade lxc exec ha -- apt install python3-pip python3-venv
Create the Venv, Build HomeAssistant, and Test
This method is simpler than all that mucking around activating and venv and paying attention to your prompt. All these command are issued on the Host. You don't need a container shell prompt.
# Setup the homeassistant venv in a dir called 'ha_system' #We will use the root account since it's an unprivileged container. lxc exec ha -- python3 -m venv --system-site-packages /root/ha_system # Build and install HomeAssistant lxc exec ha -- /root/ha_system/bin/pip3 install homeassistant # Learn the container's IP address. Need this for the web browser. lxc list | grep ha # Run HomeAssistant lxc exec ha -- /root/ha_system/bin/hass -c "/root/ha_data" # Use your browser to open the the IP address:8123 # HA takes a couple minutes to start up. Be patient. # Stop the server from within the Web UI or ^C to exit when done.
Start HomeAssistant at Boot (Container Startup)
The right way to do autostart is a systemd service file on the container. Like with the sources.list file, we will create it on the host, then push it into the container, then enable it. There's one optional ExecPreStart line - it will slow each startup slightly while it checks for and installs updates.
cat <<EOF > /tmp/container-homeassistant.service [Unit] Description=Home Assistant After=network-online.target [Service] Type=simple User=root PermissionsStartOnly=true ExecPreStart=/root/ha_system/bin/pip3 install --upgrade homeassistant ExecStart=/root/ha_system/bin/hass -c "/root/ha_data" [Install] WantedBy=multi-user.target EOF # Push the .service file into the container, and enable it lxc file push /tmp/container-homeassistant.service ha/etc/systemd/system/homeassistant.service lxc exec ha -- systemctl --system daemon-reload lxc exec ha -- systemctl enable homeassistant.service lxc exec ha -- systemctl start homeassistant.service
Now we can test it. The last command should start HA. The same command with 'stop' should gracefully stop HA. Restarting the container should gracefully stop HA, and then restart it automatically. Your web browser UI should pick up each stop and start. You did it!
Final Notes
Remember how you start without any HomeAssitant data to mount? Now that you have a running HA Core, you can save a set of data:
lxc file pull ha/root/.homeassistant /somewhere/else/.homeassistant --recursive
And remember to clean up your mess when youare done:
lxc stop ha lxc delete ha