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

Вот поэтому
Основной мотив — экономия. Ежемесячные платежи за виртуалку (≈ 2-3 тысячи рублей) быстро растут, а я не нуждаюсь в масштабируемости и гибкости хорошего облака, которым пользовался: мне нужен лишь небольшой набор сервисов (несколько приложений и хранилища для них, типа Postgres или Redis) для личного использования и пет-проектов.
- Фиксированная цена — мини-пк стоит не так дорого, потом расходы почти нулевые за счёт энергоэффективного процессора;
- Контроль — всё необходимое находится в прямом доступе в любое время;
- Безопасность — намного надёжнее, когда все данные находятся в соседней комнате;
- Полезный опыт — навыки администрирования Linux не бывают лишними.
Выбор железки
О том, какой сервер взять я думал не очень долго. Меня в первую очередь интересовал баланс между производительностью и ценой. Много хранилища мне нужно не было, как медиа-сервер для загрузки фильмов я сервер использовать не собирался, и в итоге выбор пал на какую-то китайскую железку с одного маркетплейса. Бонусом там шла лицензия винды, но я в неё даже не загружался — сразу снёс и поставил 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

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

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

Экран входа в 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:
- Изменить порт, на котором работает SSH-сервер:
Port 7891- Запретить подключение от имени пользователя root:
PermitRootLogin no- И с помощью пароля:
PasswordAuthentication no
PubkeyAuthentication yesФаервол
Если доступ к веб-сервисам нужен снаружи, то вот SSH можно смело ограничить локальной сетью:
sudo ufw allow from 192.168.0.0/16 to sshНастройка сети
Роутер
Чтобы иметь возможность стабильно подключаться к мини-пк по SSH и к компьютеру для Foundry нужно в настройках роутера зафиксировать локальные IP-адреса:

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

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