Skip to content

Installation Docker OCI

Jens Maus edited this page May 23, 2023 · 111 revisions

It is possible to run RaspberryMatic also as a docker/OCI container on existing amd64, aarch64/arm64 or arm-based systems. While the docker version comes with almost all functionality that a "normal" RaspberryMatic system provides, some functionality like, e.g. the network setup or WebUI-based update mechanism has been explicitly disabled since these operations are usually performed on the system hosting the docker container itself rather than within the container.

Furthermore, while almost the same hardware requirements in terms of homematic rf-modules apply like with a standard RaspberryMatic system, the kernel drivers for, e.g. the RPI-RF-MOD, HM-MOD-RPI-PCB or HmIP-RFUSB as well as for the HB-RF-USB/HB-RF-USB-2 and the HB-RF-ETH have to be installed on the hosting machine rather than within the container. The container then just uses these preloaded kernel drivers. This driver installation can be either performed using a dedicated install-docker.sh installation script or manually. In either cases, kernel modules are installed that are kindly developed and distributed by the author of piVCCU (@alexreinert).

Known limitations

  • To be able to communicate with a HmIP-HAP or HmIPW-DRAP device, the docker container implementation outlined here utilizes a macvlan docker network which makes it necessary to have two static ip addresses (<IP-ADDRESS> and <AUX-ADDRESS) available from your local network which are outside of any DHCP range (e.g. NOT supplied by your router) and are exclusively assigned to the docker container as well as to your docker host so that both systems (docker host and container) can communicate with eachother.

Debian/Ubuntu-based Host system

Prerequisites (on host system):

  1. Ensure you installed docker:
    curl -fsSL https://get.docker.com -o get-docker.sh
    sudo sh get-docker.sh
  2. Make sure to add the user under which the docker should run to the docker group:
    sudo usermod -aG docker $USER
  3. Make sure the docker environment works correctly by executing the hello-world example docker:
    docker run hello-world
  4. Install pivccu-modules-dkms kernel modules (required for RPI-RF-MOD, HM-MOD-RPI-PCB or HmIP-RFUSB use):
    sudo apt install wget ca-certificates build-essential bison flex libssl-dev gpg
    wget -qO - https://apt.pivccu.de/piVCCU/public.key | gpg --dearmor -o /usr/share/keyrings/pivccu-archive-keyring.gpg
    sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/pivccu-archive-keyring.gpg] https://apt.pivccu.de/piVCCU stable main" >/etc/apt/sources.list.d/pivccu.list'
    sudo apt update
    sudo apt install pivccu-modules-dkms
  5. If you are using a RaspberryPi and want to use a RPI-RF-MOD or HM-MOD-RPI-PCB connected to GPIO (NOT necessary if you want to use a HB-RF-USB, HB-RF-USB-2, HB-RF-ETH or HmIP-RFUSB):
    1. Install the necessary device tree patches:

      sudo apt install pivccu-modules-raspberrypi
    2. Disable bluetooth:

      sudo bash -c 'cat <<EOT >>/boot/config.txt
      dtoverlay=disable-bt
      EOT'
      systemctl is-active --quiet hciuart.service && sudo systemctl disable hciuart.service

      NOTE: If there is no /boot/config.txt, you may put these changes under /boot/firmware/usercfg.txt

    3. Disable serial console

      sudo sed -i /boot/cmdline.txt -e "s/console=\(serial0\|ttyAMA0\),[0-9]\+ //"

      And alternative method (if you run into problems) for disabling the serial console or ttyAMA0 would be to run the following script:

      sudo bash -c 'cat <<EOT >>/boot/config.txt
      dtoverlay=miniuart-bt
      core_freq=250
      EOT'

      NOTE: If there is no /boot/config.txt, you may put these changes under /boot/firmware/usercfg.txt

      Disable and mask any serial getty service under your OS so that ttyAMA0 will be free:

      sudo systemctl stop serial-getty@ttyAMA0.service && sudo systemctl disable serial-getty@ttyAMA0.service && sudo systemctl mask serial-getty@ttyAMA0.service 
  6. If you are using a HmIP-RFUSB, make sure you DO NOT have a file /etc/udev/rules.d/10-hmiprfusb.rules installed, otherwise delete it:
    sudo rm -f /etc/udev/rules.d/10-hmiprfusb.rules
  7. Make sure to execute the following so that a /etc/udev/rules.d/99-Homematic.rules udev rule file is created with the right entries:
    sudo bash -c 'cat <<EOF >/etc/udev/rules.d/99-Homematic.rules
    ATTRS{idVendor}=="1b1f" ATTRS{idProduct}=="c020", ENV{ID_MM_DEVICE_IGNORE}="1"
    ATTRS{idVendor}=="1b1f" ATTRS{idProduct}=="c00f", ENV{ID_MM_DEVICE_IGNORE}="1"
    ATTRS{idVendor}=="0403" ATTRS{idProduct}=="6f70", ENV{ID_MM_DEVICE_IGNORE}="1"
    ATTRS{idVendor}=="10c4" ATTRS{idProduct}=="8c07", ENV{ID_MM_DEVICE_IGNORE}="1"
    EOF'
  8. Install eq3_char_loop kernel module to automatic module load section:
    sudo sh -c 'echo eq3_char_loop >/etc/modules-load.d/eq3_char_loop.conf'
  9. Start+Load all necessary kernel modules on host:
    sudo service pivccu-dkms start
    sudo modprobe eq3_char_loop
    PLEASE NOTE: If modprobe eq3_char_loop fails it could be necessary to update the kernel-headers (especially on Raspberry Pi OS 64-bit):
    sudo apt update
    sudo apt install raspberrypi-kernel raspberrypi-kernel-headers raspberrypi-bootloader
    Afterwards you may have to execute the pivccu-dkms installation again and also install the eq3_char_loop kernel module to the automatic module load section:
    sudo apt install pivccu-modules-dkms
    sudo sh -c 'echo eq3_char_loop >/etc/modules-load.d/eq3_char_loop.conf'

Docker Network Setup

Before being able to use the container a macvlan docker network have to be setup so that the docker container can correctly communicate with the local network. For this the following pre-steps have to be performed

NOTE: These steps are NOT required if you are going to use the install-docker.sh method in the next section of the documentation to create the docker container.

  1. Create a dedicated macvlan docker network named ccu for the RaspberryMatic container:

    docker network create -d macvlan \
                          --opt parent=<INTERFACE> \
                          --subnet <SUBNET> \
                          ccu

    Make sure to adapt the values for <INTERFACE>, <SUBNET> to your needs. The following rules apply:

    • set <INTERFACE> to the interface name (e.g. eth0) of the main ethernet device of your docker host which is in the same subnet as the container.
    • set <SUBNET> to the subnet of the LAN segment (e.g. 192.168.178.0/24) the container should be reachable in.
  2. Setup a "shim" network link so that the docker host itself can access the the docker container, which usually isn't the case because of network encapsulation:

    sudo ip link add ccu-shim link <INTERFACE> type macvlan mode bridge
    sudo ip addr add <AUX-ADDRESS> dev ccu-shim
    sudo ip link set ccu-shim up
    sudo ip route add <IP-ADDRESS> dev ccu-shim protocol static

    Make sure to use the same value for <INTERFACE> like one step above and adapt the values for <AUX-ADDRESS> and <IP-ADDRESS> according to:

    • set <AUX-ADDRESS> to an IP address from the same subnet (e.g. 192.168.178.3) which will be assigned to the docker host.
    • set <IP-ADDRESS> to the IP address you would like the docker container to use (e.g. 192.168.178.4).
  3. The above ip commands to create a shim network have to be executed after each reboot of the docker host system because they are not persistent. You have to either execute them each time, use the install-docker.sh method below or add them to a boot-up script which will be executed on each boot.

    E.g. on Ubuntu/Debian the following content can be put into the file /etc/network/if-up.d/99-ccu-shim-network

    #!/bin/sh
    if [ "$IFACE" = "<INTERFACE>" ]; then
      if ! ip link show ccu-shim >/dev/null 2>&1; then
        ip link add ccu-shim link <INTERFACE> type macvlan mode bridge
        ip addr add <AUX-ADDRESS> dev ccu-shim
        ip link set ccu-shim up
        ip route add <IP-ADDRESS> dev ccu-shim protocol static
      fi
    fi

    Make sure to set executable rights (chmod a+rx /etc/network/if-up.d/99-ccu-shim-network) to that file and to replace all <XXXX> tags with the appropriate values from above.

Docker Container Setup

For creating the docker container one can use different methods (install-docker.sh, docker pull, docker load and docker compose) as explained below. Please note, that in the following description one has to replace the <IP-ADDRESS>, <INTERFACE> and <SUBNET> placeholders with appropriate values as already discussed above.

Using install-docker.sh script:

NOTE: Please note, that the current version of the install-docker.sh script can directly apply changes to the host system where the docker container is started, thus care should be taken when executing it via sudo/root rights.

  1. Execute the following command to install all necessary dependencies and download and start the container:
    wget -qO - https://raspberrymatic.de/install-docker.sh | bash -
    The script will make sure all dependencies are installed and might request sudo rights to do so. If this is the case, re-run it with sudo accordingly:
    wget -qO - https://raspberrymatic.de/install-docker.sh | sudo bash -
  2. After the script has created the docker container you should be able to start the container using:
    docker start -i ccu
    NOTE: Omit -i if you want to start the container non-interactive and put it in background.

If you want to use this script to permanently start the RaspberryMatic docker container you should download the install-docker.sh script and perhaps adapt it to your needs. Make sure, however, to update the script over time because we might introduce changes to it as we move forward.

Using docker pull:

  1. Use docker pull to retrieve the latest version:
    docker pull ghcr.io/jens-maus/raspberrymatic:latest
  2. Create the raspberrymatic docker container:
    docker create --name ccu \
                  --volume ccu_data:/usr/local:rw \
                  --volume /lib/modules:/lib/modules:ro \
                  --volume /run/udev/control:/run/udev/control \
                  --privileged --restart always --stop-timeout 30 \
                  --network ccu --ip <IP-ADDRESS> --hostname ccu \
                  ghcr.io/jens-maus/raspberrymatic:latest
  3. Start the docker container:
    docker start -i ccu
    NOTE: Omit -i if you want to start the container non-interactive and put it in background.

Using docker load:

  1. Download the latest docker/oci image version of RaspberryMatic:
    wget https://github.com/jens-maus/RaspberryMatic/releases/download/3.XX.XX.YYYYMMDD/RaspberryMatic-3.XX.XX.YYYYMMDD-oci_amd64.tgz
  2. Use docker load to load the downloaded into the docker environment:
    docker load < RaspberryMatic-3.XX.XX.YYYYMMDD-oci_amd64.tgz
  3. Run the raspberrymatic docker using cmd-line:
    docker create --name ccu \
                  --volume ccu_data:/usr/local:rw \
                  --volume /lib/modules:/lib/modules:ro \
                  --volume /run/udev/control:/run/udev/control \
                  --privileged --restart always --stop-timeout 30 \
                  --network ccu --ip <IP-ADDRESS> --hostname ccu \
                  ghcr.io/jens-maus/raspberrymatic:amd64-3.XX.XX.YYYYMMDD
  4. Start the docker container:
    docker start -i ccu
    NOTE: Omit -i if you want to start the container non-interactive and put it in background.

Using docker compose

  1. Add the following to your docker-compose.yml:
    version: "3.8"
    services:
      raspberrymatic:
        image: ghcr.io/jens-maus/raspberrymatic:latest
        container_name: ccu
        hostname: ccu
        privileged: true
        restart: unless-stopped
        stop_grace_period: 30s
        volumes:
          - ccu_data:/usr/local:rw
          - /lib/modules:/lib/modules:ro
          - /run/udev/control:/run/udev/control
        networks:
          ccu:
            ipv4_address: <IP-ADDRESS>
    volumes:
      ccu_data:
    networks:
      ccu:
        name: ccu
        driver: macvlan
        driver_opts:
          parent: <INTERFACE>
        ipam:
          config:
            - subnet: <SUBNET>
  2. Start your docker compose environment:
    docker compose up

Potential container startup errors

  1. If you get ERROR: could not insert 'eq3_char_loop': Exec format error messages during startup the required kernel modules could not be correctly compiled on your docker host machine. To solve that issue you can try the following procedure:

    sudo apt remove --purge linux-headers-*
    sudo apt autoremove && sudo apt autoclean
    sudo apt install linux-headers-generic
    sudo apt install --reinstall pivccu-modules-dkms
  2. If during container startup you receive cpu.rt_runtime_us == 0 errors in the docker logs ccu output of your RaspberryMatic startup, your host system is using a linux kernel with CONFIG_RT_GROUP_SCHED real-time scheduling capabilities and did not assign enough real-time shares to the docker daemon, thus the RaspberryMatic container.

    • Modify/Create the /etc/docker/daemon.json file to contain/look like:
      {
        "cpu-rt-runtime": 950000
      }
      Afterwards, make sure you restart the docker daemon accordingly (e.g. sudo systemctl restart docker).
    • Depending on the method you are using to start the RaspberryMatic docker container make sure to either
      1. If using install-docker.sh:

        No action required as the latest install-docker.sh script should automatically set the necessary additional command-line options.

      2. If using docker pull or docker load:

        add the following command-line options to the corresponding docker run ... command:

        --cpu-rt-runtime 950000 --ulimit rtprio=99
      3. If using docker compose:

        add the following options to your docker-compose.yml file:

        cpu_rt_runtime: 950000
        ulimits:
          rtprio: 99

WebUI access to container

After startup, point your web browser to the IP of your hosting system and with port :80 and the usual CCU WebUI should be accessible:

http://<IP-ADDRESS>/

Shell access to container

After startup, you can either access the container via enabling SSH access to it in the CCU WebUI or you use the following docker exec command to be able to use the command-line:

docker exec -it ccu /bin/sh

Update to newer versions

The RaspberryMatic docker has to be updated from the host machine (the recommended "docker way"). Use either the same instructions like in using install-docker.sh and the other subsections (e.g. using docker pull) so that you retrieve the very latest version of the RaspberryMatic docker with the next execution.

Docker Container Uninstall

If you decide to get rid of the whole RaspberryMatic container installation or if you would like to cleanup before trying to install the container in a fresh environment you can do so by running the install-docker.sh script with an uninstall parameter:

wget -qO - https://raspberrymatic.de/install-docker.sh | sudo bash -s uninstall

This command will make sure to uninstall, remove and purge all relevant configuration files, docker container and docker network dependencies so that after a fresh reboot of your docker host system only the user data volume should still be present.

Additional install-docker.sh settings

You can pass variables to the install-docker.sh script to change the defaults upon runtime:

  • CCU_DATA_VOLUME
    • Name of the docker volume where CCU data will persist. It can be a local location as well such as a mounted NAS folder, cluster fs (glusterfs), etc.
    • Default: "ccu_data"
  • CCU_OCI_REPO
    • Container repository to use
    • Default: "ghcr.io/jens-maus/raspberrymatic"
  • CCU_OCI_TAG
    • Container image tag to use
    • Default: "latest"
  • CCU_CONTAINER_NAME
    • Name of the container instance. Will also be used as --hostname parameter for docker run.
    • Default: "ccu"
  • CCU_CONTAINER_IP
    • IP address for the container instance (e.g. 192.168.178.4). Have to be a non-DHCP assignable IP address from the same LAN subnet as the docker host.
    • Default: "" (will be interactively requested)
  • CCU_CONTAINER_IP_AUX
    • Auxiliary ip address additionally assigned to the docker host system (e.g. 192.168.178.3). Have to be a non-DHCP assignable IP address from the same LAN subnet as the main IP address of the docker host.
    • Default: "" (will be interactively requested)
  • CCU_NETWORK_NAME
    • Name for the docker macvlan network being created/maintained.
    • Default: "ccu"
  • CCU_NETWORK_INTERFACE
    • Interface name (e.g. eth0, ensXX) of the main interface the docker host is running on and on which the same subnet is located.
    • Default: "" (will be automatically determined or interactively requested)
  • CCU_NETWORK_SUBNET
    • Subnet (e.g. 192.168.178.0/24) of the main docker host and container ip address being used.
    • Default: "" (will be automatically determined or interactively requested)
  • CCU_DOCKER_RUN_OPTIONS
    • Additional options for docker run
    • Default: ""
  • CCU_DOCKER_PULL_OPTIONS
    • Additional options for docker pull run
    • Default: ""
  • CCU_DOCKER_PULL_REFRESH
    • Do a docker pull to refresh image
    • Default: "true"
  • CCU_DOCKER_STOP_TIMEOUT
    • time allowed to stop the container before Docker kills it
    • Default: 30 (seconds)