Хочется свой GitHub, но без лишних облаков и подписок. Gitea — легковесный, быстрый, и устанавливается за один helm install. k3s даёт вам полноценный Kubernetes на одной VPS за пять минут. Вместе они закрывают 90% потребностей небольшой команды или домашней лаборатории. Разберём всё по шагам: от создания namespace до первого git push через SSH.
Что потребуется
- VPS или bare-metal с Ubuntu 22.04 / Debian 12 (минимум 2 ГБ RAM, 20 ГБ диска)
- k3s уже запущен:
kubectl get nodesвозвращаетReady helmv3 установлен- Домен или IP с возможностью открыть порты 80, 443, 22 (или нестандартный SSH)
kubectlнастроен (kubeconfig в~/.kube/config)
Если k3s ещё не стоит — одна команда решает вопрос:
curl -sfL https://get.k3s.io | sh -
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
k3s в стандартной поставке включает Traefik как Ingress Controller и local-path как StorageClass — нам оба пригодятся.
Namespace и StorageClass
Изолируем всё в отдельный namespace. Так проще чистить, обновлять и отлаживать.
kubectl create namespace gitea
kubectl config set-context --current --namespace=gitea
Проверим доступные StorageClass:
kubectl get storageclass
На ванильном k3s вы увидите local-path (default). Он хранит данные на хосте в /var/lib/rancher/k3s/storage/. Для продакшена на одном узле это нормально, пока вы делаете регулярные бэкапы. Если у вас несколько узлов — смотрите в сторону Longhorn или NFS-провизионера.
PostgreSQL в кластере
Gitea умеет работать с SQLite, MySQL и PostgreSQL. SQLite — это удобно для теста, но при нагрузке выше десятка пушей в минуту начнутся локи. Ставим PostgreSQL через официальный Helm-чарт Bitnami.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
Создадим файл значений postgres-values.yaml:
auth:
username: gitea
password: "ЗАМЕНИТЕ_НА_СИЛЬНЫЙ_ПАРОЛЬ"
database: gitea
primary:
persistence:
storageClass: local-path
size: 10Gi
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
metrics:
enabled: false
Устанавливаем:
helm install postgres bitnami/postgresql \
--namespace gitea \
--values postgres-values.yaml \
--wait
Флаг --wait держит команду до тех пор, пока под не перейдёт в Running. Через минуту-две должно появиться:
kubectl get pods -n gitea
# NAME READY STATUS RESTARTS AGE
# postgres-postgresql-0 1/1 Running 0 90s
Запомните Service-имя — оно нам понадобится: postgres-postgresql.gitea.svc.cluster.local.
Установка Gitea через Helm
Официальный чарт Gitea живёт на gitea-charts:
helm repo add gitea-charts https://dl.gitea.com/charts/
helm repo update
Создадим основной файл конфигурации gitea-values.yaml. Это главный рычаг — прочитайте его целиком перед применением:
# --- Общие настройки ---
replicaCount: 1
image:
tag: "1.22" # Укажите последний стабильный тег
# --- Ingress ---
ingress:
enabled: true
className: traefik
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
cert-manager.io/cluster-issuer: letsencrypt-prod # если cert-manager установлен
hosts:
- host: git.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: gitea-tls
hosts:
- git.example.com
# --- Service ---
service:
http:
type: ClusterIP
port: 3000
ssh:
type: NodePort
port: 22
nodePort: 30022 # внешний порт SSH
# --- База данных ---
postgresql:
enabled: false # используем внешний PostgreSQL
postgresql-ha:
enabled: false
gitea:
config:
database:
DB_TYPE: postgres
HOST: postgres-postgresql.gitea.svc.cluster.local:5432
NAME: gitea
USER: gitea
PASSWD: "ЗАМЕНИТЕ_НА_СИЛЬНЫЙ_ПАРОЛЬ" # тот же, что в postgres-values.yaml
server:
DOMAIN: git.example.com
ROOT_URL: https://git.example.com
SSH_DOMAIN: git.example.com
SSH_PORT: 30022
START_SSH_SERVER: "true"
repository:
DEFAULT_BRANCH: main
service:
DISABLE_REGISTRATION: "false" # отключите после регистрации администратора
REQUIRE_SIGNIN_VIEW: "false"
security:
INSTALL_LOCK: "false" # станет true после первого запуска мастера
# --- Хранилище ---
persistence:
enabled: true
storageClass: local-path
size: 20Gi
accessModes:
- ReadWriteOnce
# --- Ресурсы ---
resources:
requests:
memory: 128Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
Несколько пояснений по ключевым параметрам:
service.ssh.type: NodePort — Traefik не проксирует TCP-трафик из коробки (нужен дополнительный IngressRouteTCP). NodePort проще: порт 30022 напрямую пробрасывается с хоста в под. Пользователи клонируют через ssh://[email protected]:30022/user/repo.git.
START_SSH_SERVER: "true" — Gitea поднимает встроенный SSH-сервер внутри пода. Это избавляет от необходимости монтировать хостовый /etc/ssh.
INSTALL_LOCK: "false" — при первом запуске Gitea покажет веб-мастер установки. После его прохождения значение автоматически станет true в app.ini. Если хотите пропустить мастер и всё настроить через values.yaml — сразу поставьте "true" и заполните все поля конфига.
Устанавливаем:
helm install gitea gitea-charts/gitea \
--namespace gitea \
--values gitea-values.yaml \
--wait --timeout 5m
Смотрим на поды:
kubectl get pods -n gitea
# NAME READY STATUS RESTARTS AGE
# postgres-postgresql-0 1/1 Running 0 5m
# gitea-7d9f8b5c4-xk2pv 1/1 Running 0 2m
Первый вход и настройка администратора
Откройте браузер на https://git.example.com. Если видите мастер установки — большинство полей уже заполнено из values.yaml. Обратите внимание:
- Database type: PostgreSQL
- Host:
postgres-postgresql.gitea.svc.cluster.local:5432— должен подтянуться автоматически - Admin account: создайте здесь же, не пропустите этот шаг
После завершения мастера Gitea перезапишет app.ini внутри PVC и установит INSTALL_LOCK = true.
Если предпочитаете CLI — создайте администратора через kubectl exec:
kubectl exec -n gitea deploy/gitea -- \
/app/gitea/gitea admin user create \
--username admin \
--password "СИЛЬНЫЙ_ПАРОЛЬ" \
--email [email protected] \
--admin
Проверка SSH-доступа
Добавьте свой публичный ключ в Gitea (Settings → SSH / GPG Keys), затем:
ssh -T [email protected] -p 30022
# Hi admin! You've successfully authenticated, but Gitea does not provide shell access.
Клонирование:
git clone ssh://[email protected]:30022/admin/test-repo.git
Чтобы не указывать порт каждый раз, добавьте в ~/.ssh/config:
Host git.example.com
User git
Port 30022
IdentityFile ~/.ssh/id_ed25519
После этого работает стандартный синтаксис: git clone [email protected]:admin/test-repo.git.
TLS без cert-manager
Если cert-manager не установлен, самый быстрый путь — установить его прямо сейчас:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--set installCRDs=true
Создайте ClusterIssuer для Let’s Encrypt:
# letsencrypt-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: traefik
kubectl apply -f letsencrypt-issuer.yaml
После этого аннотация cert-manager.io/cluster-issuer: letsencrypt-prod в Ingress начнёт работать: сертификат будет получен и обновляться автоматически.
Обновление Gitea
Helm-обновление — одна команда:
helm upgrade gitea gitea-charts/gitea \
--namespace gitea \
--values gitea-values.yaml \
--wait
Перед мажорными обновлениями (например, 1.21 → 1.22) проверьте CHANGELOG на ломающие изменения. Gitea при старте автоматически выполняет миграции базы данных, но резервная копия перед обновлением — обязательна.
Бэкап
Gitea хранит данные в двух местах:
- PostgreSQL — все метаданные: пользователи, issues, pull requests, настройки
- PVC (
/dataвнутри пода) — репозитории (bare git), аватары, вложения
Бэкап базы данных
kubectl exec -n gitea postgres-postgresql-0 -- \
pg_dump -U gitea gitea | gzip > gitea-db-$(date +%Y%m%d).sql.gz
Бэкап репозиториев
POD=$(kubectl get pod -n gitea -l app.kubernetes.io/name=gitea -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n gitea "$POD" -- \
tar czf - /data/gitea/repositories | \
cat > gitea-repos-$(date +%Y%m%d).tar.gz
Автоматизируйте через CronJob в кластере или простой cron на хосте с доступом к kubectl. Храните бэкапы вне ноды — на S3, Backblaze B2 или смонтированном NFS.
Встроенный backup-инструмент Gitea
Gitea умеет делать самосогласованный дамп:
kubectl exec -n gitea "$POD" -- \
/app/gitea/gitea dump -c /data/gitea/conf/app.ini \
--file /tmp/gitea-dump.zip --type zip
kubectl cp gitea/"$POD":/tmp/gitea-dump.zip ./gitea-dump-$(date +%Y%m%d).zip
Архив содержит и базу данных, и репозитории, и конфиг — удобно для полного восстановления на чистой машине.
Отладка типичных проблем
Pod в CrashLoopBackOff
kubectl logs -n gitea deploy/gitea --previous
Чаще всего причина: неверный пароль к PostgreSQL или опечатка в HOST. Сверьте значение PASSWD в gitea-values.yaml и postgres-values.yaml.
Gitea недоступна по домену
kubectl describe ingress -n gitea
kubectl get events -n gitea --sort-by='.lastTimestamp'
Убедитесь, что DNS домена указывает на IP вашей ноды, и порты 80/443 открыты в фаерволле:
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 30022/tcp
SSH работает, но медленно
Внутри пода встроенный SSH-сервер Gitea может медленно резолвить клиентский IP. Добавьте в конфиг:
gitea:
config:
server:
SSH_SERVER_HOST_KEYS: ssh/gitea.rsa,ssh/gitea.ed25519
log:
LEVEL: Warn
PVC остался после helm uninstall
Helm не удаляет PersistentVolumeClaim автоматически — это защита от случайной потери данных. Если хотите полную очистку:
helm uninstall gitea -n gitea
kubectl delete pvc -n gitea --all
kubectl delete pvc -n gitea data-postgres-postgresql-0
Интеграция с CI/CD
Gitea поддерживает Gitea Actions — совместимый с GitHub Actions движок. Включите в values.yaml:
gitea:
config:
actions:
ENABLED: "true"
DEFAULT_ACTIONS_URL: https://gitea.com
Запустите Act Runner рядом с Gitea:
helm install act-runner gitea-charts/act-runner \
--namespace gitea \
--set gitea.instanceURL=https://git.example.com \
--set gitea.token="ВАШ_REGISTRATION_TOKEN"
Токен берётся из Gitea: Site Administration → Runners → Create new runner.
После этого .gitea/workflows/ci.yaml в репозитории работает по той же логике, что GitHub Actions, но полностью внутри вашей инфраструктуры.
Производительность и ресурсы
На одном узле с 4 ГБ RAM комфортно живут:
- PostgreSQL: 256–512 МБ
- Gitea: 128–256 МБ в покое, до 512 МБ при активных CI-прогонах
Если памяти мало — рассмотрите SQLite: для 1-5 разработчиков и нескольких десятков репозиториев разницы не заметите, зато сэкономите 200+ МБ.
Включите кэш в памяти для ускорения веб-интерфейса:
gitea:
config:
cache:
ADAPTER: memory
INTERVAL: 60
session:
PROVIDER: memory
При нескольких репликах (replicaCount: 2) переключитесь на Redis:
gitea:
config:
cache:
ADAPTER: redis
HOST: redis://gitea-redis-master:6379/0
session:
PROVIDER: redis
PROVIDER_CONFIG: redis://gitea-redis-master:6379/1
И установите Redis через Bitnami: helm install gitea-redis bitnami/redis --namespace gitea --set auth.enabled=false.
Итого: у вас работающий Gitea на k3s с PostgreSQL, TLS, SSH-доступом и базовым планом бэкапов. Следующий шаг — настроить Gitea Actions и подключить внешний S3 для хранения артефактов. Но это уже отдельная история.