Эта статья написана по следам реальной установки GitLab CE на домашний сервер. Задача казалась стандартной — пока не выяснилось, что провайдер блокирует входящий 443 порт, диск работает за пределами паспортного ресурса по циклам паркинга, а в GitLab 18.x убрали несколько параметров конфига которые есть в большинстве гайдов. Здесь собрано всё что нужно знать чтобы не наступать на те же грабли.
Важно понимать: часть шагов этого гайда продиктована конкретными условиями — ограниченные ресурсы сервера, несколько сайтов на одной машине, заблокированный 443 порт провайдером. Там где это важно — я явно это указываю, чтобы вы могли пропустить лишнее или воспользоваться более простым встроенным решением GitLab.
Что мы будем делать
Берём домашний сервер на Ubuntu Server 22.04 и поднимаем на нём полноценный GitLab с правильной архитектурой:
- Проверка здоровья HDD и защита от износа
- SWAP для стабильной работы при нехватке RAM
- GitLab CE с отключённым встроенным Nginx
- Container Registry для хранения Docker-образов
- SSL-терминация на внешнем сервере с универсальным проксированием
- GitLab Exporter для подключения к внешнему Prometheus
Содержание
- Подготовка сервера
- Установка GitLab CE
- Настройка gitlab.rb
- Nginx на домашнем сервере
- Проксирование через внешний сервер
- Настройка DNS
- Подключение к Prometheus
- Проверка работоспособности
1. Подготовка сервера
1.1. Проверяем здоровье диска
Прежде чем нагружать сервер — проверьте состояние диска. GitLab активно использует диск, и если он на излёте, лучше знать об этом заранее:
apt install -y smartmontools
smartctl -H /dev/sda # общий вердикт: PASSED или FAILED
smartctl -a /dev/sda # полная информация
Обратите внимание на эти параметры:
Reallocated_Sector_Ct— переназначенные сектора. Если больше 0 — диск уже имеет бэдыCurrent_Pending_Sector— потенциальные бэды, ожидающие переназначенияLoad_Cycle_Count— количество парковок головки. Для ноутбучных HDD паспортный ресурс 300 000–600 000 цикловPower_On_Hours— суммарное время работы диска
Важно: Если используете ноутбучный HDD,
Load_Cycle_Countможет быть критически высоким даже при нулевых бэдах. Такой диск паркует головку при каждом простое чтобы экономить батарею — на сервере работающем 24/7 это накапливается быстро. Если счётчик уже превышает паспортный ресурс — следующий шаг обязателен.
1.2. Отключаем агрессивный паркинг HDD
Актуально только для ноутбучных HDD. Если у вас серверный HDD или SSD — пропускайте этот шаг.
Отключаем агрессивный паркинг — это остановит дальнейший рост Load_Cycle_Count:
apt install -y hdparm
hdparm -B 254 /dev/sda # 254 = минимальный APM, почти отключён
Проверяем что применилось — в выводе должно быть APM_level = 254. Делаем настройку постоянной через udev-правило, иначе после перезагрузки вернётся агрессивный паркинг:
echo 'ACTION=="add", SUBSYSTEM=="block", KERNEL=="sda", RUN+="/sbin/hdparm -B 254 /dev/sda"' \
> /etc/udev/rules.d/69-hdparm.rules
Через несколько дней можно проверить что счётчик перестал активно расти:
smartctl -a /dev/sda | grep Load_Cycle_Count
1.3. Настраиваем SWAP
GitLab в пике потребляет 3–5 GiB RAM. Если на сервере параллельно работают другие сервисы и памяти немного — можно упереться в потолок во время тяжёлых операций, например git push с CI. SWAP здесь не рабочий режим, а страховка от падения.
Если у вас много RAM или GitLab единственный сервис — этот раздел можно пропустить или ограничиться 4 GiB SWAP. На сервере с 16+ GiB RAM и только GitLab SWAP практически не используется.
Что такое SWAP: часть диска, работающая как резервная RAM. Когда оперативная память заканчивается — без SWAP ядро убивает процессы (OOM Killer), GitLab просто падает. Со SWAP вместо падения ядро перекладывает «холодные» данные на диск — медленно, но сервис не упадёт.
Проверяем что уже есть на сервере:
swapon --show
Если увидели /swap.img с небольшим размером — лучше увеличить его, чем создавать второй файл:
# Отключаем текущий swap
swapoff /swap.img
# Дописываем ещё 4G к существующим 4G (итого 8G)
# Размер подбирайте под объём вашей RAM и количество сервисов
dd if=/dev/zero bs=1M count=4096 >> /swap.img
# Переинициализируем и включаем
mkswap /swap.img
swapon /swap.img
Если /swap.img не было — создаём с нуля:
fallocate -l 8G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Снижаем агрессивность использования SWAP — чтобы система не трогала его без крайней необходимости:
echo 'vm.swappiness=10' >> /etc/sysctl.conf
sysctl -p
Проверяем результат:
swapon --show
free -h
Проверяем автозапуск — файл SWAP должен быть прописан в /etc/fstab:
cat /etc/fstab | grep swap
Симуляция перезагрузки без самой перезагрузки:
swapoff /swap.img && swapon -a
swapon --show # должен подняться обратно
1.4. Устанавливаем зависимости и Nginx
В этом гайде мы отключаем встроенный Nginx GitLab и используем системный — это нужно потому что на сервере живут другие сайты и Nginx управляет всеми виртуальными хостами централизованно.
Если GitLab единственный сервис на сервере — системный Nginx не нужен. GitLab поставляется со своим встроенным Nginx который уже настроен под него и поддерживается пакетом. В таком случае пропустите установку Nginx здесь, а в разделе 3 не отключайте
nginx['enable'].
apt update && apt upgrade -y
apt install -y curl ca-certificates tzdata perl postfix nginx
# В диалоге postfix выбираем "Internet Site"
systemctl enable nginx
2. Установка GitLab CE
2.1. Добавляем репозиторий
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | bash
2.2. Устанавливаем GitLab
Указываем внешний URL сразу при установке — это нужно GitLab для правильной генерации ссылок и CSRF-защиты:
EXTERNAL_URL="https://git.example.com" apt install -y gitlab-ce # ЗАМЕНИТЕ ДОМЕН
Установка занимает 5–10 минут и требует ~3–4 GiB RAM в пике. Если сервер в этот момент выключится — проверьте логи после перезагрузки:
dpkg -l | grep gitlab-ce # ii = установлен, iF = установлен но не сконфигурирован
dmesg | grep -i "oom\|killed" | tail -20
Если статус iF — пакет установился, но конфигурация не завершилась. Лечится запуском:
gitlab-ctl reconfigure
Классическая ошибка в GitLab 18.x:
reconfigureпадает с ошибкойReading unsupported config value grafana. В этой версии встроенная Grafana убрана, строкаgrafana['enable'] = falseбольше не поддерживается. Найдите и удалите её изgitlab.rbперед повторным запуском reconfigure:
grep -n grafana /etc/gitlab/gitlab.rb # найдёт номер строки
sed -i 'Xd' /etc/gitlab/gitlab.rb # ЗАМЕНИТЕ X на найденный номер строки
3. Настройка gitlab.rb
3.1. Основная конфигурация
GitLab по умолчанию поднимает собственный Nginx и пытается получить SSL-сертификат через Let's Encrypt. Если GitLab единственный сервис и 443 порт открыт — это удобно и работает из коробки. В нашем случае оба механизма отключены: SSL терминируется на внешнем сервере, а Nginx управляется системно.
nano /etc/gitlab/gitlab.rb
external_url 'https://git.example.com' # ЗАМЕНИТЕ НА ВАШ ДОМЕН
# Отключаем встроенный Nginx — используем системный
# Если GitLab единственный сервис — уберите эту строку
nginx['enable'] = false
# Отключаем встроенный Let's Encrypt — сертификаты получаем сами
# Если порт 443 открыт и GitLab один на сервере — уберите эту строку,
# Let's Encrypt настроится автоматически
letsencrypt['enable'] = false
# GitLab слушает локально на порту 8181
gitlab_workhorse['listen_network'] = "tcp"
gitlab_workhorse['listen_addr'] = "127.0.0.1:8181"
# Оптимизация под ограниченные ресурсы сервера с несколькими сервисами.
# Если GitLab единственный сервис или сервер мощный — уберите эти строки,
# GitLab выставит оптимальные значения сам исходя из CPU и RAM
puma['worker_processes'] = 2
puma['min_threads'] = 1
puma['max_threads'] = 2
sidekiq['concurrency'] = 5
# Встроенный Prometheus и Alertmanager отключены — используется внешний мониторинг
# Если своего Prometheus нет — уберите эти строки, GitLab поднимет мониторинг сам
prometheus['enable'] = false
alertmanager['enable'] = false
# grafana['enable'] = false — НЕ добавлять! В GitLab 18.x этого параметра нет
# GitLab Exporter для подключения к внешнему Prometheus
# Если внешнего мониторинга нет — пропустите этот блок
prometheus_monitoring['enable'] = true
node_exporter['enable'] = false
redis_exporter['enable'] = false
postgres_exporter['enable'] = false
gitlab_exporter['enable'] = true
gitlab_exporter['listen_address'] = '0.0.0.0'
gitlab_exporter['listen_port'] = '9168'
# Container Registry — свой Docker Hub
registry['enable'] = true
registry_external_url 'https://dcr.example.com' # ЗАМЕНИТЕ НА ВАШ ДОМЕН
# Pages не нужны
gitlab_pages['enable'] = false
# Оптимизация PostgreSQL под ограниченные ресурсы.
# На мощном сервере — уберите эти строки
postgresql['shared_buffers'] = "128MB"
postgresql['max_worker_processes'] = 2
3.2. Применяем конфигурацию
gitlab-ctl reconfigure
Должно завершиться строкой gitlab Reconfigured!. Проверяем что все сервисы запустились:
gitlab-ctl status
В списке должны быть run: для: gitaly, gitlab-exporter, gitlab-workhorse, postgresql, puma, redis, registry, sidekiq. Если что-то в статусе down — смотрим логи:
gitlab-ctl tail
4. Nginx на домашнем сервере
Этот раздел актуален только если вы отключили встроенный Nginx GitLab в предыдущем разделе. Если GitLab единственный сервис — пропускайте.
Домашний Nginx работает только по HTTP на порту 80 — SSL здесь не нужен, шифрование обеспечивает внешний сервер. Редирект на HTTPS добавлять не нужно — иначе получим бесконечный цикл.
4.1. Конфиг для GitLab
nano /etc/nginx/sites-available/git.example.com # ЗАМЕНИТЕ ДОМЕН
upstream gitlab {
server 127.0.0.1:8181;
}
server {
listen 80;
server_name git.example.com; # ЗАМЕНИТЕ НА ВАШ ДОМЕН
client_max_body_size 250m; # для больших репозиториев и пушей
location / {
proxy_pass http://gitlab;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https; # говорим GitLab что снаружи HTTPS
proxy_set_header X-Forwarded-Ssl on; # необходимо для корректной CSRF-защиты
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
}
Важно: Заголовки
X-Forwarded-Proto httpsиX-Forwarded-Ssl on— не опциональные. Без них GitLab считает что работает по HTTP, и при попытке войти на сайт выдаёт ошибку422: The change you requested was rejected. Это CSRF-защита, реагирующая на несоответствие междуexternal_url(HTTPS) и реальным протоколом запроса (HTTP).
4.2. Конфиг для Container Registry
nano /etc/nginx/sites-available/dcr.example.com # ЗАМЕНИТЕ ДОМЕН
server {
listen 80;
server_name dcr.example.com; # ЗАМЕНИТЕ НА ВАШ ДОМЕН
client_max_body_size 0; # без ограничений — Docker-образы могут быть большими
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:5000; # порт Registry по умолчанию
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
}
4.3. Активируем конфиги
ln -s /etc/nginx/sites-available/git.example.com /etc/nginx/sites-enabled/ # ЗАМЕНИТЕ
ln -s /etc/nginx/sites-available/dcr.example.com /etc/nginx/sites-enabled/ # ЗАМЕНИТЕ
nginx -t
systemctl reload nginx
Проверяем что GitLab отвечает локально:
curl -I http://127.0.0.1:8181
Должен вернуть 302 Found с редиректом на /users/sign_in — GitLab работает.
5. Проксирование через внешний сервер
Этот раздел нужен только если провайдер блокирует входящий 443 порт. Сначала проверьте — с любой внешней машины выполните:
nc -zv ВАШ_IP 80
nc -zv ВАШ_IP 443
Если 80 отвечает succeeded, а 443 висит и выдаёт Connection timed out — провайдер режет входящий HTTPS. Если оба порта открыты — этот раздел пропускайте, настройте SSL через встроенный certbot или Let's Encrypt GitLab напрямую на домашнем сервере.
При заблокированном 443 решение — терминировать HTTPS на внешнем сервере и проксировать трафик на домашний по открытому 80 порту. Итоговая схема:
Пользователь
│
▼
Внешний сервер :443 (SSL termination, Nginx)
│ proxy_pass по $host → домашний :80
▼
Домашний сервер :80 (Nginx)
│ server_name
├── git.example.com → GitLab :8181
└── dcr.example.com → Registry :5000
5.1. Подготовка общего файла настроек проксирования
Выносим общие настройки в отдельный файл — чтобы при добавлении новых сайтов не копировать одни и те же строки. Подключаемся к внешнему серверу:
mkdir -p /etc/nginx/includes
nano /etc/nginx/includes/proxy-to-home.conf
proxy_pass http://IP_ДОМАШНЕГО_СЕРВЕРА; # ЗАМЕНИТЕ
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
client_max_body_size 250m;
5.2. Получаем SSL-сертификаты
DNS уже должен указывать на внешний сервер — проверяем:
ping git.example.com # должен вернуть IP внешнего сервера
Классическая ошибка: если в конфиге Nginx уже прописан путь к сертификату которого ещё нет — certbot упадёт, потому что Nginx не стартует без файла сертификата. Порядок важен: сначала временно убрать конфиг из
sites-enabled, перезапустить Nginx, получить сертификат, вернуть конфиг.
apt install -y certbot python3-certbot-nginx
certbot certonly --nginx \
-d git.example.com \
-d dcr.example.com \
--email your@email.com \ # ЗАМЕНИТЕ
--agree-tos \
--non-interactive
Сертификаты действуют 90 дней. Certbot автоматически настраивает systemd-таймер для обновления — проверяем:
certbot renew --dry-run
5.3. Конфиг для GitLab и Registry на внешнем сервере
nano /etc/nginx/sites-available/home-proxy
# HTTP → HTTPS для всех доменов домашнего сервера
server {
listen 80;
server_name git.example.com dcr.example.com; # ЗАМЕНИТЕ ДОМЕНЫ
return 301 https://$host$request_uri;
}
# GitLab
server {
listen 443 ssl;
server_name git.example.com; # ЗАМЕНИТЕ
ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem; # ЗАМЕНИТЕ
ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem; # ЗАМЕНИТЕ
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
include /etc/nginx/includes/proxy-to-home.conf;
}
}
# Container Registry
server {
listen 443 ssl;
server_name dcr.example.com; # ЗАМЕНИТЕ
ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem; # ЗАМЕНИТЕ
ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem; # ЗАМЕНИТЕ
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
include /etc/nginx/includes/proxy-to-home.conf;
}
}
ln -s /etc/nginx/sites-available/home-proxy /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
5.4. Как добавлять новые сайты в будущем
Схема универсальная — новый сайт добавляется в четыре шага и не требует трогать уже настроенные конфиги.
Шаг 1 — DNS: добавляем A-запись в панели регистратора, указываем на IP внешнего сервера.
Шаг 2 — SSL на внешнем сервере:
certbot certonly --nginx -d newsite.example.com --email your@email.com --agree-tos --non-interactive
Шаг 3 — добавляем блок в home-proxy на внешнем сервере:
server {
listen 80;
server_name newsite.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name newsite.example.com;
ssl_certificate /etc/letsencrypt/live/newsite.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/newsite.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
include /etc/nginx/includes/proxy-to-home.conf;
}
}
nginx -t && systemctl reload nginx
Шаг 4 — конфиг на домашнем сервере:
server {
listen 80;
server_name newsite.example.com;
location / {
# ваш сайт
}
}
nginx -t && systemctl reload nginx
6. Настройка DNS
DNS-записи для GitLab и Registry должны указывать на IP внешнего сервера — именно он принимает HTTPS и проксирует дальше. В панели регистратора добавляем A-записи:
git.example.com → IP_ВНЕШНЕГО_СЕРВЕРА TTL 300
dcr.example.com → IP_ВНЕШНЕГО_СЕРВЕРА TTL 300
Проверяем распространение DNS:
ping git.example.com # должен вернуть IP внешнего сервера
ping dcr.example.com
Важно: Если ранее получали сертификаты на домашнем сервере — после смены DNS их стоит удалить. Certbot при попытке обновления будет падать, потому что домен больше не резолвится на домашний IP:
certbot delete --cert-name git.example.com
7. Подключение к Prometheus
Этот раздел актуален только при наличии внешнего Prometheus. Если своего мониторинга нет — GitLab при включённом встроенном Prometheus поднимает его сам без каких-либо правок конфига. Если хотите выстроить полноценную систему мониторинга с нуля — у меня есть отдельный гайд: Мониторинг серверов с Prometheus и Grafana: с нуля до централизованных дашбордов.
GitLab Exporter собирает метрики самого GitLab на уровне приложения: количество репозиториев, активных пользователей, время ответа CI-пайплайнов, статистику фоновых задач Sidekiq. Именно его мы подключаем к внешнему Prometheus.
7.1. Открываем порт экспортера
На домашнем сервере открываем порт GitLab Exporter только для сервера с Prometheus — не для всего интернета:
ufw allow from IP_PROMETHEUS_СЕРВЕРА to any port 9168 # ЗАМЕНИТЕ IP
ufw reload
7.2. Добавляем target в Prometheus
На сервере с Prometheus добавляем в prometheus.yml:
scrape_configs:
- job_name: 'gitlab'
static_configs:
- targets: ['IP_ДОМАШНЕГО_СЕРВЕРА:9168'] # ЗАМЕНИТЕ IP
labels:
instance: 'home'
service: 'gitlab'
systemctl restart prometheus
Проверяем что метрики доступны — открываем в браузере http://IP_ДОМАШНЕГО_СЕРВЕРА:9168/metrics с IP сервера Prometheus. Должна вернуться страница с метриками.
8. Проверка работоспособности
8.1. Базовая проверка
Открываем в браузере:
https://git.example.com— открывается страница входа GitLabhttp://git.example.com— редиректит на HTTPShttps://git.example.com/admin/— админка доступна
8.2. Меняем пароль root
Начальный пароль хранится в файле и действителен только 24 часа:
cat /etc/gitlab/initial_root_password
Входим на сайт с логином root и этим паролем. Сразу меняем: аватар в правом верхнем углу → Edit profile → Password. Если пароль уже истёк — сбрасываем через консоль без старого пароля:
gitlab-rake "gitlab:password:reset[root]"
8.3. Статус сервисов
gitlab-ctl status # все должны быть run:
systemctl status nginx
8.4. Мониторинг логов
gitlab-ctl tail # все логи GitLab в реальном времени
tail -f /var/log/nginx/error.log
8.5. Container Registry
Container Registry — это личный Docker Hub. Вместо того чтобы хранить образы на публичном hub.docker.com, вы пушите их на свой сервер. Основной сценарий — CI/CD: код запушен в GitLab, GitLab автоматически собирает Docker-образ, сохраняет в Registry, деплоит на сервер.
Проверяем через веб-интерфейс: любой проект → Deploy → Container Registry. Registry пустой до первого пуша — это нормально.
Для работы с Registry с локальной машины:
docker login dcr.example.com # логин и пароль как в GitLab
docker build -t dcr.example.com/myproject/myapp:latest .
docker push dcr.example.com/myproject/myapp:latest
8.6. Тест автозапуска
reboot
# После перезагрузки:
gitlab-ctl status
systemctl status nginx
swapon --show
Все три команды должны показывать что сервисы поднялись автоматически.
Заключение
Теперь у вас полноценный GitLab на домашнем железе:
- Диск защищён от быстрого износа через отключение агрессивного паркинга
- SWAP страхует от падения при нехватке RAM
- GitLab работает через системный Nginx — сервер готов к хостингу других сайтов
- HTTPS терминируется на внешнем сервере — блокировка 443 провайдером обойдена
- Container Registry доступен для хранения Docker-образов
- GitLab Exporter подключён к внешнему Prometheus
- Универсальная схема проксирования позволяет добавлять новые сайты в четыре шага
При возникновении проблем первым делом смотрите логи. gitlab-ctl tail показывает что происходит внутри GitLab, /var/log/nginx/error.log — проблемы на уровне Nginx. Большинство ошибок подробно описаны там.
Следите за здоровьем диска если используете ноутбучный HDD — раз в несколько месяцев прогоняйте smartctl -H /dev/sda и проверяйте что Load_Cycle_Count больше не растёт активно.
Комментарии (0)
Чтобы оставить комментарий, войдите или зарегистрируйтесь.
Комментариев пока нет. Будьте первым!