Развёртывание
slimTDS поддерживает три режима развёртывания. Выбирайте в зависимости от топологии вашей сети.
Режимы развёртывания
Заголовок раздела «Режимы развёртывания»| Режим | Compose-файлы | TLS | Реальный IP клиента |
|---|---|---|---|
dev | docker-compose.yml + авто-слияние docker-compose.override.yml | TLS обрабатывается локальным окружением (Caddyfile.dev работает с auto_https off) | локально (без прокси) |
cf_flex | docker-compose.yml + docker-compose.prod.cf.yml | Cloudflare терминирует TLS; Caddy слушает :80 | заголовок CF-Connecting-IP; Caddy доверяет IP-диапазонам Cloudflare |
cf_full | docker-compose.yml + docker-compose.prod.cf.yml | Cloudflare Origin Certificate; Caddy слушает :443 | заголовок CF-Connecting-IP; Caddy доверяет IP-диапазонам Cloudflare |
direct | docker-compose.yml + docker-compose.prod.direct.yml | Caddy auto-TLS через Let’s Encrypt; порты 80 + 443 должны быть публичными | только доверенные прокси (без промежуточного прокси) |
И cf_flex, и cf_full используют один и тот же Caddyfile.cf. Порт прослушивания контролируется CF_LISTEN_PORT, который entrypoint.sh ставит в 443 для cf_full и 80 для cf_flex.
Режим direct использует Caddyfile.direct, который берёт переменную окружения $DOMAIN для адреса сайта Caddy и устанавливает ACME-email в admin@{$DOMAIN}.
Как выбирается режим
Заголовок раздела «Как выбирается режим»DEPLOY_MODE в .env — единственный источник истины. При старте контейнера docker/entrypoint.sh читает его и выбирает Caddyfile:
dev → config/frankenphp/Caddyfile.devcf_flex → config/frankenphp/Caddyfile.cf (CF_LISTEN_PORT=80)cf_full → config/frankenphp/Caddyfile.cf (CF_LISTEN_PORT=443)direct → config/frankenphp/Caddyfile.directЛюбое другое значение приводит к тому, что entrypoint.sh завершается с ошибкой.
Конфигурация (.env)
Заголовок раздела «Конфигурация (.env)»Скопируйте .env.example в .env командой make env, которая также генерирует случайные значения для APP_SECRET и ADMIN_PASSWORD. Ключевые переменные:
| Переменная | Назначение |
|---|---|
DEPLOY_MODE | Одно из dev, cf_flex, cf_full, direct |
DOMAIN | Публичный хостнейм (обязателен для режима direct; необязателен для cf_*) |
APP_SECRET | 64 hex-символа; подписывает сессии и CSRF-токены — ротация инвалидирует все сессии |
APP_TZ | Таймзона приложения, например Europe/Moscow. Сессии PostgreSQL устанавливаются в эту таймзону, поэтому чтения timestamptz возвращаются локализованными; хранение остаётся в UTC |
DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASSWORD | Подключение к PostgreSQL |
DB_DSN | Полный PDO DSN, собранный из переменных DB_* выше |
ADMIN_LOGIN | Имя администратора (по умолчанию admin) |
ADMIN_PASSWORD | Задаётся однократно командой admin:init; меняется через UI или admin:set-password впоследствии |
TRUSTED_PROXIES | Список IP/CIDR через запятую для режимов cf_* (обрабатывается автоматически в Caddyfile.cf; задаётся здесь для осведомлённости на уровне приложения) |
MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY | Бесплатные учётные данные GeoLite2 для гео-фильтров |
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID | Telegram-бот для ежедневной сводки и ежечасных оповещений |
FRANKENPHP_WORKER_MODE | 1 (по умолчанию, in-memory worker) или 0 (как CGI, проще отлаживать) |
Разработка
Заголовок раздела «Разработка»make env # скопировать .env.example и сгенерировать секреты (нужно один раз)make migrate # применить миграции Phinxmake up # запустить контейнеры db + app + cronОткройте /admin/login на хосте, где вы опубликовали slimTDS.
Продакшен — за Cloudflare
Заголовок раздела «Продакшен — за Cloudflare»-
Установите
DEPLOY_MODE=cf_full(илиcf_flex) в.env. ЗадайтеAPP_SECRET,ADMIN_PASSWORDи любые другие необходимые переменные. -
Примените миграции и инициализируйте аккаунт администратора:
Окно терминала docker compose -f docker-compose.yml -f docker-compose.prod.cf.yml run --rm app php bin/console admin:init -
Запустите стек:
Окно терминала make prod-up-cfmake prod-up-cfпроверяет, чтоDEPLOY_MODEначинается сcf, прежде чем продолжить. Он выполняет:Окно терминала docker compose -f docker-compose.yml -f docker-compose.prod.cf.yml up -d
Продакшен — direct (Caddy Let’s Encrypt)
Заголовок раздела «Продакшен — direct (Caddy Let’s Encrypt)»-
Установите
DEPLOY_MODE=directиDOMAIN=tds.example.comв.env. Порты 80 и 443 должны быть публично доступны. -
Примените миграции и инициализируйте аккаунт администратора:
Окно терминала docker compose -f docker-compose.yml -f docker-compose.prod.direct.yml run --rm app php bin/console admin:init -
Запустите стек:
Окно терминала make prod-up-directmake prod-up-directпроверяет иDEPLOY_MODE=direct, и чтоDOMAINзадан, прежде чем продолжить.
Остановка продакшена
Заголовок раздела «Остановка продакшена»make prod-downmake prod-down читает DEPLOY_MODE из .env и автоматически останавливает соответствующий compose-стек.
Детали первичной инициализации
Заголовок раздела «Детали первичной инициализации»Аккаунт администратора
Заголовок раздела «Аккаунт администратора»Первый администратор создаётся командой admin:init, которая читает ADMIN_LOGIN и ADMIN_PASSWORD из .env. Команда идемпотентна — она пропускается, если аккаунт уже существует:
docker compose exec app php bin/console admin:initПосле первого входа смените пароль на /admin/settings или через CLI:
docker compose exec app php bin/console admin:set-password admin <new-password>MaxMind GeoLite2
Заголовок раздела «MaxMind GeoLite2»Базы GeoLite2 не входят в комплект. Без них GeoLookup молча ничего не делает, и все условия гео-фильтров дают «не совпало» (ожидаемое поведение).
- Зарегистрируйте бесплатный аккаунт MaxMind
- Сгенерируйте лицензионный ключ и добавьте в
.env:MAXMIND_ACCOUNT_ID=123456MAXMIND_LICENSE_KEY=xxxxxxxxxxxx - Выполните однократную загрузку:
Это наполнит
Окно терминала docker compose --profile geo up geoipupdate./geoip-data/файламиGeoLite2-City.mmdb,GeoLite2-Country.mmdbиGeoLite2-ASN.mmdb.
Cron-задача geoip:check (запускается ежедневно в 06:00 UTC) логирует предупреждение и выходит с ненулевым кодом, если какой-либо файл .mmdb отсутствует или старше 14 дней. (Telegram-уведомление об устаревшем GeoIP доставляется отдельно ежечасной задачей telegram:alerts.)
Telegram-оповещения
Заголовок раздела «Telegram-оповещения»- Создайте бота через @BotFather и скопируйте токен.
- Узнайте свой chat ID (например, перешлите любое сообщение боту @userinfobot).
- Добавьте в
.env:TELEGRAM_BOT_TOKEN=1234567890:AAAAA...TELEGRAM_CHAT_ID=-100xxxxxxxx
Контейнер cron автоматически отправляет ежедневную сводку в 10:00 UTC и ежечасные системные оповещения.
Бэкап и восстановление
Заголовок раздела «Бэкап и восстановление»make backup# или:docker compose exec app php bin/console db:backupДампы пишутся в /app/var/backups/ внутри контейнера app в custom-формате PostgreSQL. Ежедневный cron (db:backup, 01:00 UTC) хранит файлы за последние 14 дней и автоматически удаляет более старые.
Восстановление
Заголовок раздела «Восстановление»# Список доступных дамповdocker compose exec app ls /app/var/backups/
# Восстановление (аргумент 'yes' подтверждает деструктивную операцию)docker compose exec app php bin/console db:restore slimtds_2026-04-24_03-00-00.dump yesРешение проблем
Заголовок раздела «Решение проблем»FrankenPHP в worker-режиме падает при старте
Заголовок раздела «FrankenPHP в worker-режиме падает при старте»Если возникает глобальная ошибка инициализации (например, плохое значение в .env или отсутствующая миграция), worker может падать в цикле перезапусков. Временно откатитесь на классический режим:
FRANKENPHP_WORKER_MODE=0Затем проверьте make logs на предмет ошибки PHP. Не забудьте снова включить worker-режим после исправления проблемы.
Базы GeoIP отсутствуют или устарели
Заголовок раздела «Базы GeoIP отсутствуют или устарели»docker compose --profile geo up geoipupdatemake test трогает мои dev-данные
Заголовок раздела «make test трогает мои dev-данные»Не должна. make test использует db-test — отдельный контейнер PostgreSQL с хранилищем tmpfs — поэтому dev-база никогда не затрагивается. Если подозреваете загрязнение, проверьте, что DB_DSN в .env указывает на dev-базу, а не на тестовую.
