Перейти к содержимому

Разработка3 мин чтения

Один VPS на весь сайт студии: Next.js, Go, PostgreSQL и Dokploy

Как onlyapps.org работает на одном сервере — Go API, очередь задач внутри PostgreSQL, Next.js для сайта и консоли, Dokploy для деплоя — и четыре урока дня запуска.

Новый onlyapps.org — публичный сайт, консоль, в которой его редактирует наша команда, API и все фоновые задачи — работает на одном VPS. Без управляемых баз данных, без Redis, без внешнего CI. Рассказываем, как всё устроено и что мы узнали при запуске.

Из чего состоит

ЧастьТехнологииЧто делает
СайтNext.js 16Серверный рендеринг страниц на английском, сербском и русском
КонсольNext.js 16Редакторы, публикация, настройки для команды
APIGo, chi, pgx, sqlcВся бизнес-логика, авторизация, медиа
ВоркерGo, RiverОтложенные публикации, письма, IndexNow, обновление кэша
База данныхPostgreSQL 18Данные и очередь задач
ПлатформаDokploy, Traefik, Let's EncryptСборка, маршрутизация и TLS на нашем VPS

Одна база для данных и задач

Фоновые задачи живут в PostgreSQL благодаря River — очереди задач для Go поверх Postgres. Главный выигрыш — транзакции: когда редактор публикует пост, изменение контента и запускаемые им задачи — обновить кэш сайта, уведомить IndexNow, отправить письма подписчикам — записываются в одной транзакции. Либо происходит всё, либо ничего, и не нужна вторая система, за которой надо следить.

Контент как данные, а не HTML

Посты и документы хранятся как структурированный JSON из редактора, а сайт отрисовывает их компонент за компонентом. HTML не хранится, поэтому его нечего очищать при выводе, а один и тот же контент одинаково выглядит на всех языках.

Быстрые страницы без CDN

Сайт использует cache components в Next.js. Страницы отдаются из кэша и инвалидируются по тегам в момент публикации — воркер вызывает сайт, и пересобираются только затронутые страницы. Медиа хранятся на Docker-томе и отдаются Go API со своего поддомена, а изображения конвертируются в более лёгкие варианты WebP или JPEG. В наших тестовых прогонах Lighthouse для мобильных показывает производительность в районе 90+ и 100 по SEO.

Деплой через Dokploy

Весь стек — один Docker Compose с шестью сервисами: сайт, консоль, API, воркер, PostgreSQL и контейнер бэкапов, который каждую ночь запускает pg_dump и хранит дампы за две недели. Dokploy забирает репозиторий, собирает образы на сервере и маршрутизирует домены через Traefik с сертификатами Let's Encrypt. Миграции базы применяются автоматически при старте API.

Четыре урока дня запуска

  1. Уникальные имена сервисов в общей сети. На сервере с множеством проектов Dokploy подключает публичные сервисы к одной общей Docker-сети. У других проектов там были свои сервисы api, web и postgres, так что обычный http://api:8080 мог попасть не в тот контейнер. Теперь внутренние адреса — уникальные алиасы вроде onlyapps-api.
  2. Домены — в одном месте. Наш compose-файл объявлял роутеры Traefik, а Dokploy генерировал свои для тех же хостов. Мы перенесли все домены в Dokploy, а в compose оставили только middleware — заголовки безопасности и редирект с www, каждый на своём сервисе.
  3. Большим загрузкам нужен долгий read timeout. Traefik v3 по умолчанию обрывает тело запроса через 60 секунд, и загрузка видео на медленных соединениях прерывалась. Помогло увеличение read timeout на entrypoint.
  4. Пароли внутри URL. Пароль базы, сгенерированный в base64, может содержать слэш — и строка подключения postgres:// ломается. Пароли в hex полностью снимают проблему.

Совет

Перед первым деплоем проверьте, не попадают ли ваши секреты внутрь URL, и если попадают — генерируйте их в hex.

Почему мы держим всё таким простым

Всё проверяется до выхода в прод: каждое изменение проходит линтер, unit- и интеграционные тесты, продакшен-сборку и end-to-end-тесты с проверкой доступности. В итоге получается стек, который небольшая команда понимает целиком, может перенести на другой сервер за вечер и содержит по цене одного VPS.

ПоделитьсяXLinkedIn

ONLYAPPS team

Нови-Сад, Сербия · onlyapps.org

Контакты

Аналитические cookie мы используем только с вашего согласия. Необходимые cookie обеспечивают работу сайта. Политика конфиденциальности