These are my notes setting up rtl_433 docker container on a Proxmox host inside an LXC guest...all the inceptions...
- https://discussions.flightaware.com/t/howto-rtl-sdr-dongle-pass-through-to-proxmox-lxc-container/89093
- https://sdr-enthusiasts.gitbook.io/ads-b/setting-up-rtl-sdrs/blacklist-kernel-modules
Some decent helper scripts here, this one installs docker in LXC pretty effortlessly
We need to prevent autoload of proxmox kernel modules from seizing the RTL device:
create /etc/modprobe.d/blacklist-rtlsdr.conf:
# Blacklist host from loading modules for RTL-SDRs to ensure they
# are left available for the Docker guest.
blacklist dvb_core
blacklist dvb_usb_rtl2832u
blacklist dvb_usb_rtl28xxu
blacklist dvb_usb_v2
blacklist r820t
blacklist rtl2830
blacklist rtl2832
blacklist rtl2832_sdr
blacklist rtl2838
# This alone will not prevent a module being loaded if it is a
# required or an optional dependency of another module. Some kernel
# modules will attempt to load optional modules on demand, which we
# mitigate here by causing /bin/false to be run instead of the module.
#
# The next time the loading of the module is attempted, the /bin/false
# will be executed instead. This will prevent the module from being
# loaded on-demand. Source: https://access.redhat.com/solutions/41278
install dvb_core /bin/false
install dvb_usb_rtl2832u /bin/false
install dvb_usb_rtl28xxu /bin/false
install dvb_usb_v2 /bin/false
install r820t /bin/false
install rtl2830 /bin/false
install rtl2832 /bin/false
install rtl2832_sdr /bin/false
install rtl2838 /bin/false
run this once to unload modules if already loaded on host
modprobe -r dvb_core
modprobe -r dvb_usb_rtl2832u
modprobe -r dvb_usb_rtl28xxu
modprobe -r dvb_usb_v2
modprobe -r r820t
modprobe -r rtl2830
modprobe -r rtl2832
modprobe -r rtl2832_sdr
modprobe -r rtl2838
update modules and initramfs on host
depmod -a
update-initramfs -u
find path of rtl device we want to export to lxc/docker
# lsusb
Bus 002 Device 002: ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 8087:0a2b Intel Corp. Bluetooth wireless interface
Bus 001 Device 009: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
In this case we want the device on Bus 001 and Device 009
check permissions of /dev/bus/usb/001/009
# ls -l /dev/bus/usb/001/009
crw-rw-r-- 1 root root 189, 8 May 15 09:59 /dev/bus/usb/001/009
set permissions of /dev/bus/usb/001/009
to 0666:
# chmod 0666 /dev/bus/usb/001/009
# ls -l /dev/bus/usb/001/009
crw-rw-rw- 1 root root 189, 8 May 15 09:59 /dev/bus/usb/001/009
a more permament setup is to use udev rules:
/etc/udev/rules.d/50-usb.rules:
SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="2838", GROUP="root", MODE="0666"
And reload them with:
udevadm control --reload-rules && udevadm trigger
I've found that, even without phsycially changing the USB devices the paths can move around, which is weird to me but whatever. I devised this method to dynamically update the lxc configuration on boot. Could probably also do this with a udev rule but, let's not get crazy...
Create a script, for example, /usr/local/bin/update_lxc_usb_path.sh
, that will find the correct USB device path and update the LXC configuration file.
#!/bin/bash
# /usr/local/bin/update_lxc_usb_path.sh
# Configuration
CONTAINER_ID=111
CONFIG_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
DEVICE_ID="0bda:2838" # Change to your USB device ID
# Find the USB device path
device_info=$(lsusb | grep $DEVICE_ID)
bus=$(echo $device_info | awk '{print $2}')
device=$(echo $device_info | awk '{print $4}' | sed 's/://')
device_path="/dev/bus/usb/$bus/$device"
# Update the LXC configuration
if [ -n "$device_info" ]; then
sed -i "/^lxc.mount.entry:.*dev\/bus\/usb\/.* none bind,create=file/c\lxc.mount.entry: $device_path dev/bus/usb/$bus/$device none bind,create=file" $CONFIG_FILE
# Update snapshot configurations
sed -i "/^\[.*\]/,/\[.*\]/ s/^lxc.mount.entry:.*dev\/bus\/usb\/.* none bind,create=file/lxc.mount.entry: $device_path dev/bus/usb\/$bus\/$device none bind,create=file/" $CONFIG_FILE
echo "Updated LXC config with new device path: $device_path"
else
echo "Device with ID $DEVICE_ID not found"
fi
Make the script executable:
chmod +x /usr/local/bin/update_lxc_usb_path.sh
Create a systemd service file, for example, /etc/systemd/system/update_lxc_usb_path.service
, to run the script at boot.
[Unit]
Description=Update LXC USB Path
After=pve-cluster.service
[Service]
Type=oneshot
ExecStart=/bin/bash /usr/local/bin/update_lxc_usb_path.sh
[Install]
WantedBy=multi-user.target
Enable the service to run at boot and run it for testing (--now):
systemctl enable --now update_lxc_usb_path.service
I use the docker
LXC setup script from tteck's proxmox script and I used the default debian install from bash -c "$(wget -qLO - https://github.com/tteck/Proxmox/raw/main/ct/docker.sh)"
After the install completes I needed to shut down the container and modify the config file /etc/pve/lxc/111.conf
to passthrough / allow the USB device:
lxc.cgroup2.devices.allow: c 189:* rwm
lxc.mount.entry: /dev/bus/usb/001/009 dev/bus/usb/001/009 none bind,optional,create=file
Then I restart the container and log-in (console is auto-login by default, we modify this later). I also setup my ssh keys for root
in /root/.ssh/authorized_keys
to login remotely later.
I created a user called rtl_433 and dropped my config file in /home/rtl_433/rtl_433.conf
on the LXC guest and I exported that volume to docker.
adduser --system rtl_433 --home /home/rtl_433
```bash
docker run --rm --name rtl_433 --device /dev/bus/usb/001/009 -v /home/rtl_433:/home/rtl_433:rw hertzg/rtl_433:latest-alpine -c /home/rtl_433/rtl_433.conf
because the USB device path might change (happened to me a couple of times without phsyically changing the device) I decided to make this part dynamic in the container, so that it only needs to be changed in one place (in the lxc config file)
/usr/local/bin/rtl_dev.sh:
#!/bin/bash
# /usr/local/bin/rtl_dev.sh
device_info=$(lsusb | grep '0bda:2838')
bus=$(echo $device_info | awk '{print $2}')
device=$(echo $device_info | awk '{print $4}' | sed 's/://')
device_path="/dev/bus/usb/$bus/$device"
echo "USB_DEVICE_PATH=$device_path" > /etc/sysconfig/rtl_433
The /etc/sysconfig directory didn't exist for me, but it felt like the right place so i made it anyway, also chmod the script we just made:
mkdir /etc/sysconfig
chmod 755 /usr/local/bin/rtl_dev.sh
created a service to start the container at boot: /etc/systemd/system/rtl_433.service:
[Unit]
Description=rtl_433 Container
After=network.target docker.service
Requires=docker.service
Wants=network-online.target
[Service]
Restart=always
RestartSec=5s
EnvironmentFile=/etc/sysconfig/rtl_433
ExecStartPre=/bin/bash /usr/local/bin/rtl_dev.sh
ExecStartPre=-/usr/bin/docker stop rtl_433
ExecStartPre=-/usr/bin/docker rm rtl_433
ExecStart=/usr/bin/docker run --name rtl_433 --device ${USB_DEVICE_PATH} -v /home/rtl_433:/home/rtl_433:rw --restart unless-stopped hertzg/rtl_433:latest-alpine -c /home/rtl_433/rtl_433.conf
ExecStop=/usr/bin/docker stop rtl_433
[Install]
WantedBy=multi-user.target
reload units and start it
# systemctl daemon-reload
# systemctl enable --now rtl_433
# systemctl status rtl_433
● rtl_433.service - rtl_433 Container
Loaded: loaded (/etc/systemd/system/rtl_433.service; enabled; preset: enabled)
Active: active (running) since Sat 2024-05-11 12:36:40 EDT; 6min ago
Process: 460 ExecStartPre=/usr/bin/docker stop rtl_433 (code=exited, status=0/SUCCESS)
Process: 465 ExecStartPre=/usr/bin/docker rm rtl_433 (code=exited, status=0/SUCCESS)
Main PID: 470 (docker)
Tasks: 8 (limit: 38334)
Memory: 8.0M
CPU: 69ms
CGroup: /system.slice/rtl_433.service
└─470 /usr/bin/docker run --name rtl_433 --device /dev/bus/usb/001/009 -v /home/rtl_433:/home/rtl_433:rw --restart unless-stopped hertzg/rtl_433:latest-alpine -c /home/rtl_433/rtl_433.conf
May 11 12:43:17 rtl-433 docker[470]: Wind speed: 0.0 m/s
May 11 12:43:17 rtl-433 docker[470]: Gust speed: 0.0 m/s
May 11 12:43:17 rtl-433 docker[470]: UVI : 0.0
May 11 12:43:17 rtl-433 docker[470]: Light : 30.0 lux
May 11 12:43:17 rtl-433 docker[470]: Flags : 82
May 11 12:43:17 rtl-433 docker[470]: Total Rain: 86.8 mm
May 11 12:43:17 rtl-433 docker[470]: Supercap Voltage: 2.5 V
May 11 12:43:17 rtl-433 docker[470]: Firmware Version: 133
May 11 12:43:17 rtl-433 docker[470]: Extra Data: 3fff67b2bc------1492ffbffb0000
May 11 12:43:17 rtl-433 docker[470]: Integrity : CRC
modified the auto-login from ttech boi to tail the logs of the container instead
/etc/systemd/system/[email protected]/override.conf
:
[Service]
ExecStart=
ExecStart=/usr/local/bin/rtl_433_journal.sh
StandardInput=tty
StandardOutput=tty
TTYPath=/dev/tty1
Restart=always
# old contents
# [Service]
# ExecStart=
# ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 $TERM
and created this to support above:
/usr/local/bin/rtl_433_journal.sh
:
#!/bin/bash
# Follow the journal of rtl-433 in color
exec journalctl -f -u rtl_433 --output=short-iso
reloaded and restarted:
systemctl daemon-reload
systemctl restart container-getty@1