📷 День 48 - Две USB-камеры в Proxmox: отдельный LXC с Motion и motionEye
✎
Для удалённого визуального контроля одной из площадок HomeLab я подключил к компактному Lenovo ThinkCentre M710q две обычные USB-камеры:
- Logitech C270;
- Logitech C525.
Первый тест работал прямо на гипервизоре. Затем видеосервис переехал в отдельный unprivileged LXC. В итоге Proxmox остался чистым, а камеры получили удобный веб-интерфейс motionEye.
🧩 Архитектура
+------------------+ +--------------------------+
| USB camera C270 | ----> | |
+------------------+ | Proxmox VE host |
| /dev/video0 |
+------------------+ | /dev/video2 |
| USB camera C525 | ----> | | |
+------------------+ +------------+-------------+
| dev passthrough
v
+------------+-------------+
| Unprivileged Debian LXC |
| Motion + motionEye |
| MJPEG streams |
+------------+-------------+
| local network / VPN
v
+--------------------------+
| Browser live-view |
+--------------------------+

🔎 Как Linux видит камеры
Обе камеры определились стандартным драйвером Linux uvcvideo.
lsusb
046d:0825 Logitech Webcam C270
046d:0826 Logitech HD Webcam C525
В системе появились четыре V4L2-устройства:
/dev/video0
/dev/video1
/dev/video2
/dev/video3
Это нормально: одна USB-камера может создавать несколько /dev/videoN. Один интерфейс обычно отдаёт основной поток, другой используется для дополнительных функций или метаданных.
Основными видеоустройствами в тестовой конфигурации оказались:
C270: /dev/video0
C525: /dev/video2
Соответствие лучше проверять не по номеру, а через V4L2 и стабильные ссылки:
v4l2-ctl --list-devices
ls -l /dev/v4l/by-id/
🧹 Почему Motion не остался на гипервизоре
Для быстрого теста Motion можно запустить прямо на Proxmox. Для постоянной эксплуатации это плохо масштабируется:
- на гипервизоре появляются лишние пакеты и зависимости;
- видеосервис конкурирует с виртуализацией за ресурсы;
- обновления Motion смешиваются с обновлениями Proxmox;
- сложнее резервировать и переносить конфигурацию;
- ошибка в видеостеке затрагивает сам гипервизор.
Отдельный LXC даёт чистую границу: хост передаёт только нужные символьные устройства, а весь прикладной стек живёт внутри контейнера.
📦 Параметры LXC
Для двух USB-камер мощный контейнер не нужен:
ОС: Debian 12
CPU: 2 vCPU
RAM: 2 ГБ
Swap: 512 МБ
Диск: 16 ГБ
Сеть: статический адрес
Автозапуск: включён
Тип: unprivileged LXC
Если нужен только live-view, 16 ГБ более чем достаточно. Постоянная запись требует отдельного хранилища, квоты и политики очистки архива.
pct create <CTID> local:vztmpl/debian-12-standard_amd64.tar.zst \
--hostname cam01 \
--unprivileged 1 \
--cores 2 \
--memory 2048 \
--swap 512 \
--rootfs <STORAGE>:16 \
--net0 name=eth0,bridge=vmbr0,ip=<STATIC-IP>/24,gw=<GATEWAY> \
--onboot 1
🔌 Передача камер в LXC
В Proxmox VE 9 символьные устройства можно передать контейнеру штатными параметрами devN. Для V4L2 используется major number 81.
pct set <CTID> \
--dev0 path=/dev/video0,gid=44,mode=0660 \
--dev1 path=/dev/video2,gid=44,mode=0660
После перезапуска проверяем права и major/minor:
pct reboot <CTID>
pct exec <CTID> -- ls -l /dev/video0 /dev/video2
crw-rw---- root video 81,0 /dev/video0
crw-rw---- root video 81,2 /dev/video2
Группа video в Debian обычно имеет GID 44, но перед настройкой это нужно проверить внутри контейнера:
getent group video
Благодаря точечной передаче устройств Motion получает доступ к камерам без перевода всего LXC в privileged-режим.
Важный нюанс: /dev/videoN может измениться
Символические ссылки из /dev/v4l/by-id/ не всегда корректно передаются через devN, поэтому надёжнее указывать реальные символьные устройства.
При перезагрузке или повторном подключении USB номера могут поменяться. Для постоянной эксплуатации на хосте нужны udev-правила, которые формируют предсказуемые имена по VID, PID и, если он доступен, серийному номеру.
⚙️ Установка Motion и motionEye
Внутри LXC устанавливаем базовые зависимости:
apt update
apt install -y \
ca-certificates \
curl \
python3 \
python3-venv \
python3-pip \
motion \
v4l-utils \
ffmpeg
motionEye изолируем в отдельном Python-окружении:
python3 -m venv /opt/motioneye
/opt/motioneye/bin/pip install --upgrade pip
/opt/motioneye/bin/pip install motioneye
/opt/motioneye/bin/motioneye_init
В этой конфигурации systemd unit ожидал meyectl в /usr/local/bin, поэтому потребовалась ссылка:
ln -s /opt/motioneye/bin/meyectl /usr/local/bin/meyectl
systemctl daemon-reload
systemctl enable --now motioneye
Официальный проект описывает motionEye как веб-интерфейс для Motion. Инструкции и исходники: motioneye-project/motioneye.
По умолчанию веб-интерфейс слушает TCP-порт 8765. Сразу после первого запуска нужно установить пароль администратора и отдельный пароль для просмотра.
Порт 8765 не публикуется в интернет. Доступ разрешён только из доверенной локальной сети и через VPN.
🎥 Настройка двух потоков
Обе камеры добавлены как Local V4L2 Camera.
Logitech C270: /dev/video0
Logitech C525: /dev/video2
Разрешение: 1280x720
Формат: MJPEG
Частота: 30 FPS
Streaming Quality: 75%
Запись: отключена
Снимки: отключены
Детекция движения: отключена
Обе камеры умеют отдавать MJPEG. Камера сама сжимает кадры, поэтому USB-шина и CPU нагружаются меньше, чем при несжатом YUYV.
v4l2-ctl -d /dev/video0 --list-formats-ext
v4l2-ctl -d /dev/video2 --list-formats-ext
🧪 Проверка после запуска
systemctl status motioneye
pgrep -af motion
ss -lntp
Motion создаёт отдельные MJPEG-потоки. Текущие кадры можно проверить локально из контейнера:
curl -o camera1.jpg http://127.0.0.1:9081/current
curl -o camera2.jpg http://127.0.0.1:9082/current
file camera1.jpg camera2.jpg
Оба файла должны определяться как JPEG нужного разрешения. После этого проверяем USB, UVC и сам сервис:
journalctl -k | grep -Ei 'usb|uvc|video'
journalctl -u motioneye
📊 Нагрузка и границы решения
В ходе теста оба потока 1280x720 / MJPEG / 30 FPS работали одновременно. Ошибок USB, повреждённых кадров и отключений камер не наблюдалось.
Для обычного live-view ресурсов LXC хватает с большим запасом. Основная нагрузка появится, если включить:
- постоянную запись;
- перекодирование в H.264/H.265;
- детекцию движения;
- распознавание объектов;
- длительное хранение архива.
Для такого сценария логичным следующим шагом будет Frigate с аппаратным ускорением Intel Quick Sync или OpenVINO. Но для текущей задачи Motion и motionEye проще и достаточно.
🛠️ Что ещё стоит улучшить
- Создать
udev-правила для стабильной привязки камер. - Ограничить motionEye локальной сетью и VPN.
- Добавить HTTPS через reverse proxy.
- Разделить учётные записи администратора и просмотра.
- Включить резервное копирование конфигурации LXC.
- Добавить мониторинг доступности обеих камер.
- При включении записи вынести архив на отдельное хранилище.
- Проверить автовосстановление после физического отключения USB.
🏁 Итог
Получилась простая и изолированная система просмотра двух USB-камер:
USB cameras
|
v
Proxmox VE
| device passthrough
v
Unprivileged LXC
|
+-- Motion
+-- motionEye
|
v
Web UI through VPN
Такой вариант хорошо подходит для HomeLab, удалённой площадки, серверной или технического помещения. Он почти не нагружает сервер, не требует отдельных IP-камер и оставляет понятный путь к записи, событиям и детекции объектов.