Similar to my post about running Home Assistant on Fedora in Docker, this is about using podman instead and integrating the container as a service with systemd. One of the major advantages to me is the removal of Docker daemon and integration with the rest of the system including management of dependencies like regular services.
This assumes you’ve just installed Fedora server and have a local user with sudo privileges. Let’s also install some SELinux tools.
sudo dnf install -y /usr/sbin/semanage
Create non-root user
Let’s create a specific user to run the Home Assistant service.
We could create a regular user (and remove password expiry settings), but as this is a service let’s create a system account even though it’s a bit more tricky.
sudo useradd -r -m -d /var/lib/hass hass
As this is a system account, we’ll need to manually specify sub user and group ids that the account is allowed to use inside the container. We work out what range is available by looking at /etc/subuid and /etc/subgid files on the host, ideally UID and GID should be the same.
NEW_SUBUID=$(($(tail -1 /etc/subuid |awk -F ":" '{print $2}')+65536)) NEW_SUBGID=$(($(tail -1 /etc/subgid |awk -F ":" '{print $2}')+65536)) sudo usermod \ --add-subuids ${NEW_SUBUID}-$((${NEW_SUBUID}+65535)) \ --add-subgids ${NEW_SUBGID}-$((${NEW_SUBGID}+65535)) \ hass
Inside the hass user’s home directory, create a config directory to store configuration files and ssl to store SSL certificates. These will be mapped into the container as /config and /ssl respectively. We will also set the appropriate SELinux context so that the directories can be accessed in the container.
sudo -H -u hass bash -c "mkdir ~/{config,ssl}" sudo semanage fcontext -a -t user_home_dir_t "/var/lib/hass(/.+)?" sudo semanage fcontext -a -t svirt_sandbox_file_t \ "/var/lib/hass/((config)|(ssl))(/.+)?" sudo restorecon -Frv /var/lib/hass
Pull the container image
Now that we have the basic home directly in place, we can switch to the hass user with sudo.
sudo su - hass
As the hass user, let’s use podman to download and run the official Home Assistant image in a container.
First, pull the container which is stored under the non-root user’s ~/.local/share/containers/ directory. Note the latest tag on the end of the image name specifies the version to run. While this is not necessary if you’re getting the latest (as it’s the default), if you want a specific release simply replace latest with the version you want (see their Docker hub page for available releases). Specifying latest means we’ll get the latest release of the container at the time.
podman pull \ docker.io/homeassistant/home-assistant:latest
Manually start the container
Now we can spin up a container using that image. Note that we’re passing in the config and ssl (as read only) directories we created earlier and using host networking to open the required ports on the host.
podman run -dt \ --name=hass \ -v /var/lib/hass/config:/config \ -v /var/lib/hass/ssl:/ssl:ro \ -v /etc/localtime:/etc/localtime:ro \ --net=host \ docker.io/homeassistant/home-assistant:latest
Similar to Docker, you can look at the status of the container and manage it with podman, including getting the logs if you require them.
podman ps -a podman logs hass podman restart hass
To get a temporary shell on the container, execute bash.
podman exec -it hass /bin/bash
Inside the container, take a look at the passed-in /config directory (do anything else you want) and then exit when you’re done.
ls -l /config id echo "I am root in the container" exit
Once the container is up and running, the Home Assistant port should be listening on the host.
ss -ltn |grep 8123
Manually destroy the container
Next we’ll create a service to manage this, so for now you can stop and delete this container (this does not delete the image we downloaded). Do this as the hass user still, then exit to return to your regular user.
podman stop hass podman rm hass podman ps -a exit
Configuring the firewall
Home Assistant runs on port 8123, so we will need to open this port on the firewall (back as your regular user).
sudo firewall-cmd --get-active-zones sudo firewall-cmd --zone=FedoraServer --add-port=8123/tcp
You can test this by using your web browser to connect to the IP address of your machine on port 8123 from another machine on your network.
If that works, make the firewall change permanent.
sudo firewall-cmd --runtime-to-permanent
Create service for the container
Now that we have the container that works, let’s create a systemd service to manage it. This will auto start the container on boot and allow us to manage it as a regular service, including any dependencies. This service stops, removes and starts a new container every time.
Note the Exec lines which will delete and restart the container from the image. As per the manual command above, to run a specific version replace latest with an available tagged release.
cat << EOF | sudo tee /etc/systemd/system/hass.service [Unit] Description=Home Assistant in Container After=network.target [Service] User=hass Group=hass Type=simple TimeoutStartSec=5m ExecStartPre=-/usr/bin/podman rm -f "hass" ExecStart=podman run --name=hass -v /var/lib/hass/ssl:/ssl:ro -v /var/lib/hass/config:/config -v /etc/localtime:/etc/localtime:ro --net=host docker.io/homeassistant/home-assistant:latest ExecReload=-/usr/bin/podman stop "hass" ExecReload=-/usr/bin/podman rm "hass" ExecStop=-/usr/bin/podman stop "hass" Restart=always RestartSec=30 [Install] WantedBy=multi-user.target EOF
Reload the systemd daemon is required to pick up the new file.
sudo systemctl daemon-reload
Manage the container with systemd
Let’s see if we can restart the container and check its status. Because it is now managed by systemd, we can check the log with journalctl.
sudo systemctl restart hass sudo systemctl status hass sudo journalctl -u hass
Once you’re happy, we can enable the service.
sudo systemctl enable hass
Now is probably a good time to reboot your machine and make sure that the service comes up fine on boot.
Configuring Home Assistant
After rebooting, you should be able to browse to the Home Assistant port on your machine.
Now that you have Home Assistant running, modify the configuration as you please by editing the configuration file under the hass user home directory.
If you make a change, you can simply restart the service.
Updating the container
To update the container, switch to the hass user again and pull a newer version of the container. We can see the newer version of the image with podman and if you want to you can inspect the image for more details.
podman pull docker.io/homeassistant/home-assistant:latest podman images -a podman inspect docker.io/homeassistant/home-assistant:latest
Now you can restart the container as your regular user.
sudo systemctl restart hass sudo journalctl -uf hass.service
Conclusion
Anyway, that’s an example of how you could do it with something like Home Assistant. It can be modified accordingly for any other container you might want to run.
13 thoughts on “Running a non-root container on Fedora with podman and systemd (Home Assistant example)”
Why not use systemd’s “portable services”? (https://systemd.io/PORTABLE_SERVICES.html)
They’re not containers but they arealmost there and they are (still) more integrated in systemd.
Thanks anyway for your post!
Hey Oscar, thanks for the link. I haven’t played with portable services yet, but I will try to make some time to check it out. Cheers.
Great guide. I’m trying this out on RHEL8 rather than on Fedora and I’m getting errors starting up the container image
For debugging I tried
podman run -it –name=hass \
-v /var/lib/hass/config:/config \
-v /var/lib/hass/ssl:/ssl:ro \
-v /etc/localtime:/etc/localtime:ro \
–net=host docker.io/homeassistant/home-assistant:latest /bin/bash
Then
python -m homeassistant –config /config
Traceback (most recent call last):
File “/usr/local/lib/python3.7/site-packages/keyring/backend.py”, line 203, in _load_plugins
init_func = ep.load()
File “/usr/local/lib/python3.7/site-packages/importlib_metadata/__init__.py”, line 94, in load
module = import_module(match.group(‘module’))
File “/usr/local/lib/python3.7/importlib/__init__.py”, line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1006, in _gcd_import
File “”, line 983, in _find_and_load
File “”, line 967, in _find_and_load_unlocked
File “”, line 677, in _load_unlocked
File “”, line 728, in exec_module
File “”, line 219, in _call_with_frames_removed
File “/usr/local/lib/python3.7/site-packages/keyrings/alt/Windows.py”, line 17, in
from . import _win_crypto
File “/usr/local/lib/python3.7/site-packages/keyrings/alt/_win_crypto.py”, line 3, in
from ctypes import (
File “/usr/local/lib/python3.7/ctypes/__init__.py”, line 551, in
_reset_cache()
File “/usr/local/lib/python3.7/ctypes/__init__.py”, line 273, in _reset_cache
CFUNCTYPE(c_int)(lambda: None)
MemoryError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File “/usr/local/lib/python3.7/runpy.py”, line 193, in _run_module_as_main
“__main__”, mod_spec)
File “/usr/local/lib/python3.7/runpy.py”, line 85, in _run_code
exec(code, run_globals)
File “/usr/src/homeassistant/homeassistant/__main__.py”, line 380, in
sys.exit(main())
File “/usr/src/homeassistant/homeassistant/__main__.py”, line 354, in main
args = get_arguments()
File “/usr/src/homeassistant/homeassistant/__main__.py”, line 96, in get_arguments
import homeassistant.config as config_util
File “/usr/src/homeassistant/homeassistant/config.py”, line 56, in
from homeassistant.util.yaml import SECRET_YAML, load_yaml
File “/usr/src/homeassistant/homeassistant/util/yaml/__init__.py”, line 4, in
from .loader import clear_secret_cache, load_yaml, secret_yaml
File “/usr/src/homeassistant/homeassistant/util/yaml/loader.py”, line 17, in
import keyring
File “/usr/local/lib/python3.7/site-packages/keyring/__init__.py”, line 1, in
from .core import (
File “/usr/local/lib/python3.7/site-packages/keyring/core.py”, line 192, in
init_backend()
File “/usr/local/lib/python3.7/site-packages/keyring/core.py”, line 96, in init_backend
filter(limit, backend.get_all_keyring()),
File “/usr/local/lib/python3.7/site-packages/keyring/util/__init__.py”, line 22, in wrapper
func.always_returns = func(*args, **kwargs)
File “/usr/local/lib/python3.7/site-packages/keyring/backend.py”, line 216, in get_all_keyring
_load_plugins()
File “/usr/local/lib/python3.7/site-packages/keyring/backend.py”, line 207, in _load_plugins
log.exception(“Error initializing plugin %s.” % ep)
TypeError: not all arguments converted during string formatting
How much memory is home-assistant using in your container as I’m running a relatively small VM as the container host with 4GB RAM.
Hi I was trying with latest Centos 8.1 and , while the container running as root runs fine,
under hass user homeassistant bombs with
MemoryError
even if I see no memory limit anywhere…
anyone has some suggestion?
thanks
Hey Steve and g, sorry I missed your comments. I’ve just spun up a 1GB CentOS 8 VM with the latest updates and it seems to work fine for me… Can you try pulling the latest container and going again?
[hass@example-centos-8 ~]$ podman logs hass |tail -5
2020-02-16 20:53:53 INFO (MainThread) [homeassistant.setup] Setup of domain tts took 0.0 seconds.
2020-02-16 20:53:55 INFO (MainThread) [homeassistant.bootstrap] Home Assistant initialized in 3.39s
2020-02-16 20:53:55 INFO (MainThread) [homeassistant.core] Starting Home Assistant
2020-02-16 20:53:55 INFO (MainThread) [homeassistant.core] Timer:starting
2020-02-16 20:53:55 INFO (SyncWorker_2) [homeassistant.components.zeroconf] Starting Zeroconf broadcast
[hass@example-centos-8 ~]$ ss -ltnp |grep 8123
LISTEN 0 128 0.0.0.0:8123 0.0.0.0:* users:(("python3",pid=1454,fd=14))
[hass@example-centos-8 ~]$ curl http://localhost:8123/onboarding.html
Home Assistant
Much thanks, I’m confused for non-root user running podman on systemd, this article helps a lot.
🙂
Thanks for the blog post!
I’ve tried above but couldn’t make it work with my hardware devices necessary for my setup.
The devices are /dev/ttyUSB0 and /dev/ttyUSB1, owned by root/dialout. How do I pass these in with podman (and probably selinux enforcing), just doing the workarounds I use with docker (pass the directory /dev/serial/by-id and then pass the devices ttyUSB0/ttyUSB1) doesn’t work here.
Hi Oscar, does this help? Maybe try creating a udev rule.
https://blog.christophersmart.com/2020/04/18/accessing-usb-serial-devices-in-fedora-silverblue/
-c
Hi Chris
I’ve also got a mosquitto pod running under the hass user for mqtt services
cat << EOF | sudo tee /etc/systemd/system/mosquitto.service
[Unit]
Description=Home Assistant in Container
After=network.target
[Service]
User=hass
Group=hass
Type=simple
TimeoutStartSec=5m
ExecStartPre=-/usr/bin/podman rm -f "mosquitto"
ExecStart=podman run –name mosquitto \
–rm -p "9001:9001" -p "1883:1883" \
eclipse-mosquitto:latest
ExecReload=-/usr/bin/podman stop "mosquitto"
ExecReload=-/usr/bin/podman rm "mosquitto"
ExecStop=-/usr/bin/podman stop "mosquitto"
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
EOF
Hi !
Thank you very much for this great tutorial. I’ve the latest Home Assistant up and running well on Fedora server 33.
But I miss the “supervisor” (add-ons). Is there a way to get the supervisor with podman ? Should I use something else to get the supervisor with Fedora 33 server ?
From memory, I think supervisor is only available when you run the Home Assistant image, which bundles everything. You’d need to update your host and the Home Assistant container image yourself outside of HA itself (if that makes sense). To update HA you’d probably check on your host whether if there is a newer version and if so, restart the container with the new image, maybe this post helps? https://blog.christophersmart.com/2019/12/15/automatically-updating-containers-with-docker/
Hi,
The latest Docker now supports cgroups v2. Thus it should now be possible to use docker and home assistant on Fedora.
But as I want the supervisor which provide easy update, easy install of add-ons (like Samba share, Mosquitto broker, a file editor…), and snapshots, I went the virtualization road with Home Assistant OS.
The documentation is here :
https://www.home-assistant.io/hassio/installation/
1- get the home assistant OS image :
$ wget https://github.com/home-assistant/operating-system/releases/download/5.9/hassos_ova-5.9.qcow2.xz
$ unxz –keep hassos_ova-5.9.qcow2.xz
2- Create a bridge in your network using the interface for internet. I made this using cockpit and named it “virbr0”
3- One can find the machines on the local network using this command (the bridge changed the local address of my server):
$ sudo nmap -sn 192.168.1.0/24
4- Create the virtual machine using the image (RAM 3GiB, 1 CPU seems the minimum requirement to me):
virt-install –name HAOS \
–memory 3072 \
–vcpus 1,maxvcpus=2 \
–import \
–disk path=/PATH_TO/hassos_ova-5.9.qcow2,size=32,format=qcow2 \
–boot uefi \
–os-variant name=linux2020 \
–network bridge=virbr0 \
–autostart
4- find the local IP adress of the virtual machine (192.168.1.48 here):
$ sudo nmap -sn 192.168.1.0/24
../..
Nmap scan report for 192.168.1.48
Host is up (0.00034s latency).
MAC Address: 52:54:00:BE:44:CE (QEMU virtual NIC)
5- Log in the virtual machine:
http://192.168.1.48:8123
Great, thanks for posting!