dadyarri
01.12.2025
6 минут

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

Почему локальный сервер?

Вот поэтому

Вот поэтому

Основной мотив — экономия. Ежемесячные платежи за виртуалку (≈ 2-3 тысячи рублей) быстро растут, а я не нуждаюсь в масштабируемости и гибкости хорошего облака, которым пользовался: мне нужен лишь небольшой набор сервисов (несколько приложений и хранилища для них, типа Postgres или Redis) для личного использования и пет-проектов.

Выбор железки

О том, какой сервер взять я думал не очень долго. Меня в первую очередь интересовал баланс между производительностью и ценой. Много хранилища мне нужно не было, как медиа-сервер для загрузки фильмов я сервер использовать не собирался, и в итоге выбор пал на какую-то китайскую железку с одного маркетплейса. Бонусом там шла лицензия винды, но я в неё даже не загружался — сразу снёс и поставил Debian.

Мой выбор

Мой выбор

Подготовка операционной системы

За неимением лишнего монитора, мне пришлось временно перенести свой, чтобы базово настроить свежеустановленную систему. В первую очередь — подключение к интернету, обновление пакетов и установка SSH. Всё остальное можно будет сделать удалённо.

Как это выглядело в процессе

Как это выглядело в процессе

Установка софта

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

Запущенные контейнеры

Запущенные контейнеры

Многие приложения сохранились с прошлой статьи, но изменился способ деплоя. Раньше я использовал Nginx Proxy Manager — утилита, которая работала в отдельном контейнере и позволяла привязывать домены к портам на хосте и настраивать Let’s Encrypt сертификаты. Хотя оно работало, но это было не так удобно — оставался доступ к приложениям по HTTP через IP сервера и порт приложения, настройка происходила через упрощенный веб-интерфейс, который тоже нужно было высвечивать наружу.

В этой итерации лаборатории я использовал Caddy — веб-сервер, который можно подключать к сетям Docker и использовать контейнеры напрямую, без необходимости открывать порты. Кроме того, он автоматически занимается оформлением и продлением Let’s Encrypt сертификатов по умолчанию.

Задача Caddy простая — слушать 80 и 443 порты сервера и перенаправлять запросы на нужные приложения в зависимости от поддомена и/или пути:

services:
  caddy:
    image: caddy:alpine
    container_name: caddy
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - NET_BIND_SERVICE
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - $DATADIR/caddy/config:/etc/caddy
      - $DATADIR/caddy/site:/srv
      - $DATADIR/caddy/data:/data
    networks:
      - caddy

Сам конфиг Caddy при этом прост и понятен: домен и директива о том, на какой хост проксировать:

auth.example.com {
  reverse_proxy authentik_server:9000
}

photos.example.com {
  reverse_proxy immich-server:2283
}

Каждый контейнер, который должен быть доступен по своему поддомену, подключается к общей с веб-сервером сети (в моём случае это caddy). Тогда Docker автоматически определит IP-адрес контейнера по названию сервиса из Docker Compose. При этом ни одно приложение не светит портами наружу и по HTTP к ним нельзя подключиться напрямую.

При таком большом количестве контейнеров docker-compose.yml становится огромным, поэтому я разбил описания приложений на отдельные файлы и импортировал их в основной docker-compose.yml:

Организация файлов описания приложений

Организация файлов описания приложений

networks:
  default:
    driver: bridge
  caddy:
    name: caddy
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.90.0/24

include:
  - compose/caddy.yml
  - compose/postgresql.yml
  - compose/immich.yml
  - compose/linkwarden.yml
  - compose/docker-gc.yml
  - compose/authentik.yml
  - compose/vaultwarden.yml
  - compose/gatus.yml
  - compose/glance.yml
  - compose/paperless.yml
  - compose/outline.yml
  - compose/melody-track.yml
  - compose/minio.yml

Так же рядом с docker-compose.yml лежит общий .env файл, из которого все приложения могут брать нужные для себя переменные окружения. Сделано это так было потому, что есть общие для всех контейнеров переменные:

PUID=1000
PGID=1000
TZ="Europe/Moscow"
DATADIR="/home/dadyarri/docker/appdata"
HOSTNAME="homelab"

Галерея

В качестве галереи я решил оставить прежний Immich, который не так давно вышел из беты и наконец добрался до релиза. Кажется, сейчас это самый близкий по функционалу аналог Google Фото, даже с поддержкой распознавания лиц на базе локальной ИИ модели. При этом распознавание работает в фоне на процессоре (без видеокарты) и не сильно нагружает систему.

Экран входа в Immich

Экран входа в Immich

Главная страница галереи

Главная страница галереи

Авторизация

Некоторые приложения позволяют настроить SSO - аутентификацию с использованием единого аутентификатора. Такой механизм позволяет войти в аккаунт однажды и затем автоматически авторизовываться на всех поддерживаемых сервисах. В качестве такого аутентификатора я выбрал Authentik.

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

Экран входа в Authentik

Экран входа в Authentik

Менеджер паролей

Как и в прошлый раз, я использовал Vaultwarden - бесплатный клон Bitwarden с полностью совместимым API. Он может импортировать данные из облачного Bitwarden и бесплатно даёт тот функционал, за который на облачной версии нужно покупать подписку (генерацию одноразовых кодов для 2FA, например), кроме того, распространяется в виде одного Docker-контейнера, в отличие от официального Bitwarden, для которого нужно поднимать несколько тяжелых контейнеров.

Экран входа в Vaultwarden

Экран входа в Vaultwarden

SSO здесь находится в бете, но даже если его настраивать, мастер-пароль для расшифровки хранилища всё равно вводить понадобится. Кому-то это может показаться фичей, но мне такое поведение не очень нравится.

Дашборд

В Glance я собрал конфигурацию, которая позволяет мне отслеживать обновления в блогах и подкастах и в реальном времени наблюдать за загруженностью моего сервиса. Вообще там настроен виджет Youtube с лентой новых видео от избранных каналов, но по понятным причинам он работает примерно никогда.

Дашборд

Дашборд

Архив документов

В Paperless-ngx я храню электронные версии своих документов. Каждый из них можно быстро просмотреть и скачать, а найти нужный среди всех документов можно очень быстро с системой тегов и поддержкой распознавания текста (русский работает так себе)

Игровой стол

Я вернулся к проведению игр в D&D, поработав над своими ошибками, понаблюдав за другими мастерами и сейчас веду новый модуль. Нужно ещё немного поработать над темпом и увлечённостью игроков, но в целом нам нравится то, что получается сейчас.

Раньше я пытался разворачивать Foundry VTT на той же виртуалке, на которой у меня был весь остальной софт, чтобы игроки могли беспроблемно подключаться к игре. Это потребовало сильного увеличения расходов на сервер, поскольку Foundry раздаёт очень много изображений и динамического контента. В конечном итоге у меня не получилось достигнуть там нормальной производительности, поэтому я забил на попытку держать Foundry на виртуалке.

Теперь Foundry работает локально на моём большом компьютере, а Caddy проксирует трафик с домена, по которому подключаются игроки, на компьютер в той же локальной сети, что и мини-пк:

vtt.example.com {
  reverse_proxy 192.168.0.129:30001 {
    transport http {
      keepalive 30s
      versions 2 1.1
    }
  }
}

Безопасность

Возможно, вариант разворачивания сервисов с доступом к ним из публичного интернета не самый безопасный, но я предпочёл немного пожертвовать безопасностью в пользу удобства. Но какие-то меры предосторожности я всё же принял:

SSH

Самое очевидное, что можно сделать это ужесточить правила подключения к SSH:

  1. Изменить порт, на котором работает SSH-сервер:
Port 7891
  1. Запретить подключение от имени пользователя root:
PermitRootLogin no
  1. И с помощью пароля:
PasswordAuthentication no
PubkeyAuthentication yes

Фаервол

Если доступ к веб-сервисам нужен снаружи, то вот SSH можно смело ограничить локальной сетью:

sudo ufw allow from 192.168.0.0/16 to ssh

Настройка сети

Роутер

Чтобы иметь возможность стабильно подключаться к мини-пк по SSH и к компьютеру для Foundry нужно в настройках роутера зафиксировать локальные IP-адреса:

Резервирование адресов

Резервирование адресов

А чтобы по веб-портам (80/443) можно было стучаться снаружи локальной сети, нужно настроить переадресацию портов:

Перенаправление портов

Перенаправление портов

Итоги и планы на будущее

Наконец-то нашёл время на то, чтобы рассказать, как я настроил свою домашнюю лабораторию. Получилось достаточно удобное решение с возможностью быстро поднимать любые приложения с автоматической генерацией и продлением HTTPS сертификатов. На будущее у меня есть мысль добить работу Youtube на дашборде и расписать механизм резервного копирования приложений.

Назад