Делатель (Doer) — установка на своём сервере
Раннбук для администратора: как развернуть, обновить, забэкапить и восстановить приложение. Руководство пользователя — отдельный документ.
«Делатель» ставится целиком у вас: приложение, база и вход живут в четырёх контейнерах на одной машине. Наружу смотрит один порт, и всё за ним закрыто паролем. Никаких обращений к внешним сервисам при установке и работе нет — интернет нужен, только если вы сами подключите внешний календарь или ИИ-помощника.
Требования
| Минимум | |
|---|---|
| ОС | Linux x86-64 (Ubuntu 22.04/24.04, Debian 12, РЕД ОС, Astra Linux) |
| Docker | 20.10 или новее |
| Docker Compose | v2 (команда docker compose, не docker-compose) |
| Память | 2 ГБ |
| Процессор | 2 ядра |
| Диск | 5 ГБ свободно |
Дополнительно на хосте нужна база часовых поясов (tzdata, в дистрибутивах
Linux она есть по умолчанию): по ней контейнер расписаний понимает местное время.
Установка
tar xzf doer-onprem-<версия>.tar.gz
cd doer-onprem-<версия>
sh install.sh
Установщик проверит окружение, загрузит образы из архива, создаст .env со
случайными паролями, спросит логин и пароль для входа, поднимет стек и дождётся,
пока приложение ответит. Пароль входа он показывает один раз (если вы
доверили его сгенерировать) — запишите сразу.
Дальше приложение открывается по адресу http://<адрес машины>:8080/. Порт
меняется переменной HOST_PORT в .env.
Установка без вопросов (например, из своего скрипта):
HOST_PORT=8080 DOER_LOGIN=ivan DOER_PASSWORD='…' sh install.sh
Что появилось в каталоге
| Файл | Что это |
|---|---|
.env | все настройки и секреты установки; права 600, в бэкап не кладите вместе с базой в общий доступ |
nginx/htpasswd | логины и пароли входа |
nginx/doer.conf | конфигурация двери, там же выключенный блок TLS |
scheduler/crontab | расписания уведомлений |
tls/ | место для ваших сертификатов |
docker-compose.yml | описание стека |
Проверка после установки
docker compose ps # все четыре сервиса running/healthy
curl -si http://127.0.0.1:8080/ | head -n 1 # 401 без пароля
curl -su ivan:'пароль' http://127.0.0.1:8080/api/health # {"status":"ok"}
/api/health отвечает ok только когда приложение действительно работает с
базой; по этой же ручке docker следит за контейнером и не пускает трафик на
неподнявшееся приложение.
Обновление
Новая версия приезжает таким же архивом.
cd /путь/к/установке
docker compose down # остановить (данные в томе остаются)
tar xzf ~/doer-onprem-<новая версия>.tar.gz --strip-components=1 \
--exclude='.env' --exclude='nginx/htpasswd'
gunzip -c doer-onprem-images.tar.gz | docker load
sed -i "s|^APP_IMAGE=.*|APP_IMAGE=$(cat IMAGE)|" .env
docker compose up -d
Миграции базы приложение накатывает само при старте — отдельного шага нет. Перед обновлением сделайте бэкап (ниже): откат к прошлой версии образа возможен всегда, а откат схемы базы — только из дампа.
Откат: вернуть в .env прежний тег APP_IMAGE и docker compose up -d app.
Бэкап
Бэкап — это дамп базы плюс файл .env (в нём пароль базы, без него дамп не
развернуть в тот же стек).
cd /путь/к/установке
. ./.env
docker compose exec -T postgres pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \
| gzip > ~/doer-backup-$(date +%F).sql.gz
Чтобы это происходило само, добавьте строку в системный крон (пример: каждый день в 03:35, хранить 14 дней):
35 3 * * * cd /путь/к/установке && . ./.env && docker compose exec -T postgres pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" | gzip > /var/backups/doer/doer-$(date +\%F).sql.gz && find /var/backups/doer -name 'doer-*.sql.gz' -mtime +14 -delete
Раз в месяц проверяйте, что дамп разворачивается: без такой проверки бэкап — это надежда, а не бэкап.
Восстановление
cd /путь/к/установке
. ./.env
docker compose stop app scheduler # чтобы никто не писал в базу
gunzip -c ~/doer-backup-2026-08-24.sql.gz \
| docker compose exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"
docker compose start app scheduler
Восстановление в чистую установку: сначала sh install.sh (поднимется пустая
база нужной схемы), потом положите старый .env (в нём пароль базы) и накатите
дамп командой выше.
Смена пароля входа
cd /путь/к/установке
# если в системе есть apache2-utils
htpasswd -B nginx/htpasswd ivan
# если нет — тем же openssl, что при установке (спросит пароль дважды)
printf '%s:%s\n' ivan "$(openssl passwd -apr1)" > nginx/htpasswd
docker compose exec nginx nginx -s reload
Несколько человек = несколько строк в файле (htpasswd -B дописывает,
перенаправление > перезаписывает — будьте внимательны со стрелкой).
Пароль знает только этот файл: в приложении своей учётной записи нет, и «забыл
пароль» решается заменой строки в nginx/htpasswd.
TLS
Дистрибутив работает по http: предполагается, что сертификатом и доменом занимаетесь вы. Два пути:
- Свой обратный прокси перед стеком. Тогда поставьте в
.envHOST_BIND=127.0.0.1, чтобы порт был доступен только с самой машины. - Сертификат внутрь nginx. Положите
fullchain.pemиprivkey.pemв каталогtls/, раскройте блок TLS в концеnginx/doer.conf, раскомментируйте публикацию порта 443 вdocker-compose.ymlи выполнитеdocker compose up -d nginx.
Базовая авторизация по http передаёт пароль в открытом виде: если приложение доступно не только из вашей локальной сети — TLS обязателен.
Уведомления
Push-уведомления (утренняя сводка, напоминания, финиш таймера) требуют выхода в интернет к сервисам Google и Apple. В закрытом контуре они работать не будут — приложение при этом полностью исправно, просто без уведомлений.
Если интернет есть: ключи VAPID установщик уже сгенерировал и положил в .env.
Серверная половина заработает сразу, а клиентская требует пересборки образа —
публичный ключ вшивается в бандл на сборке:
docker build --target production \
--build-arg NEXT_PUBLIC_VAPID_PUBLIC_KEY=<VAPID_PUBLIC_KEY из .env> \
-t doer-onprem:<версия> .
Расписания уведомлений (09:00 — сводка дня, каждые 5 минут — напоминания,
21:00 — вечерний итог, каждую минуту — финиш таймера) живут в контейнере
scheduler, файл scheduler/crontab. Время в нём местное, по TZ из .env.
Если что-то не работает
docker compose ps # кто жив, кто healthy
docker compose logs -f app # приложение (в том числе миграции на старте)
docker compose logs nginx # дверь и отказы входа
docker compose logs scheduler # расписания: строка на каждый запуск
docker compose exec postgres pg_isready -U doer
| Симптом | Куда смотреть |
|---|---|
| «Bad gateway» на всех страницах | docker compose logs app — приложение не поднялось; при первом старте миграции идут до полутора минут |
| Браузер не спрашивает пароль | сбился nginx/htpasswd или конфиг; docker compose logs nginx. Если страница «Doer закрыт паролем» показывается, а окна ввода нет — закомментируйте error_page 401 в nginx/doer.conf и docker compose exec nginx nginx -s reload |
| Пароль не подходит | строка пользователя в nginx/htpasswd; после правки нужен nginx -s reload |
Контейнер scheduler перезапускается | в логе — причина: не найден часовой пояс (tzdata на хосте) или пуст CRON_SECRET |
| Кончилось место | база растёт в томе docker: docker system df -v |
Полная остановка и удаление стека (данные в томе при этом остаются):
docker compose down
Удалить вместе с базой — docker compose down -v. Это необратимо.