Домашнее облако на старом Jetson Nano: Nextcloud, Immich, CGNAT и три сбоя USB

Домашнее облако на старом Jetson Nano: Nextcloud, Immich, CGNAT и три сбоя USB

Прокомментировать Просмотры: 16

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

В итоге на скромных четырех гигабайтах оперативной памяти успешно запедали Nextcloud, Immich, семейный мессенджер, система мониторинга и кастомный REST API. Самым капризным компонентом оказались вовсе не Docker или ограничения CGNAT, а USB-накопитель: три последовательных сбоя заставили перетряхнуть схему подключения, конфигурацию загрузки и правила обработки репозитория.

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

Чего хотелось добиться

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

Серьезным препятствием стал CGNAT провайдера, из-за которого прямой проброс портов на роутере оказался невозможен. У меня уже имелся VPS с белым IP-адресом, поэтому архитектура построена вокруг исходящего SSH-туннеля: мини-компьютер самостоятельно устанавливает соединение с облачным сервером, а nginx на стороне VPS принимает входящий пользовательский трафик.

Использованное «железо»

Компонент

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

Происхождение

NVIDIA Jetson Nano Dev Kit

4 ГБ LPDDR4, архитектура ARM64, графический чип Maxwell

Остался после увлечения робототехникой

Системный накопитель

microSD на 64 ГБ

Извлечен из старого смартфона

Основное хранилище

SSD на 250 ГБ (доступно 229 ГБ), файловая система ext4

Демонтирован при апгрейде ноутбука

VPS (удаленный сервер)

Ubuntu 24.04, 2 ГБ RAM

Арендован ранее для сопутствующих задач

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

Выделение статичного IP для Jetson в локальной сети

Обычный бытовой роутер

Архитектурные ограничения

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

Вторым сдерживающим фактором стали особенности JetPack и устаревшая версия Docker 20.10.7. Обновлять Docker отдельно от базового системного ПО чревато сбоями, поскольку к JetPack привязаны драйверы платформы, а экосистема ARM64 располагает далеко не всеми готовыми образами. В результате я осознанно отказался от громоздких универсальных NAS-дистрибутивов в пользу модульной сборки из отдельных контейнеров.

Функции машинного обучения в 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]

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

Использовать WireGuard в моем случае мешали нюансы ядра Tegra 4.9, а Tailscale конфликтовал с уже развернутым VPN-клиентом на смартфонах. Поэтому я остановился на проверенном и неприхотливом решении — autossh с принудительным перезапуском.

Полноценный домен пока не приобретен. Для мобильных клиентов под Android я сгенерировал самоподписанный сертификат с прописанными в SAN IP-адресами и выделил индивидуальные HTTPS-порты. Это временная мера: как только появится доменное имя, я перейду на стандартные сертификаты Let’s Encrypt.

Перечень запущенных Docker-контейнеров

Контейнер

Назначение

Порт

Лимит RAM

nextcloud

Облачное хранилище, контакты, календарь, Talk

8080

512 МБ

nextcloud_db

СУБД PostgreSQL 16

512 МБ

nextcloud_redis

Кэширование Redis

64 МБ

immich_server

Сервер фотогалереи

2283

1024 МБ

immich_db

PostgreSQL для Immich

384 МБ

immich_redis

Кэш Immich

64 МБ

immich_microservices

Фоновые процессы фотохостинга

512 МБ

llm_gateway

Шлюз для нейросетевых API

8090

256 МБ

nas_jetson_nano_api

Кастомный FastAPI-сервис управления

8099

128 МБ

samba

Файловый доступ строго из LAN

445

netdata

Мониторинг производительности в реальном времени

19999

256 МБ

uptime_kuma

Контроль доступности эндпоинтов

3001

128 МБ

portainer

Веб-интерфейс Docker

9000

128 МБ

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

Хронические проблемы с USB

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

1. RTL9210B-CG, функция autosuspend и ошибка -71

Первый бокс для диска вел себя прилично пару дней, после чего в системном логе dmesg посыпались фатальные сбои ввода-вывода:

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 МБ/с, после чего интерфейс зависал. Контейнер пришлось сменить, купив бокс на чипсете JMS583.

2. Физический износ USB-порта

После установки нового корпуса проблема возобновилась. Источник оказался до смешного банальным: четвертый порт USB на самой плате Jetson имел плохой контакт. Стоило переставить SSD во второй порт, как ошибки исчезли. Номер исправного разъема я задокументировал и прописал в параметрах сторожевого таймера.

3. Проблема с окончаниями строк (CRLF) в bash-скриптах

Часть служебных сценариев и юнитов systemd я правил в Windows. При попытке запуска на Jetson они выдавали ошибку:

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

Виной всему оказались невидимые символы перевода строки в стиле Windows (CRLF). Проблема решилась добавлением в репозиторий правила:

* text=auto eol=lf

Теперь Git принудительно приводит все скрипты к стандарту Linux (LF) при клонировании.

Финальная конфигурация диска

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

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

Многоуровневый мониторинг

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

  • Ежедневные сводки в Telegram;

  • Графики долговременной статистики в Beszel Hub;

  • Автоматические интеграционные тесты с помощью утилиты goss.

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

Отчет содержит данные об аптайме, степени заполнения дисков, температурах чипов, рестартах контейнеров и результатах пинга локальных и внешних эндпоинтов.

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

После любых серьезных правок я запускаю проверку:

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 прекрасен, когда нужно зайти глазами в консоль и вручную проверить состояние контейнеров. Однако для автоматизации этого мало. Требовалась единая точка входа, через которую внешний скрипт или бот мог бы запрашивать телеметрию, статистику фотохостинга и выполнять строго регламентированные команды.

Так родился сервис на FastAPI, реализующий два десятка эндпоинтов. Основные категории запросов:

Категория

Возвращаемые данные или выполняемое действие

System

Нагрузка на CPU/RAM, температура кристаллов, аптайм, статус дисков

Containers

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

Talk

Активные чаты, список участников, отправка пушей

Photos

Счетчики фото и видеоматериалов в Immich

Actions

Перезапуск контейнеров из белого списка и запуск бэкапов

Все ответы строго типизированы с помощью Pydantic-моделей, поэтому формат выдачи неизменен. Потенциально деструктивные операции защищены списками разрешенных команд.

Интерфейс Swagger UI для кастомного NAS API

Swagger UI
Swagger UI

Роль ИИ-ассистента в разработке

Нейросетевой помощник выступал инструментом реализации, но не автором архитектурных концепций. Я ставил задачу, описывал текущее положение дел, валидировал предложенный код и утверждал итоговые изменения.

Максимальную пользу ассистент принес в трех сценариях:

  • Генерация однотипных конфигураций Docker Compose, nginx и служебных файлов systemd;

  • Написание тестовых сценариев, сопутствующей документации и инструкций на случай отката изменений;

  • Анализ альтернативных решений при уже известных аппаратных ограничениях.

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

Без участия человека ИИ не смог бы диагностировать физический дефект разъема или узнать, какие критические службы на VPS нельзя перезагружать. Манипуляции с файрволом, таблицей разделов fstab, секретными ключами и потенциально опасные команды я всегда выполнял самостоятельно.

Типичный запрос к ассистенту выглядел следующим образом:

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

Безопасность и слабые места

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

Тем не менее, проект пока нельзя назвать эталоном отказоустойчивости:

  • Версия Docker 20.10.7 безнадежно устарела, а ее апдейт завязан на зависимости JetPack;

  • Самоподписанные TLS-сертификаты создают лишнюю головную боль при подключении новых гаджетов;

  • Внеплощадочное резервное копирование отсутствует, поэтому единственный SSD остается единой точкой отказа;

  • Модули машинного обучения Immich отключены из-за дефицита памяти.

Итоговые показатели

Метрика

Фактический результат

Docker-окружение

Запущено 13 контейнеров, для 12 из них настроены healthcheck-проверки

Медиатека

6622 фотографии и 357 видео по состоянию на 16.07.2026

Контакты

2151 запись, синхронизируемая через CardDAV/DAVx⁵

Семейный чат

Активны 5 пользователей

Тестирование

40 из 40 интеграционных тестов проходят успешно

Дисковая подсистема

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

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

Работа над ошибками: как я поступил бы сейчас

  • Сразу купил проверенный USB-бокс. Борьба с аппаратными сбоями диска отняла больше времени, чем вся остальная конфигурация софта.

  • Добавил бы файл .gitattributes в самый первый коммит. Это уберегло бы меня от проблем с символами CRLF на плате Jetson.

  • Оформил бы файл AGENTS.md до начала работ. Контекст железа и ограничения пришлось растолковывать нейросети заново несколько раз.

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

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

Заключение

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

Главный урок, который я вынес: надежность подобных самодельных систем определяется вовсе не количеством запущенных контейнеров. Куда критичнее стабильное питание, качественный USB-мост, здоровье файловой системы, механизмы самовосстановления и актуальные бэкапы. Именно на борьбу с этими нюансами ушла львиная доля времени.

Исходные конфигурации Docker Compose, юниты systemd, тесты и журнал принятых решений опубликованы в профильном репозитории на GitHub.

 

Источник

Поделиться:

Похожие статьи

Поиск по играм, новостям и статьям…

Введите не менее двух символов

Введите не менее двух символов