HostiServer
2026-10-09 11:19
Gitea / Forgejo у Docker: повний self-hosted стек
📚 Серія «Docker на VPS», частина 5 з 5:
- Docker на VPS: встановлення і перший контейнер
- Docker Compose: кілька сервісів разом
- Docker у production
- Docker + CI/CD: автодеплой з GitHub Actions / GitLab CI
- Gitea / Forgejo у Docker: повний self-hosted стек ← ви тут
Gitea / Forgejo у Docker: повний self-hosted стек
Усі чотири попередні статті серії спирались на те, що код живе десь зовні — на GitHub, GitLab чи будь-якій іншій зовнішній платформі. Self-hosted Git знімає цю залежність повністю: повний контроль над кодом, приватність без винятків для сторонніх сервісів, відсутність ліцензійних обмежень для внутрішніх чи закритих проєктів, а заразом — власний Docker registry і власний CI, які не рахують хвилини збірки і не обмежують приватні репозиторії.
У результаті цієї статті на одному VPS підніметься повноцінна платформа з чотирьох сервісів: Forgejo (сам Git-сервер і веб-інтерфейс), PostgreSQL, act runner (виконавець CI-пайплайнів) і Nginx — усе в одному docker-compose.yml.
Ця стаття замикає одразу дві серії. З Docker-серії вона спирається на все пройдене раніше: Compose з кількома сервісами (стаття 2), Nginx і SSL (стаття 3), принцип автодеплою (стаття 4). З серії «CI/CD з нуля» — на концепцію self-hosted runner'а і синтаксис Actions-подібних pipeline.
2. Gitea vs Forgejo
Forgejo — форк Gitea, що відокремився у 2022 році через розбіжності щодо відкритості управління проєктом. Він розвивається як community-driven ініціатива під егідою Codeberg, тоді як Gitea лишається під окремим керівництвом.
Функціонально обидва проєкти на сьогодні практично ідентичні, а API повністю сумісний — міграція бази даних і репозиторіїв в обидва боки проходить без втрат. Різниця здебільшого в назвах Docker-образів та деталях governance-моделі. Усе, що описано далі в цій статті, стосується обох варіантів однаково; як основний приклад тут узято Forgejo.
3. Архітектура стека
Стек складається з чотирьох сервісів:
| Сервіс | Роль |
|---|---|
forgejo |
Веб-інтерфейс і сам Git-сервер |
db |
PostgreSQL — сховище даних Forgejo |
runner |
act runner — виконавець CI-пайплайнів |
nginx |
Reverse proxy і термінація SSL |
Мережа forgejo-net ізолює forgejo, db і runner в одному внутрішньому просторі; назовні виходить тільки nginx через порти 80 і 443. За збереження даних відповідають два volumes: forgejo-data тримає самі репозиторії, конфігурацію і Git LFS-об'єкти, pgdata — базу даних. SSH для git-операцій виходить назовні окремим портом (типово 2222 → 22 усередині контейнера), або прокидається на хост через окремий механізм — про це нижче.
4. docker-compose.yml
Повний стек в одному файлі:
services:
forgejo:
image: codeberg.org/forgejo/forgejo:9
environment:
FORGEJO__server__ROOT_URL: https://git.example.com
FORGEJO__server__SSH_DOMAIN: git.example.com
FORGEJO__server__SSH_PORT: "2222"
FORGEJO__database__DB_TYPE: postgres
FORGEJO__database__HOST: db:5432
FORGEJO__database__NAME: forgejo
FORGEJO__database__USER: forgejo
FORGEJO__database__PASSWD: ${DB_PASSWORD}
volumes:
- forgejo-data:/data
ports:
- "2222:22"
depends_on:
db:
condition: service_healthy
networks: [forgejo-net]
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: forgejo
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U forgejo -d forgejo"]
interval: 5s
timeout: 5s
retries: 10
networks: [forgejo-net]
restart: unless-stopped
runner:
image: code.forgejo.org/forgejo/runner:3
environment:
FORGEJO_INSTANCE_URL: https://git.example.com
FORGEJO_RUNNER_REGISTRATION_TOKEN: ${RUNNER_TOKEN}
volumes:
- runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
networks: [forgejo-net]
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on: [forgejo]
networks: [forgejo-net]
restart: unless-stopped
volumes:
forgejo-data:
pgdata:
runner-data:
networks:
forgejo-net:
Залежність forgejo від db через condition: service_healthy працює за тим самим принципом, що й зв'язка app → db у статті 2 цієї серії: без healthcheck Forgejo може спробувати підключитись до бази раніше, ніж PostgreSQL дійсно готовий приймати з'єднання.
Конфігурація Forgejo через змінні середовища з префіксом FORGEJO__ — рекомендований спосіб, зручніший за bind-mount готового app.ini: усі налаштування лишаються видимими прямо в docker-compose.yml, без окремого файлу, який легко забути синхронізувати при переносі на інший сервер. Ключові змінні — SERVER__ROOT_URL (публічна адреса), SERVER__SSH_DOMAIN (домен для git-клонів по SSH), блок database__* з типом і доступом до PostgreSQL, і за потреби — секція mailer для сповіщень поштою. Сервіс runner реєструється до Forgejo за токеном (детальніше в розділі 8) і зберігає свій стан у власному volume.
5. SSH і клонування
За замовчуванням Forgejo всередині контейнера слухає SSH на стандартному порту 22. Назовні цей порт маппиться на 2222, щоб не конфліктувати із системним SSH-сервером самого хоста, який уже займає 22-й порт для адміністрування сервера.
У конфігурації Forgejo це відображається парою SSH_PORT=2222 і SSH_DOMAIN=git.example.com — саме ці значення й визначають, як виглядатиме команда клонування для будь-якого репозиторію:
git clone git@git.example.com:2222/user/repo.git
Є й альтернативний шлях — SSH passthrough через AuthorizedKeysCommand на самому хості, коли системний SSH хоста перенаправляє git-запити всередину контейнера. Це дозволяє лишити стандартний порт 22 для git-клонів без додаткового :2222 в адресі, але налаштовується помітно складніше за простий проброс порту.
6. Перший запуск і налаштування
При першому старті Forgejo за замовчуванням показує Installation wizard прямо в браузері — кілька кроків налаштування бази даних, адміністратора і загальних параметрів. Цей крок можна повністю обійти, заздалегідь передавши всі потрібні значення через змінні середовища і додавши INSTALL_LOCK=true — тоді контейнер стартує одразу в готовому до роботи стані, без інтерактивного майстра.
Перший акаунт, зареєстрований у системі, за замовчуванням автоматично стає адміністратором; альтернативно адміна можна задати явно через FORGEJO__security__ADMIN_USER. Решта — SSH-ключі користувачів, організації, видимість репозиторіїв — налаштовується вже через звичайний веб-інтерфейс так само, як у хмарних аналогах.
7. Nginx + SSL
Конфігурація Nginx тут майже один в один повторює статтю 3 серії: proxy_pass http://forgejo:3000; і той самий набір заголовків — Host, X-Real-IP, X-Forwarded-Proto. SSL отримується тим самим шляхом через Let's Encrypt і Certbot у режимі webroot, з тим самим deploy hook'ом на reload після поновлення сертифіката.
Важливий нюанс саме для Git-сервера: Nginx проксіює лише веб-трафік (HTTP/HTTPS). SSH-трафік для git-операцій він не зачіпає взагалі — той іде напряму на порт 2222, в обхід Nginx, прямо до контейнера forgejo.
8. Act Runner
Act runner — офіційний виконавець CI-пайплайнів від команди Gitea/Forgejo. Він виконує файли з .forgejo/workflows/*.yml, формат яких сумісний із синтаксисом GitHub Actions — переважна більшість простих workflow переносяться майже без правок.
Реєстрація runner'а до інстансу Forgejo відбувається за токеном: Settings → Actions → Runners → New runner у веб-інтерфейсі видає одноразовий токен, який передається runner-контейнеру через змінну середовища при першому старті.
Є два режими виконання завдань. Docker-in-Docker ізолює runner повністю — завдання виконуються у вкладеному Docker-демоні, недоступному решті хоста, але налаштовується складніше. Простіший варіант — прокинути хостовий /var/run/docker.sock прямо в контейнер runner'а: налаштування мінімальне, проте runner у такому разі отримує фактичний доступ до Docker хоста, за тією ж логікою ризику, що й Traefik зі статті 3 цієї серії.
Labels, призначені runner'у при реєстрації, визначають, які значення runs-on: у workflow-файлах він здатен обслуговувати — той самий принцип, що й у self-hosted runner'ах GitHub Actions чи GitLab CI.
9. Перший pipeline
Файл .forgejo/workflows/deploy.yml пишеться синтаксисом, ідентичним GitHub Actions. Стандартний потік — той самий абстрактний pipeline зі статті 4 серії: тригер на push, checkout коду, збірка Docker-образу, публікація — тільки тепер усе це виконується на власному runner'і, а не на чужій інфраструктурі хмарної CI-платформи.
ℹ️ Детальний синтаксис: покрокове налаштування workflow-файлів для Gitea/Forgejo Actions розібрано в окремому матеріалі серії «CI/CD з нуля», присвяченому саме цій платформі.
10. Forgejo Container Registry
У Forgejo вбудований OCI-сумісний container registry, доступний прямо за адресою вашого інстансу — git.example.com — як альтернатива Docker Hub чи ghcr.io зі статті 4. Образи зберігаються поруч із самими репозиторіями, а авторизація використовує той самий токен, що й для git-операцій — окремого облікового запису для registry заводити не потрібно.
Це замикає стек у єдине ціле: Forgejo одночасно виконує роль Git-сервера, CI-платформи і registry — усі три компоненти, які в попередній статті серії були рознесені по різних зовнішніх сервісах, тепер живуть на одному VPS під вашим повним контролем.
11. Бекап і оновлення
Надійний бекап складається з трьох кроків: зупинити Forgejo, заархівувати volume forgejo-data (репозиторії, конфігурація, LFS-об'єкти), окремо зняти дамп бази через pg_dump. Обидва файли варто зберігати разом, позначивши датою — відновлення з неузгоджених у часі архіву репозиторіїв і дампу бази може призвести до неконсистентного стану.
Оновлення версії — звичний для серії патерн: docker compose pull, тоді docker compose up -d. Forgejo підтримує rolling-міграції бази даних автоматично при старті нової версії, тож у більшості випадків додаткових ручних кроків не потрібно. Виняток — мажорні оновлення: перед ними варто звірятись із CHANGELOG конкретної версії, оскільки деякі мажорні переходи вимагають окремого ручного кроку до запуску нового образу.
12. Висновок і підсумок серії
Шлях усієї серії — від docker run hello-world у першій статті до повного self-hosted стека, що поєднує Git, CI, registry і SSL в одному docker-compose.yml на одному сервері. Кожна наступна стаття знімала одне конкретне обмеження попередньої: ручний запуск контейнерів → декларативний опис стека → production-готовність → автоматичний деплой → повна незалежність від зовнішніх платформ.
Весь цей стек відтворюється на будь-якому новому VPS приблизно за тридцять хвилин: git clone конфігураційного репозиторію з docker-compose.yml і .env, тоді docker compose up -d — і платформа піднята.
Логічне продовження для тих, кому одного сервера вже недостатньо, — Docker Swarm: той самий підхід до опису стека, але розподілений по кількох нодах, із автоматичним failover при падінні однієї з них.
📚 Навігація серії:
Ви читаєте частину 5 з 5 «Gitea / Forgejo у Docker: повний self-hosted стек» — фінальну статтю серії.
Попередня: Частина 4. Docker + CI/CD: автоматичний деплой ←
🚀 VPS під власний Git + CI + registry
Чотири сервіси одночасно — Git-сервер, база даних, CI-runner і reverse proxy — це вже помітне навантаження на CPU і диск, особливо в момент виконання pipeline. Hostiserver дає ресурси, розраховані саме на такий self-hosted стек.
🖥️ Виділені Сервери
- Від $90/міс, повний контроль над залізом для активних CI-навантажень
- Без сусідів по ресурсах: збірка образів на runner'і не залежить від чужих контейнерів
- 24/7 підтримка: допоможемо з мережами, SSH-портом і реєстрацією runner'а
💻 Cloud (VPS) Хостинг
- Від $19.95/міс, KVM-ізоляція, виділені vCPU і RAM
- NVMe-диски: швидкі git-операції, LFS і збірка Docker-образів прямо на runner'і
- Виділена IP-адреса: стабільний домен для git-клонів по SSH і для SSL
- Легко масштабувати: більше ресурсів під runner, коли пайплайнів стає більше
💬 Не впевнені, який варіант вам необхідний?
💬 Напишіть нам і ми зі всім допоможемо!
Часті запитання
- Gitea чи Forgejo — що обрати для нового проєкту?
Функціонально різниця мінімальна, тож вибір частіше залежить від governance-моделі: Forgejo розвивається як community-driven проєкт під Codeberg, Gitea — під окремим керівництвом. Оскільки API повністю сумісний, цей вибір не є незворотним — перейти з одного на інший можна пізніше без втрати даних.
- Чи можна мігрувати існуючий інстанс Gitea на Forgejo без втрат?
Так, завдяки повній сумісності API і структури бази даних міграція в обидва боки проходить без втрати репозиторіїв, issues чи налаштувань. На практиці це зводиться до заміни Docker-образу і підняття стека наново з тим самим volume бази даних.
- Чому SSH виходить назовні на порту 2222, а не на стандартному 22?
Порт 22 на самому VPS вже зайнятий системним SSH-сервером, через який адмініструється сам хост. 2222 → 22 всередині контейнера — найпростіший спосіб уникнути конфлікту. Якщо потрібен саме стандартний порт 22 для git-клонів, є альтернатива через
AuthorizedKeysCommandна хості, але вона складніша в налаштуванні.
- Docker-in-Docker чи host docker socket для act runner — що обрати?
Host docker socket простіший у налаштуванні — достатньо прокинути
/var/run/docker.sock— але дає runner'у фактичний доступ до Docker хоста, а отже й непрямий root-доступ. Docker-in-Docker ізольований повністю, але складніший у конфігурації. Для одного довіреного проєкту на власному сервері host-сокет зазвичай прийнятний компроміс; для CI, що виконує код із зовнішніх pull request-ів, варто обирати ізольований варіант.
- Чи потрібен окремий Docker registry, якщо вже є Docker Hub?
Необов'язково, але вбудований у Forgejo registry усуває ще одну зовнішню залежність і ще один обліковий запис для авторизації — токен той самий, що й для git-операцій. Якщо мета саме повна незалежність від зовнішніх сервісів, використання вбудованого registry логічно завершує цю ідею.
- Як правильно бекапити Forgejo, щоб не отримати неконсистентний стан?
Ключовий момент — узгодженість у часі: архів volume
forgejo-dataі дамп бази даних черезpg_dumpзнімаються в одній точці в часі, бажано на коротко зупиненому сервісі, і зберігаються разом з однаковою позначкою дати. Відновлення зі старого дампу бази і нового архіву репозиторіїв (чи навпаки) може призвести до розбіжностей між метаданими в базі і реальним станом Git-репозиторіїв.
- Чи обов'язково проходити Installation wizard через браузер?
Ні. Якщо всі потрібні параметри (база даних, адміністратор, домен) задані заздалегідь через змінні середовища з префіксом
FORGEJO__, а також встановленоINSTALL_LOCK=true, контейнер стартує одразу в готовому стані — зручно для відтворюваного розгортання з конфігурації, без ручного проходження майстра щоразу.