- BrainTools - https://www.braintools.ru -

Старому Jetson Nano — домашнее облако: Nextcloud, Immich, CGNAT и три USB‑сбоя

После переезда я нашёл в коробке NVIDIA Jetson Nano, который несколько лет лежал без дела. Покупать готовый NAS не хотелось, поэтому решил собрать домашнее облако из того, что уже было: Jetson, старого SSD и VPS.

В результате на четырёх гигабайтах памяти [1] заработали Nextcloud, Immich, семейный чат, мониторинг и собственный REST API. Самой сложной частью оказались не Docker и не CGNAT, а USB‑хранилище: три разных сбоя заставили переделать подключение диска, загрузочные параметры и правила работы с репозиторием.

Собранный стенд: Jetson Nano, USB-накопитель и домашний роутер

Собранный стенд: Jetson Nano, USB‑накопитель и домашний роутер

Что я хотел получить

Задача была бытовой: перестать постоянно расширять облачное хранилище Google и перенести домой фотографии, файлы и контакты. Полностью отказаться от внешних сервисов я не планировал. Хотел сократить объём личных данных, которые хранятся вне моей инфраструктуры, и при этом не превратить домашний сервер в отдельную работу.

Доступ к дому осложнял CGNAT. Пробрасывать порты на роутере было невозможно, но у меня уже был VPS с публичным адресом. Поэтому архитектура сложилась вокруг исходящего SSH‑туннеля: Jetson сам подключается к VPS, а nginx на VPS принимает запросы клиентов.

Исходное железо

Компонент

Что использовал

Откуда взялось

NVIDIA Jetson Nano Dev Kit

4 ГБ LPDDR4, ARM64, GPU Maxwell

Остался после занятий сына робототехникой

Системный диск

microSD 64 ГБ

Старая карта памяти от телефона

Хранилище

SSD 250 ГБ, доступно 229 ГБ, ext4

Остался после замены диска в ноутбуке

VPS

Ubuntu 24.04, 2 ГБ RAM

Уже использовался для домашних сервисов

Домашний роутер

Статический адрес Jetson в LAN

Обычный домашний роутер

Ограничения, которые определили архитектуру

На старте в системе не был настроен swap. Позже я включил четыре zram‑устройства общим объёмом около 2 ГБ, но это не превратило Jetson в машину с полноценными шестью гигабайтами памяти. Приходилось считать память и не держать сервисы «на всякий случай».

Второе ограничение — JetPack и старый Docker 20.10.7. Обновлять Docker отдельно от системного стека рискованно: от JetPack зависят компоненты платформы. Для ARM64 также есть не все контейнерные образы. Поэтому я отказался от тяжёлого универсального NAS‑дистрибутива и собрал систему из отдельных сервисов.

Машинное обучение [2] в Immich отключено:

IMMICH_DISABLE_MACHINE_LEARNING=true

На этом этапе важнее было обеспечить устойчивый автобэкап фотографий, чем распознавание лиц и объектов.

Архитектура и доступ через CGNAT

[Смартфон / браузер / приложение]
         |
         | HTTPS / HTTP
         v
[VPS]
  nginx
  :8080 / :8443  -> Nextcloud
  :2283 / :2443  -> Immich
  :8090 / :9443  -> LLM Gateway
  :8099          -> NAS API
         |
         | reverse SSH tunnel (autossh)
         v
[Jetson Nano, домашняя сеть]
  Nextcloud + PostgreSQL + Redis
  Immich + PostgreSQL + Redis
  Samba, Netdata, Uptime Kuma, Portainer
  NAS API, LLM Gateway
         |
         | USB 3.0
         v
[JMS583 SSD, ext4, /mnt/storage]

Jetson не принимает входящие подключения из интернета. Сервис autossh поднимает обратный туннель до VPS и восстанавливает его после разрыва. На VPS работает nginx, который проксирует трафик к локальным портам туннеля.

В моей конфигурации WireGuard упирался в особенности ядра Tegra 4.9, а Tailscale конфликтовал с уже используемым VPN на Android. Поэтому я оставил более простой для этого стенда вариант — autossh с автоматическим перезапуском.

Домена пока нет. Для Android‑клиентов сделал самоподписанный сертификат с IP‑адресами в SAN и отдельные HTTPS‑порты. Это рабочее, но временное решение: при появлении домена планирую перейти на обычный сертификат Let’s Encrypt.

Что работает в Docker

Контейнер

Назначение

Порт

Лимит памяти

nextcloud

Файлы, контакты, календарь, Talk

8080

512 МБ

nextcloud_db

PostgreSQL 16

512 МБ

nextcloud_redis

Redis

64 МБ

immich_server

Фотоархив

2283

1024 МБ

immich_db

PostgreSQL для Immich

384 МБ

immich_redis

Redis

64 МБ

immich_microservices

Фоновые задачи Immich

512 МБ

llm_gateway

Шлюз AI‑сервисов

8090

256 МБ

nas_jetson_nano_api

Собственный FastAPI

8099

128 МБ

samba

Файловый доступ только из LAN

445

netdata

Текущие метрики

19 999

256 МБ

uptime_kuma

Проверка доступности

3001

128 МБ

portainer

Ручное управление Docker

9000

128 МБ

Для контейнеров заданы mem_limit, healthcheck и политика перезапуска. На четырёх гигабайтах это не оптимизация «на будущее», а защита: один неограниченный процесс способен вытеснить остальные сервисы.

Три USB‑сбоя

До этого момента проект был обычной сборкой сервисов. Инженерная работа началась, когда несколько дней подряд отваливался SSD.

1. RTL9210B‑CG, autosuspend и error -71

Первый USB‑энклоужер несколько дней работал нормально, а затем в dmesg появились отключения и ошибки [3] ввода‑вывода:

usb 2-1.3: USB disconnect, device number X
sd 0:0:0:0: [sda] tag#0 FAILED Result: hostbyte=DID_ERROR...

Проблема проявлялась после простоя. Отключение autosuspend помогло частично, но при записи через UAS скорость постепенно падала примерно до 40 МБ/с, затем появлялись stream errors. В итоге я заменил энклоужер на JMS583.

2. Физически нестабильный USB‑порт

После замены энклоужера симптомы повторились. Причина оказалась проще: четвёртый USB‑порт на конкретной плате был нестабилен. После переноса SSD во второй порт ошибки исчезли. Номер рабочего порта я зафиксировал в конфигурации watchdog и в документации проекта.

3. CRLF в bash‑скриптах

Часть systemd‑юнитов и скриптов я готовил на Windows. После переноса на Jetson они не запускались:

Failed to execute /usr/bin/env bash
: No such file or directory

Причина — окончания строк CRLF. Для репозитория добавил правило:

* text=auto eol=lf

После этого Git приводит скрипты к LF при checkout в Linux.

Итоговая конфигурация SSD

# /boot/extlinux/extlinux.conf, параметр APPEND
usb-storage.quirks=152d:a583:u usbcore.autosuspend=-1

Флаг u переключает JMS583 из UAS в BOT. На этом стенде после настройки получились 250 МБ/с на запись и 172 МБ/с на чтение без новых USB‑ошибок в dmesg. Дополнительно настроены увеличенный SCSI timeout, автоматическое монтирование и восстановление контейнеров после переподключения диска.

Мониторинг: не только «контейнер запущен»

Мне было важно понимать не только текущее состояние, но и что происходило ночью или перед сбоем. Для этого использовал три уровня контроля:

  • ежедневный отчёт в Telegram;

  • исторические графики Beszel Hub;

  • инфраструктурные тесты goss после изменений.

Ежедневный Telegram‑отчёт: система, температуры, контейнеры и доступность сервисов
Ежедневный Telegram-отчёт: система, температуры, контейнеры и доступность сервисов

Ежедневный Telegram‑отчёт: система, температуры, контейнеры и доступность сервисов

В отчёте видны uptime, использование памяти и диска, температуры, перезапуски контейнеров, локальные HTTP‑проверки и доступность через VPS.

Beszel Hub: Jetson Nano и VPS доступны

Beszel Hub: Jetson Nano и VPS доступны
Исторические метрики Jetson: общая нагрузка и потребление памяти контейнерами

Исторические метрики Jetson: общая нагрузка и потребление памяти контейнерами

После существенных изменений запускаю goss:

goss validate --gossfile tests/goss/goss.yaml

Count: 40, Failed: 0, Skipped: 0

Тесты проверяют порты, systemd‑сервисы, файлы и HTTP‑эндпоинты. Это не заменяет ручную проверку, но быстро показывает, что именно сломалось после очередной правки.

Клиенты: Immich, DAVx⁵ и Nextcloud Talk

Большинство семейных телефонов — Xiaomi с MIUI или HyperOS. Без настройки автозапуска и исключения приложений из энергосбережения Immich переставал загружать фотографии в фоне. Поэтому для каждого пользователя сделал короткую инструкцию без серверных терминов.

Настройка DAVx⁵ и Immich на Android. Скриншот отражает состояние на момент настройки

Настройка DAVx⁵ и Immich на Android. Скриншот отражает состояние на момент настройки

На историческом снимке было загружено 6697 из 6719 объектов. При отдельной проверке 16 июля 2026 года в базе находились 6622 изображения и 357 видео — 6979 активных объектов. Контакты синхронизировались через DAVx⁵; в адресной книге было 2151 запись.

Для семейного чата использовал Nextcloud Talk. Группа состоит из пяти человек, история хранится на домашнем SSD.

Групповой семейный чат в Nextcloud Talk

Групповой семейный чат в Nextcloud Talk
Дашборд Nextcloud с файлами, контактами и чатом

Дашборд Nextcloud с файлами, контактами и чатом

REST API поверх сервисов

Portainer удобен, когда я сам открываю браузер и вручную проверяю контейнеры. Для автоматизации этого оказалось недостаточно. Нужна была одна точка, через которую скрипт или бот может получить состояние системы, статистику Immich и выполнить только заранее разрешённое действие.

Так появился FastAPI‑сервис с 20 HTTP‑операциями. Основные группы:

Группа

Что возвращает или выполняет

System

Память, процессор, температура, uptime, состояние диска

Containers

Список контейнеров и число перезапусков

Talk

Комнаты, участники, отправка уведомлений

Photos

Статистика изображений и видео в Immich

Actions

Перезапуск контейнера из whitelist и запуск резервного копирования

Ответы описаны Pydantic‑моделями, поэтому формат не меняется от запроса к запросу. Опасные операции ограничены списком разрешённых действий.

Swagger UI собственного NAS API

Swagger UI

Swagger UI

Как использовал AI‑агента

AI‑агент был инструментом разработки, а не источником технических решений. Я задавал цель, показывал текущее состояние системы, проверял команды и принимал итоговое решение.

Лучше всего агент помогал в трёх типах задач:

  • создание однотипных Docker Compose, nginx и systemd‑файлов;

  • подготовка тестов, документации и rollback‑инструкций;

  • сравнение вариантов, когда ограничения уже были известны.

Ключевым файлом стал AGENTS.md. В нём записаны ограничения конкретного стенда: не трогать работающие VPN‑контейнеры на VPS, не использовать нестабильный USB‑порт, сохранять bash‑скрипты только с LF, проверять монтирование SSD перед резервным копированием.

Без человека агент не определил бы физически неисправный порт и не знал бы, какие сервисы на VPS нельзя останавливать. Конфигурации firewall, fstab, секреты и команды с риском потери данных я проверял сам.

Один из рабочих запросов выглядел так:

Подними Nextcloud и Immich на Jetson Nano.
PostgreSQL для обоих. Данные храни в /mnt/storage.
Учти ограничения: 4 ГБ RAM, старый Docker, ARM64.
Перед изменениями покажи команды проверки и способ отката.

Безопасность и известные ограничения

Внешние сервисы доступны только через VPS и обратный туннель. На самом Jetson входящие порты из интернета не открыты. Samba разрешена только в домашней сети. NAS API требует авторизацию, а команды управления контейнерами проходят через whitelist.

При этом проект нельзя считать законченным с точки зрения [4] отказоустойчивости:

  • Docker 20.10.7 устарел, а его обновление ограничено зависимостями JetPack;

  • самоподписанный TLS усложняет подключение новых устройств;

  • off‑site‑копия пока не настроена, поэтому один SSD остаётся единой точкой отказа;

  • машинное обучение Immich отключено из‑за ограничений памяти.

Результат

Показатель

Зафиксированный результат

Docker

13 контейнеров запущены; для 12 настроен healthcheck

Фотоархив

6622 изображения и 357 видео по проверке от 16.07.2026

Контакты

2151 запись через CardDAV/DAVx⁵

Семейный чат

5 участников

Инфраструктурные тесты

40 из 40 в историческом прогоне

SSD

250 МБ/с запись, 172 МБ/с чтение после настройки JMS583

Веб-интерфейс семейного фотоархива Immich

Веб‑интерфейс семейного фотоархива Immich

Что я сделал бы иначе

  • Сразу выбрал бы проверенный USB‑энклоужер. Два аппаратных инцидента заняли больше времени, чем развёртывание основных сервисов.

  • Добавил бы .gitattributes в первый коммит. Тогда CRLF не попал бы на Jetson.

  • Создал бы AGENTS.md до первой сессии. Контекст оборудования и запреты пришлось несколько раз объяснять заново.

  • Сразу завёл бы домен. Самоподписанный сертификат работает, но усложняет подключение родственников.

  • Раньше занялся бы второй копией данных. Домашнее облако без независимого бэкапа остаётся экспериментом, даже если работает стабильно.

Вывод

Старый Jetson Nano оказался достаточен для домашнего облака, которым семья действительно пользуется. Ограничения четырёх гигабайт памяти заставили аккуратно выбирать сервисы, но не помешали запустить Nextcloud, Immich, мониторинг и собственный API.

Главный вывод для меня — надёжность такого проекта определяется не количеством контейнеров. Гораздо важнее питание, USB‑мост, файловая система, автоматическое восстановление и резервная копия. Именно эти вещи заняли больше всего времени.

Исходный код, Docker Compose, systemd‑юниты, тесты и журнал решений опубликованы в репозитории на Гитхабе [5].

Автор: Alexey_git

Источник [6]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/33530

URLs in this post:

[1] памяти: http://www.braintools.ru/article/4140

[2] обучение: http://www.braintools.ru/article/5125

[3] ошибки: http://www.braintools.ru/article/4192

[4] зрения: http://www.braintools.ru/article/6238

[5] опубликованы в репозитории на Гитхабе: https://github.com/AlexeyBorovskoy/NAS_Jetson_Nano

[6] Источник: https://habr.com/ru/articles/1062914/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1062914

www.BrainTools.ru

Rambler's Top100