Open-source продукт. README обещает «деплой за 10 минут». Человек клонирует, запускает — 502. Потому что мейнтейнеры держат nginx с хардкоженым proxy_pass и забыли написать об этом.
Или хуже: в публичный коммит однажды улетел API-ключ, потому что prod-конфиг жил в том же репо.
Это один и тот же баг — одно репо пытается делать две разные работы.
Папка examples/ не спасает
Первая реакция всегда одинаковая: «разделим — вот пример, вот настоящее». Появляются examples/, docs/deployment-example/, заметки в README.
Не работает. Через месяц пример расходится с реальностью. Никто не идёт обновлять examples/nginx.conf, когда ночью фиксит настоящий конфиг. Через год пример сломан — новые пользователи снова получают 502.
Дело не в лени. Просто у этих двух вещей разный жизненный цикл, и жить вместе они не умеют.
Это две разные вещи
Шаблон деплоя, который копирует пользователь — это часть продукта. Он generic, с {{ DOMAIN }} и ${PORT} вместо реальных значений. Обновляется когда меняется сам продукт. Его видят контрибьюторы, его проверяет CI. Это публичное.
Твой реальный деплой — это твоё. Конкретные домены, сертификаты, IP, секреты. Обновляется когда надо. Никто, кроме тебя, знать это не должен. Это приватное.
Смешать их в один репо — как в одной схеме держать production-базу и учебный SQL-туториал. Сломаешь или одно, или другое.
Три варианта, которые не работают
.gitignore на приватные файлы. Одна опечатка — и секрет в публичной истории навсегда. git clean -fdx снесёт всю инфру. Мина под собой.
Submodule на приватный репо. Факт его существования торчит из .gitmodules. Контрибьюторы не склонируются. И submodule — это отдельная боль: pinning, рассинхрон.
«Просто напишем в доках». Расходится с реальностью за 2–3 деплоя. В 3 часа ночи никто не открывает PR в docs.
Что работает
Два отдельных репо. Без связей в коде.
Публичный — монорепо продукта. Там templates/nginx/, templates/deploy/ с плейсхолдерами и README. Видят все, проходит ревью, версионируется с продуктом.
Приватный — твоя реальная инфра. Реальные домены, сертификаты, секреты, CI/CD для деплоя на VPS. Ссылок из публичного нет — пользователям не нужно знать, что он вообще существует.
Единственная связь — в доках приватного: «деплоит github.com/<org>/<product>, основан на templates/nginx/». Публичный про приватный ничего не знает.
Когда это окупается
На старте схема дороже: два места для коммитов, два workflow. Окупается в трёх случаях.
Открытый исходник. Если код видит мир — это не «если утечёт», а «когда».
Первый клиент. Когда кто-то начинает деплоить твой продукт у себя, ему нужен чистый шаблон без твоих конкретных доменов. Иначе он копирует твой kblabs.ru.conf и потом разбирается, почему упало.
Команда растёт. Джуну нельзя давать SSH к проду, но PR в templates/nginx/ — можно. Разделение позволяет делегировать без страха.
Когда я настраивал nginx для kblabs.ru, был соблазн положить всё в infra/ в монорепо. Не стал. Теперь у нас templates/nginx/ (публичный, generic) и kb-labs-infra (приватный, с реальными секретами). Когда первый клиент попробует самохостить — у него будет что копировать, не нужно расшифровывать мой конкретный конфиг.
Один репо не должен делать две работы.