Fork me: starter for the Forgejo Actions deploy flow
Find a file
2026-07-17 10:32:06 +00:00
.forgejo/workflows chore(ci): forge -> git.extract.team 2026-07-17 08:28:07 +00:00
.env.example docs(env): document TWITTER_URL 2026-07-17 10:32:06 +00:00
.gitignore clean starter (phase 5) 2026-06-01 23:44:09 +00:00
README.md docs: EXTRA_DOMAINS + faster builds (re-apply after sync) 2026-07-17 08:31:54 +00:00

Deploy (Forgejo Actions)

Push в main → сайт на твоём домене с SSL. Один файл .env, один пуш.

Флоу на Forgejo Actions. Один .env + .forgejo/workflows/deploy.yml в репе — и push. Никакого GitLab.


TL;DR

  1. Скопируй в свой репозиторий 2 файла из этого стартера: .forgejo/workflows/deploy.yml (не трогать) и .env (из .env.example). Форкать/клонировать весь стартер не надо — он лишь источник этих файлов; деплой цепляется к твоей репе по самому факту наличия deploy.yml.
  2. Заполни .env (см. .env.example), минимум:
    DOMAIN=mysite.com
    
  3. Пропиши A-запись домена на IP сервера платформы.
  4. git push в main. Через ~3090 сек сайт открыт на https://mysite.com.

Дальше любой push = редеплой. Файл .forgejo/workflows/deploy.yml трогать не нужно.


Три типа проектов

Тип определяется по .env (DEPLOY=...) или авто-детектом.

1. static — фронт (90% проектов)

Vite / CRA / Next static export. bun build → статика раздаётся напрямую (без контейнера). Деплой за секунды, редеплой = только заливка новых файлов.

DOMAIN=mysite.com
# DEPLOY=static        # дефолт, если нет Dockerfile
# BUILD_DIR=dist       # Vite=dist (дефолт), CRA=build, Next export=out
  • Vite + React — ничего настраивать, дефолт.
  • CRABUILD_DIR=build.
  • Next.js static export — в next.config: output: 'export', images: { unoptimized: true }, trailingSlash: true; .env: BUILD_DIR=out. SSR / API routes / Server Actions не работают (только статика).

2. docker — фронт + бэкенд в контейнере

Положи Dockerfile в корень. Платформа собирает образ и поднимает контейнер, проксирует домен на него. Контейнер должен слушать порт из APP_PORT.

DOMAIN=mysite.com
DEPLOY=docker          # или просто наличие Dockerfile
APP_PORT=3000          # порт, который слушает контейнер (default 80)

3. docker-db — + персистентная база (SQLite)

Как docker, плюс платформа монтирует named-volume в /data и прокидывает DATABASE_URL=file:/data/app.db. Том переживает редеплои.

DOMAIN=mysite.com
DEPLOY=docker-db
APP_PORT=3000

В коде читаешь process.env.DATABASE_URL. Миграции (drizzle/prisma) запускай в своём entrypoint — платформа их не делает. Не указывай DATABASE_URL в RUNTIME_KEYS — она ставится автоматически.


Доп. домены — один сайт на нескольких доменах

По умолчанию сайт отдаётся на DOMAIN (+ www. авто, если у www есть A-запись на наш IP). Нужен тот же сайт ещё на других доменах — перечисли их через запятую:

DOMAIN=mysite.com
EXTRA_DOMAINS=mysite.io,app.mysite.com,promo.xyz

Платформа раздаёт сайт на всех доменах и на каждый выпускает свой SSL-серт автоматически. От тебя — прописать A-запись каждого домена на IP платформы и git push. www. для доп-доменов сам не добавляется — если нужен, впиши www.mysite.io явным пунктом. Нет A-записи → в логе ::warning::, серт выпустится, как только DNS укажет сюда.

One-liner curl get.<domain> | sh — особый случай, см. INSTALL_SH в .env.example (отдаёт один файл-скрипт с сабдомена get.).


Скорость билда

Платформа собирает фронт через bun (install в 1030× быстрее npm). Закоммить lock-файл:

bun install        # создаст bun.lock
git add bun.lock && git commit -m "chore: bun lock" && git push

package.json и твои npm run build скрипты bun понимает as-is. Не коммить node_modules / dist (они в .gitignore).

docker-сайты — кеш слоёв и лёгкие образы

Платформа собирает с BuildKit и --cache-from предыдущего образа: если слои не менялись, пересборка почти мгновенная. Чтобы это работало на полную:

  • Коммить lockfile и ставь зависимости ДО копирования кода — слой install кешируется, пока deps не менялись:
    COPY package.json package-lock.json ./
    RUN npm ci
    COPY . .
    RUN npm run build
    
  • Cache-mount'ы (BuildKit включён) — install/apt не качают заново между сборками, а Next держит инкрементальный кеш компиляции:
    # syntax=docker/dockerfile:1.7
    RUN --mount=type=cache,target=/root/.npm npm ci
    RUN --mount=type=cache,target=/app/.next/cache npm run build
    
  • Next.js → лёгкий образ: output: 'standalone' в next.config роняет образ с ~13 ГБ до ~150250 МБ → деплой заметно быстрее (меньше экспорт + запуск).

Секреты (DATABASE_URL, API-ключи)

.env в runtime контейнера не попадает (парсится только при деплое). Секреты:

  1. Forgejo → репа → Settings → Actions → Secrets → Add Secret. Имя секрета = имя переменной окружения, напр. DATABASE_URL, JWT_SECRET.
  2. В .env перечисли, какие из них прокинуть:
    RUNTIME_KEYS=DATABASE_URL,JWT_SECRET
    
  3. В коде: process.env.DATABASE_URL.

Платформа берёт только ключи из RUNTIME_KEYS — ничего лишнего не утечёт. (Только для docker/docker-db; у static-сайта runtime-секретов нет.)


Выключить сайт (kill-switch)

В .env:

ENABLED=false

Push → контейнер/файлы удаляются, домен отдаёт 404. Убери строку (или true) + push → поднимается обратно. Том и секреты сохраняются.

(Админ может выключить проект и без коммита в репу — переменной ENABLED=false в Settings → Actions → Variables.)


Переменные .env

Key Default Зачем
DOMAIN Обязательно (кроме ENABLED=false)
DEPLOY auto static / docker / docker-db
BUILD_DIR dist static: папка сборки
APP_PORT 80 docker: порт контейнера
WWW auto false — не подключать www
EXTRA_DOMAINS доп. домены через запятую (тот же сайт, свой SSL на каждый)
INSTALL_SH static: отдать файл-скрипт на get.<domain> (curl | sh)
ENABLED true false — выключить сайт
RUNTIME_KEYS docker: список секретов в контейнер

Не открывай сайт в браузере, пока workflow не зелёный

Пока деплой идёт, по домену ещё может не быть валидного серта. Браузер может закешировать "сломанный SSL". Дождись зелёного run в Actions, потом открывай (или открой в инкогнито).