- copy-in-place через yc storage s3 cp, 16 процессов по префиксам
Pet Project Server
Настройки виртуального сервера для домашних проектов.
В этом проекте не самые оптимальные решения. Но они помогают мне поддерживать сервер для моих личных проектов уже много лет.
История и обоснования значимых решений — в ADR-записях
(docs/adr/): почему приняты те или иные изменения, а не только что сделано.
Требования
Установка
uv sync
cp ansible-vault-password-file.dist ansible-vault-password-file
uv run ansible-galaxy install --role-file requirements.yml
Структура
- Для каждого приложения создается свой пользователь.
- Для доступа используется ssh-ключ.
- Безопасность осуществляется с помощью
ufwиfail2ban. - Докер используется для запуска и изоляции приложений. Для загрузки образов настраивается Yandex Docker Registry.
- Выход во внешнюю сеть через proxy server Caddy.
- Чувствительные данные в secrets.yml зашифрованы с помощью Ansible Vault.
- Для мониторинга за сервером устанавливается netdata.
Настройка DNS
В организации Яндекс: https://admin.yandex.ru/domains/vakhrushev.me?action=set_dns&uid=46045840
Деплой приложений
Деплой приложения через ansible:
uv run ansible-playbook -i production.yml --diff playbook-gitea.yml
Или через таску invoke:
./inv pl -- gitea
Удаление приложения
uv run ansible-playbook -i production.yml --diff playbook-remove-user-and-app.yml --extra-vars user_name=<name>
Бэкапы
Куда и как — ../ansible-shared-infra (playbook-rivendell.yml): restic,
resticprofile, rclone, репозитории yandex-rivendell (S3) и
pr86keedav-rivendell (webdav на домашнем роутере), ночной раннер
backup-apps (таймер в 04:00 по Москве; часы сервера — в UTC), бандлы
apprise в /etc/notify. Здесь — что бэкапить: плейбук приложения
вызывает роль backup_manifest, та пишет /etc/backup-apps/apps.d/<app>.toml.
Снапшот на приложение, тег = имя приложения. Решение —
ADR-2026-10-12.
| Приложение | Что бэкапится | Как |
|---|---|---|
| miniflux | backups |
pg_dump на ходу |
| outline | media, backups |
pg_dump на ходу |
| gitea | backups |
gitea dump на ходу |
| authelia | data |
контейнер остановлен (SSO лежит секунды) |
| calibre | books, config |
контейнер остановлен |
| diet | data |
контейнер остановлен |
| dozzle | data |
контейнер остановлен |
| goatcounter | data |
контейнер остановлен |
| gramps | data, media |
gramps и celery остановлены |
| memos | data |
контейнер остановлен |
| remembos | data |
контейнер остановлен |
| wanderer | data |
три контейнера остановлены |
| tuwunel | data |
на ходу (RocksDB): Matrix нужен живым для уведомлений |
| wakapi | data |
контейнер остановлен |
Не бэкапятся (восстанавливаются плейбуками и vault или не нужны): apprise, bifrost, caddyproxy, dashboard, goaccess, homepage, netdata, openobserve, rssbridge, transcriber.
Скрипты хуков — /usr/local/lib/backup-hooks/<app>/, root:root (шаблоны —
files/<app>/backup.template.sh). Хуки идут от пользователя приложения.
На хосте (от root):
backup-apps list # зарегистрированные приложения
backup-apps run gitea # бэкап одного приложения (без maintain)
systemctl list-timers backup-apps # когда следующий прогон
journalctl -u backup-apps # лог прогонов
backup restic yandex-rivendell snapshots --tag gitea
Восстановление
Общая процедура (найти снапшот, пробный restore, на место) — README
../ansible-shared-infra, «Восстановление». Каталоги приложений —
/srv/applications/<app>. Что важно на rivendell:
- Приложения с дампом восстанавливаются из дампа, а не из сырых файлов:
- miniflux, outline —
backups/postgres/*.sql.gz:gunzip -c <дамп> | docker compose exec -T <app>_postgres psql -U <user> <db>в пустую базу (контейнер приложения остановлен); - gitea — zip
gitea dumpвbackups/, по документации gitea («Backup and Restore»); - снапшоты authelia, gramps, memos, wakapi до 2026-10-12 — с дампами
gobackup в
backups/*.tar.gz(внутри SQL-дампы SQLite:sqlite3 <база> < <дамп>.sqlв новый файл базы); после — файлыdataпри остановленном контейнере.
- miniflux, outline —
- Остальные — файлы как есть, при остановленных контейнерах.
- Второй репозиторий
pr86keedav-rivendell— запасной; бывает недоступен.