GitLab CE в Docker Compose с Traefik
Опубликовано:
Используемые термины: GitLab, Docker, Traefik.
В этой инструкции развернем GitLab CE в Docker Compose с Traefik в роли reverse proxy: Traefik возьмет на себя TLS через Let's Encrypt, а GitLab будет работать за ним по HTTPS. Команды приведены для Ubuntu, Debian и RPM-дистрибутивов отдельно, где это нужно.
Системные требования
Смена SSH-порта хоста для освобождения порта 22 под Gitlab
Создание конфигурации
Пример файла docker-compose
Запуск и проверка первого подключения
Работа с резерыными копиями
Обновление GitLab
Решение возможных проблем
Дополнительные материалы
Требования
- Linux-сервер с публичным IP и установленными Docker Engine и Docker Compose plugin. Подробнее об этом ниже.
- Не менее 4 ГБ оперативной памяти — Omnibus GitLab требователен к RAM даже с уменьшенным числом воркеров.
- DNS A-запись домена GitLab на IP сервера.
- Открытые порты 80, 443 (HTTP/HTTPS) и 22 (Git по SSH).
Убедимся, что Docker и Compose plugin уже установлены на сервере:
docker --version
docker-compose version
Если docker compose установлен как плагин, то команда будет:
docker compose version
* если команд нет — потребуется установка Docker Compose и Docker Engine. Подробнее об этом в конце инструкции.
Смена SSH-порта хоста на 2222
В compose GitLab публикует 22:22. Для удобства работы с Gitlab лучше так и оставить, но чтобы порт 22 на хосте не занял sshd, перенесем системный SSH на 2222 до запуска контейнеров. Сессию не закрываем, пока не проверим вход по новому порту.
Создадим конфиг с новым портом:
mkdir -p /etc/ssh/sshd_config.d
vi /etc/ssh/sshd_config.d/port.conf
Port 2222
* Port 2222 — порт SSH самой хостовой системы; GitLab будет слушать 22.
** на старых системах (Debian 10, CentOS 7) в основном sshd_config может не быть директивы Include для sshd_config.d — проверьте это, иначе конфиг не подхватится. В таком случае, настройки нужно делать в файле /etc/ssh/sshd_config.
Откроем порт 2222 в firewall — выберем вариант под свою систему.
а) firewall — Iptables (Deb):
iptables -I INPUT -p tcp --dport 2222 -j ACCEPT
netfilter-persistent save
б) firewall — UFW (Deb):
ufw allow 2222/tcp && ufw reload
в) firewall — firewalld (RPM):
firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --reload
Проверим, какие порты слушает сервер:
ss -tlnp | grep -E ':22|:2222'
* на 2222 должен слушать sshd хоста; порт 22 пока свободен — его займет контейнер GitLab.
Во втором терминале проверьте вход: ssh -p 2222 root@IP. Только после успешного входа продолжайте.
Применим новый порт и перезапустим SSH — выберем вариант под свой дистрибутив.
а) для Deb (Debian / Ubuntu):
sshd -t && systemctl restart ssh
б) для RPM (Rocky Linux / AlmaLinux / Fedora):
semanage port -a -t ssh_port_t -p tcp 2222 || semanage port -m -t ssh_port_t -p tcp 2222
sshd -t && systemctl restart sshd
* где:
- sshd -t — проверка синтаксиса конфига до перезапуска.
- semanage port — разрешение нестандартного SSH-порта в SELinux (RPM).
Порт SSH хоста сменен, можно переходить к развертыванию GitLab.
Каталог и переменные окружения
Создадим рабочий каталог проекта и перейдем в него:
mkdir -p /opt/gitlab && cd /opt/gitlab
Опишем переменные окружения в файле .env:
vi .env
TZ=Europe/Moscow
TRAEFIK_TAG=v3.6
GITLAB_TAG=19.2.2-ce.0
ACME_EMAIL=admin@example.com
GITLAB_HOST=gitlab.dmosk.ru
GITLAB_ROOT_PASSWORD=ChangeMe_StrongPass1
* где:
- TZ — часовой пояс контейнеров и GitLab; замените на свой, если сервер не в московском часовом поясе (например, Etc/UTC).
- TRAEFIK_TAG / GITLAB_TAG — теги образов; при необходимости зафиксируйте более актуальные версии, сверив их с Docker Hub.
- ACME_EMAIL — email для Let's Encrypt.
- GITLAB_HOST — FQDN GitLab; A-запись должна указывать на этот сервер.
- GITLAB_ROOT_PASSWORD — начальный пароль пользователя root (не короче 8 символов); применяется только при первой инициализации.
Файл docker-compose.yml
Опишем сервисы Traefik и GitLab в docker-compose.yml:
vi docker-compose.yml
services:
traefik:
image: traefik:${TRAEFIK_TAG}
container_name: gitlab-traefik
hostname: traefik
restart: unless-stopped
environment:
TZ: ${TZ}
DOCKER_API_VERSION: 1.44
command:
- "--log.level=INFO"
- "--ping=true"
- "--api.dashboard=false"
- "--providers.docker=true"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=gitlab"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/letsencrypt:/letsencrypt
networks:
- gitlab
healthcheck:
test: ["CMD-SHELL", "traefik healthcheck --ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 5s
gitlab:
image: gitlab/gitlab-ce:${GITLAB_TAG}
container_name: gitlab
hostname: ${GITLAB_HOST}
restart: unless-stopped
shm_size: "512m"
environment:
TZ: ${TZ}
GITLAB_ROOT_PASSWORD: ${GITLAB_ROOT_PASSWORD}
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://${GITLAB_HOST}'
nginx['listen_port'] = 80
nginx['listen_https'] = false
gitlab_rails['gitlab_shell_ssh_port'] = 22
gitlab_rails['time_zone'] = '${TZ}'
nginx['real_ip_trusted_addresses'] = ['172.16.0.0/12', '10.0.0.0/8', '192.168.0.0/16']
nginx['real_ip_header'] = 'X-Forwarded-For'
nginx['real_ip_recursive'] = 'on'
puma['worker_processes'] = 2
sidekiq['concurrency'] = 5
prometheus_monitoring['enable'] = false
alertmanager['enable'] = false
node_exporter['enable'] = false
redis_exporter['enable'] = false
postgres_exporter['enable'] = false
gitlab_exporter['enable'] = false
expose:
- "80"
ports:
- "22:22"
volumes:
- ./gitlab/config:/etc/gitlab
- ./gitlab/logs:/var/log/gitlab
- ./gitlab/data:/var/opt/gitlab
networks:
- gitlab
labels:
- "traefik.enable=true"
- "traefik.docker.network=gitlab"
- "traefik.http.routers.gitlab.rule=Host(`${GITLAB_HOST}`)"
- "traefik.http.routers.gitlab.entrypoints=websecure"
- "traefik.http.routers.gitlab.tls.certresolver=letsencrypt"
- "traefik.http.routers.gitlab.service=gitlab"
- "traefik.http.routers.gitlab-http.rule=Host(`${GITLAB_HOST}`)"
- "traefik.http.routers.gitlab-http.entrypoints=web"
- "traefik.http.routers.gitlab-http.middlewares=gitlab-redirect-https"
- "traefik.http.routers.gitlab-http.service=gitlab"
- "traefik.http.middlewares.gitlab-redirect-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.gitlab-redirect-https.redirectscheme.permanent=true"
- "traefik.http.services.gitlab.loadbalancer.server.port=80"
- "traefik.http.services.gitlab.loadbalancer.passhostheader=true"
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:80/-/health || exit 1"]
interval: 60s
timeout: 10s
retries: 10
start_period: 600s
networks:
gitlab:
name: gitlab
driver: bridge
* где:
- traefik — reverse proxy на 80/443, HTTP→HTTPS и выпуск сертификата Let's Encrypt.
- DOCKER_API_VERSION — версия Docker API для провайдера Traefik; задана прямо в этом файле, отдельной переменной в .env для нее нет. На старых версиях Docker Engine значение может не совпасть с поддерживаемым API — тогда версию нужно снизить.
- prometheus_monitoring / node_exporter / redis_exporter / postgres_exporter / gitlab_exporter — отключены для экономии ресурсов. Если нужны графики производительности во встроенной админке GitLab — уберите эти строки или замените false на true.
- gitlab — GitLab CE; nginx внутри слушает 80 без TLS, TLS терминирует Traefik.
- ports 22:22 — Git по SSH на стандартном порту 22 хоста.
- ./gitlab/config, ./gitlab/logs, ./gitlab/data — постоянные данные Omnibus GitLab относительно /opt/gitlab.
- start_period: 600s — первый старт GitLab может занимать несколько минут.
На RPM-системах с SELinux (Rocky Linux, AlmaLinux, RHEL) примонтированные локальные каталоги могут упасть с ошибкой Permission denied. Если это произошло, добавьте суффикс :Z к путям в volumes, например ./gitlab/config:/etc/gitlab:Z.
Запуск и первый вход
Запустим стек:
docker-compose up -d
Проверим статус контейнеров:
docker-compose ps
Посмотрим лог инициализации GitLab:
docker-compose logs -f --tail=100 gitlab
* ждем строки вроде gitlab Reconfigured! / готовности healthcheck; выход из follow — Ctrl+C.
Открываем в браузере https://gitlab.dmosk.ru (в вашем случае будет другой домен). Вводим данные для входа:
- Логин: root
- Пароль: значение GITLAB_ROOT_PASSWORD из /opt/gitlab/.env
Убедимся, что порты распределены верно:
ss -tlnp | grep -E ':22|:2222'
* 2222 — sshd хоста; 22 — SSH GitLab (git clone/push по SSH без указания нестандартного порта).
Стоит сразу сменим пароль root в интерфейсе GitLab и убрать GITLAB_ROOT_PASSWORD из .env — переменная нужна только для первой инициализации.
Резервное копирование
Рассмотрим небольшой пример, как сделать резервную копию и восстановиться из нее.
Остановка и бэкап
Сделаем бэкап данных перед остановкой:
cd /opt/gitlab && docker-compose exec -T gitlab gitlab-backup create STRATEGY=copy
* архив появится в /opt/gitlab/data/backups внутри volume data.
Остановим стек:
docker-compose down
* контейнеры остановлены; каталоги config, logs, data, traefik на диске сохраняются.
Заберем бэкап и конфигурацию с сервера одним архивом:
tar -czf /root/gitlab-backup-$(date +%F).tar.gz -C /opt gitlab/config gitlab/data gitlab/traefik gitlab/.env gitlab/docker-compose.yml
* где:
- config / data — конфигурация Omnibus GitLab и данные приложения (репозитории, БД, uploads).
- traefik — в том числе acme.json с сертификатами.
- .env и docker-compose.yml — чтобы поднять тот же стенд на другом сервере.
В архиве оказываются секреты в открытом виде: gitlab-secrets.json из config (без него не расшифровать данные БД) и пароль root из .env. Храните архив в безопасном месте и ограничьте к нему доступ.
Вернем стек в работу:
docker-compose up -d
* возврат сервисов после бэкапа файлов.
Восстановление из бэкапа
Перед восстановлением остановим внутренние сервисы GitLab, которые пишут в базу:
docker-compose exec -T gitlab gitlab-ctl stop puma
docker-compose exec -T gitlab gitlab-ctl stop sidekiq
Запустим восстановление из архива:
docker-compose exec -T gitlab gitlab-backup restore BACKUP=1717000000_2024_06_01_16.11.0-ce
* BACKUP — имя архива из /opt/gitlab/data/backups без суффикса _gitlab_backup.tar.
Вернем сервисы в работу:
docker-compose exec -T gitlab gitlab-ctl start
GitLab восстановлен из бэкапа и снова доступен по HTTPS.
Обновление GitLab
Перед обновлением обязательно сделаем бэкап (см. раздел выше) — GitLab не переносит пропуск нескольких мажорных версий подряд, апгрейд лучше делать поэтапно.
Поднимем значение GITLAB_TAG (и при необходимости TRAEFIK_TAG) в .env:
vi .env
...
TRAEFIK_TAG=vXXX
GITLAB_TAG=XXX
...
Затем обновим образы и перезапустим стек:
docker-compose pull
docker-compose up -d
* GitLab выполнит reconfigure и миграции автоматически при старте; прогресс смотрим через docker-compose logs -f --tail=100 gitlab.
GitLab CE развернут в Docker Compose за Traefik и готов к работе.
Возможные проблемы
Рассмотрим несколько наиболее популярных проблем.
1. Traefik отдаёт 502 при старте
После запуска контейнера веб-интерфейс не грузится и отдает ошибку 502.
Причина: GitLab проходит внутренний reconfigure — инициализация БД, миграции, запуск puma/sidekiq. Контейнер уже поднялся, но приложение ещё не готово принимать запросы.
Решение: дождаться перехода healthcheck в healthy. Для этого в конфиге задаётся start_period: 600s — GitLab может стартовать до 10 минут. Проверить состояние можно командой:
docker ps --filter name=gitlab
Пока статус (health: starting) — 502 это норма, просто ждём.
2. Let's Encrypt перестаёт выдавать сертификаты
Причина: лимит — 5 сертификатов на один домен в неделю. При частом пересоздании контейнера Traefik каждый раз запрашивает новый сертификат и упирается в ограничение.
Решение: хранить сертификаты в томе и не удалять его между перезапусками. Настройка для этого находится в директиве volumes:
traefik:
...
volumes:
- ./traefik/letsencrypt:/letsencrypt # не удалять эту папку
Если лимит уже исчерпан — ждать неделю или использовать staging-сервер Let's Encrypt для тестов.
3. GitLab падает из-за нехватки памяти
Gitlab перестает работать, а в статусе контейнеров можно наблюдать периодические перезапуски.
Причина: puma (веб-сервер) и sidekiq (фоновые задачи) по умолчанию потребляют много RAM. На сервере с 2–4 ГБ GitLab просто не стартует или убивается OOM-killer'ом.
Решение: уменьшить количество воркеров в docker-compose.yml:
gitlab:
environment:
GITLAB_OMNIBUS_CONFIG: |
puma['worker_processes'] = 2
sidekiq['concurrency'] = 10
Либо увеличить RAM сервера — минимально комфортно 4 ГБ, лучше 8 ГБ.
Читайте также
Полезные инструкции, связанные с данной темой:
1. Установка Docker Compose на Linux.