Gitea в Docker: свой Git-сервер на VPS с SSH, бэкапами и обновлением
Запуск Gitea в Docker Compose, первоначальная настройка, SSH на порту 2222, закрытая регистрация, добавление коллег, скрипт резервных копий с ключами SSH, восстановление и обновление. Debian 12 и Ubuntu 24.04.

Свой Git-сервер нужен, когда код нельзя отдавать стороннему сервису или проекту нужны собственные правила доступа. Gitea — лёгкая открытая программа, которая превращает VPS в такой сервер: репозитории, веб-интерфейс, учётные записи и права, задачи и pull request’ы. На сервере она занимает 100–160 МБ памяти, а для небольшой команды хватает встроенной базы SQLite.
Как устроен Git-сервер на Gitea
К Gitea обращаются двумя способами. По HTTP на порт 3000 — каждая операция требует логин и пароль, и пока адрес начинается с http://, они уходят по сети открытым текстом (заголовок Authorization: Basic виден в tcpdump). По SSH — вы один раз добавляете публичный ключ в профиль, и дальше Git подтверждает личность ключом. Для этого внутри контейнера работает собственный SSH-сервер Gitea на порту 22. Порт 22 самого VPS занят вашим входом, поэтому контейнерный порт мы опубликуем как 2222.
Всё, что меняется в работе, лежит в одной папке /opt/gitea/data: app.ini, база gitea.db, репозитории в git/repositories и ключи SSH-сервера в ssh. Контейнер ничего важного не хранит — его можно удалить и пересоздать, обновление сводится к смене образа, а бэкап — к сохранению этой папки.
Версия 28, а не 1.27. Последним выпуском старой нумерации был 1.27.3, следующий вышел как 28.0.0. В статье используется 28.2.0.
Что нужно
- Сервер на tanaka.host с Debian 12 или Ubuntu 24.04. Образ около 260 МБ, память 100–160 МБ, место под репозитории — по вашим проектам.
- Docker и Compose — см. установку Docker.
- Свободные порты 3000 и 2222.
- Git на вашем компьютере.
1. Запустите Gitea
mkdir -p /opt/gitea
cd /opt/gitea
nano docker-compose.yml
services:
gitea:
image: docker.gitea.com/gitea:28.2.0
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
volumes:
- ./data:/data
ports:
- "3000:3000"
- "2222:22"
Версия образа записана явно — обновление произойдёт, когда вы сами этого захотите. USER_UID/USER_GID — пользователь, от которого Gitea работает внутри контейнера. ./data:/data — папка с базой, репозиториями и настройками рядом с файлом. restart: always поднимает контейнер после перезагрузки сервера.
docker compose up -d
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
В списке должна появиться строка gitea со статусом Up.
Порты открыты без UFW. Docker публикует порты собственными правилами раньше правил UFW, поэтому 3000 и 2222 доступны из интернета сразу. Как спрятать веб-порт — в конце статьи.
2. Пройдите первоначальную настройку
Не оставляйте страницу настройки открытой. Пока вы не нажали кнопку установки, её может заполнить любой, кто знает адрес и порт, — вплоть до создания администратора. Делайте это сразу после запуска.
Откройте http://IP-адрес-сервера:3000. Большинство полей уже заполнено, править нужно несколько:
- Настройки базы данных — оставьте SQLite3 и путь
/data/gitea/gitea.db. - Порт SSH сервера — замените 22 на 2222. Так Gitea будет показывать в адресах клонирования тот порт, что опубликован наружу.
- Gitea Website URL — проверьте, что там адрес, по которому вы открыли страницу.
- Отметьте Only administrators can create user accounts (no self-registration).
- Require sign-in to view pages — прячет список репозиториев от гостей; пригодится, если сервер только для своих.
Внизу раскройте Расширенные настройки → Настройки учётной записи администратора: логин, почта, пароль. Письма Gitea без настроенной почты не шлёт, ящик может быть любым. Пароль выбирайте длинный — установщик принимает даже трёхсимвольный и от слабого не защитит. Нажмите Установить Gitea.
Настройки запишутся в /opt/gitea/data/gitea/conf/app.ini.
3. Укажите адрес сервера в app.ini
На странице настройки нет поля с доменом, поэтому Gitea записала DOMAIN = localhost и SSH_DOMAIN = localhost. В результате SSH-адрес репозитория выглядит как ssh://git@localhost:2222/…, и подключиться по нему с другого компьютера нельзя.
cd /opt/gitea
nano data/gitea/conf/app.ini
В блоке [server] замените localhost на IP сервера в строках DOMAIN и SSH_DOMAIN. SSH_PORT = 2222 и SSH_LISTEN_PORT = 22 не трогайте: первая — порт в адресах, вторая — порт внутри контейнера. Проверьте и перезапустите:
grep -n -E "DOMAIN|ROOT_URL|SSH_PORT|SSH_LISTEN" data/gitea/conf/app.ini
docker restart gitea
Когда появится домен, в эти же строки и в ROOT_URL впишете его.
4. Создайте репозиторий
Значок + в правом верхнем углу → Новый репозиторий. Название my-project, отметьте Сделать репозиторий приватным (по умолчанию галочки нет) и Инициализировать репозиторий — Gitea сразу создаст первый коммит с README. На странице репозитория кнопка Код покажет адреса для клонирования по HTTP и SSH.
5. Клонируйте по HTTP
Один раз на своём компьютере представьтесь Git:
git config --global user.name "Ваше Имя"
git config --global user.email "you@example.com"
Репозиторий приватный, Git спросит логин и пароль администратора:
# Клонируем репозиторий по HTTP
git clone http://IP-адрес-сервера:3000/gitadmin/my-project.git
cd my-project
# Добавляем файл и фиксируем изменение
echo "print(1)" > hello.py
git add hello.py
git commit -m "Add hello.py"
# Отправляем коммит на сервер
git push
Пароль по HTTP не защищён. Адрес
http://…:3000не шифрует ни пароль, ни код. Для реальных проектов используйте SSH из следующего шага или поставьте перед Gitea домен с HTTPS.
6. Подключите SSH на порту 2222
Если ключа ещё нет, создайте — подробно в статье про вход по SSH-ключу:
ssh-keygen -t ed25519 -C "me@example.com"
cat ~/.ssh/id_ed25519.pub
В Gitea: меню профиля → Настройки → Ключи SSH / GPG → Добавить ключ. В поле Содержимое вставьте всю строку от ssh-ed25519 до почты. Вставляется только .pub, закрытый ключ никуда не копируйте.
Вернитесь к репозиторию, в окне Код откройте вкладку SSH — адрес начинается с ssh://git@ и содержит порт 2222:
# Клонируем по SSH, порт 2222 входит в адрес
git clone ssh://git@IP-адрес-сервера:2222/gitadmin/my-project.git my-project-ssh
cd my-project-ssh
# Меняем файл и отправляем изменение
echo "SSH works" >> README.md
git commit -am "Update README"
git push
При первом подключении SSH спросит, доверять ли ключу сервера, — ответьте yes. Пароль не спрашивается. Быстро проверить ключ без репозитория: ssh -p 2222 git@IP-адрес-сервера ответит Hi there, gitadmin! You've successfully authenticated… и сообщит, что shell не даёт.
7. Добавьте коллегу
Откройте http://IP-адрес-сервера:3000/user/sign_up без входа — должно быть «возможность регистрации отключена». Если галочку пропустили, в app.ini в блоке [service] задайте DISABLE_REGISTRATION = true и перезапустите контейнер. Там же включается REQUIRE_SIGNIN_VIEW = true.
Учётные записи создаёт администратор: меню профиля → Панель управления → Идентификация и доступ → Пользователи → Создать новый аккаунт. Оставьте включённым Требовать смену пароля пользователем. Коллеге нужно один раз войти через браузер и сменить пароль — до этого Git по HTTP отвечает:
remote: Update your password
fatal: Authentication failed for 'http://IP-адрес-сервера:3000/gitadmin/my-project.git/'
Приватный репозиторий новый пользователь не видит. В репозитории: меню с тремя точками → Настройки → Соавторы → добавьте его, доступ по умолчанию — Запись.
Резервные копии
Официальная команда gitea dump собирает один архив с базой, репозиториями и настройками. Ключей SSH-сервера в нём нет: восстановленный на чистом контейнере сервер получит другие ключи, и у всех, кто уже подключался, SSH начнёт ругаться на их смену (см. Remote host identification has changed). Поэтому скрипт сохраняет папку ssh отдельно.
nano /opt/gitea/backup.sh
#!/bin/sh
# Резервная копия Gitea: официальный gitea dump и ключи SSH-сервера
set -e
cd /opt/gitea
STAMP=$(date +%F-%H%M)
mkdir -p backups
# Архив с базой, репозиториями и настройками, из папки /tmp внутри контейнера
docker exec -u git -w /tmp gitea gitea dump -q -c /data/gitea/conf/app.ini -f /tmp/gitea-dump.zip
docker cp -q gitea:/tmp/gitea-dump.zip backups/gitea-dump-$STAMP.zip
docker exec gitea rm /tmp/gitea-dump.zip
# Ключи SSH-сервера в архив не входят, сохраняем их отдельно
tar czf backups/gitea-ssh-$STAMP.tar.gz -C data ssh
# Храним 7 последних копий
ls -1t backups/gitea-dump-*.zip | tail -n +8 | xargs -r rm
ls -1t backups/gitea-ssh-*.tar.gz | tail -n +8 | xargs -r rm
-u git запускает команду от пользователя Gitea, -w /tmp задаёт рабочую папку, чтобы не получить ошибок прав. Документация советует останавливать сервис на время копирования; для небольшой команды хватит ночного запуска на работающем контейнере.
chmod +x /opt/gitea/backup.sh
/opt/gitea/backup.sh
ls -lh /opt/gitea/backups
В crontab -e:
0 3 * * * /opt/gitea/backup.sh
Папку backups периодически забирайте с сервера — см. резервные копии VPS.
Восстановление из копии
На новом сервере установите Docker и создайте docker-compose.yml из шага 1. Ключи SSH распакуйте до первого запуска — тогда отпечаток сервера совпадёт со старым:
mkdir -p /opt/gitea/data
tar xzf gitea-ssh-ДАТА.tar.gz -C /opt/gitea/data
cd /opt/gitea
docker compose up -d
Скопируйте архив в контейнер и распакуйте на место (cp -a, а не mv, потому что папка conf уже существует):
docker cp gitea-dump-ДАТА.zip gitea:/tmp/gitea-dump.zip
docker exec gitea sh -c "cd /tmp && unzip -q gitea-dump.zip -d restore && cp -a restore/data/. /data/gitea/ && cp -a restore/repos/. /data/git/repositories/ && chown -R git:git /data"
Пересоздайте хуки Git и перезапустите:
docker exec -u git gitea gitea -c /data/gitea/conf/app.ini admin regenerate hooks
docker restart gitea
Обновление
При смене версии Gitea меняет структуру базы, и вернуться назад без копии нельзя. Поэтому сначала бэкап, потом правка версии в image:
cd /opt/gitea
./backup.sh
nano docker-compose.yml
docker compose pull
docker compose up -d
Перед обновлением загляните в список изменений на странице релизов — там отмечены несовместимые правки.
Домен и HTTPS
Когда понадобится нормальный адрес, поставьте перед Gitea nginx с сертификатом, а в app.ini в блоке [server] впишите домен в ROOT_URL, DOMAIN и SSH_DOMAIN. Чтобы веб-порт был виден только через nginx, в Compose замените "3000:3000" на:
- "127.0.0.1:3000:3000"
После docker compose up -d порт 3000 снаружи перестанет отвечать. Порт 2222 остаётся открытым — он нужен для Git по SSH.
Полезные команды
docker compose ps # состояние контейнера, выполнять в /opt/gitea
docker logs gitea # журнал Gitea
docker restart gitea # перезапустить Gitea
docker compose down # удалить контейнер, данные в ./data останутся
# Список пользователей
docker exec -u git gitea gitea admin user list
# Новый пароль пользователю, например при потере пароля администратора
docker exec -u git gitea gitea admin user change-password --username gitadmin --password НОВЫЙ_ПАРОЛЬ --must-change-password=false
# Создать администратора из командной строки
docker exec -u git gitea gitea admin user create --admin --username имя --password ПАРОЛЬ --email me@example.com --must-change-password=false
Пароль из командной строки остаётся в истории оболочки — после такой смены задайте постоянный в настройках профиля.
Частые ошибки
| Сообщение | Причина |
|---|---|
ssh://git@localhost:2222/… в адресе |
не исправлены DOMAIN/SSH_DOMAIN в app.ini (шаг 3) |
Permission denied (publickey) |
в профиле нет вашего ключа или добавлен не .pub; при нескольких ключах укажите нужный через ssh -i |
port 2222: Connection refused |
контейнер остановлен — docker compose ps, docker start gitea |
remote: Failed to authenticate user |
неверный логин/пароль; сброс — gitea admin user change-password |
remote: Update your password |
пользователь ещё не входил в браузер и не сменил временный пароль |
remote: Repository not found |
опечатка в адресе или коллега не добавлен в соавторы приватного репозитория |
Author identity unknown |
не выполнены git config --global user.name и user.email |
Официальная документация: https://docs.gitea.com.