Installing Gitlab behind Traefik

Insider Blog

The simplest way to install GitLab

GitLab provides official instructions to install it using Docker and Docker Compose. You can find it in the official documentation

gitlab/docker-compose.yaml
services:
  gitlab:
    image: gitlab/gitlab-ee:<version>-ce.0
    container_name: gitlab
    restart: always
    hostname: '<gitlab.example.com>'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url 'https://<gitlab.example.com>'
    ports:
      - '80:80'
      - '443:443'
      - '22:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

This assumes you have your server dedicated to GitLab and all required ports (http, https, ssh) are free. GitLab will take care of everything, including TLS configuration with Lets Encrypt

Integrating GitLab alongside other deployments

What if you can't or don't want to install GitLab on a dedicated server?

Maybe you just want a single server for all you services?

This is an example structure how you could organize your deployments:

.
├── gitlab/                # GitLab service stack
├── gitlab-runner/         # GitLab Runner for CI/CD
├── mattermost/            # Mattermost team collaboration
├── nextcloud/             # Nextcloud file hosting and collaboration
└── traefik/               # Traefik reverse proxy configuration

Using a dedicated IP

In case you have a possibility to attach an additional IP to your server (like a Floating IP on Hetzner), you can simply bind the ports to this IP.

gitlab/docker-compose.yaml
services:
  gitlab:
    image: gitlab/gitlab-ee:<version>-ce.0
    container_name: gitlab
    restart: always
    hostname: '<gitlab.example.com>'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url 'https://<gitlab.example.com>'
    ports:
      - '192.168.0.1:80:80'
      - '192.168.0.1:443:443'
      - '192.168.0.1:22:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

Using Traefik as a reverse proxy

In this case I assume that you:

  • can't or don't want to assign a dedicated IP for GitLab
  • can't or don't want to change you default ssh port
  • want to use Traefik as a reverse proxy

The Traefik deployment

Remember to replace <[email protected]> with your own email.

traefik/docker-compose.yaml
volumes:
  letsencrypt:

services:
  traefik:
    image: traefik:3
    container_name: traefik
    restart: always
    network_mode: host
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt:/letsencrypt
    command:
      - --log.level=INFO

      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https

      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls.certresolver=letsencrypt

      - --providers.docker=true
      - --providers.docker.exposedByDefault=false

      - --certificatesresolvers.letsencrypt.acme.email=<[email protected]>
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web

This setup will:

The GitLab deployment

Remember to replace <gitlab.example.com> with your own domain.

Please take a look in the official dokumentation about GITLAB_HOME directory

gitlab/docker-compose.yaml
services:
  gitlab:
    image: gitlab/gitlab-ce:<version>-ce.0
    container_name: gitlab
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gitlab.rule=Host(`<gitlab.example.com>`)"
      - "traefik.http.routers.gitlab.service=gitlab"
      - "traefik.http.services.gitlab.loadbalancer.server.port=80"
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url '<https://gitlab.example.com>'
        
        letsencrypt['enable'] = false
        
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = {
          "X-Forwarded-Proto" => "https",
          "X-Forwarded-Ssl" => "on"
        }
        
        gitlab_rails['gitlab_shell_ssh_port'] = 2424
    ports:
      - '2424:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

This setup will:

  • disable the Let's Encrypt
  • disable listening on https and listen on port 80
  • set the required proxy headers
  • change the ssh port to 2424
  • instruct Traefik to route all traffic for gitlab.example.com to port 80 of the container

That's it!



GitLab Container registry

When you got so far, you probably also want to set up the container registry

Configure container registry under its own domain

This setup is easy. We just need to set the registry_external_url and instruct Traefik where to route the requests


Update the GitLab config
gitlab/docker-compose.yaml
services:
  gitlab:
    image: gitlab/gitlab-ce:<version>-ce.0
    container_name: gitlab
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gitlab.rule=Host(`<gitlab.example.com>`)"
      - "traefik.http.routers.gitlab.service=gitlab"
      - "traefik.http.services.gitlab.loadbalancer.server.port=80"
      - "traefik.http.routers.container-registry.rule=Host(`registry.gitlab.example.com`)"
      - "traefik.http.services.container-registry.loadbalancer.server.port=5000"
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url '<https://gitlab.example.com>'

        letsencrypt['enable'] = false

        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = {
          "X-Forwarded-Proto" => "https",
          "X-Forwarded-Ssl" => "on"
        }

        gitlab_rails['gitlab_shell_ssh_port'] = 2424

        registry_external_url '<https://registry.gitlab.example.com>'
        registry_nginx['enable'] = false
    ports:
      - '2424:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

This setup will:

  • set the registry external url
  • disable the nginx for the registry
  • instruct Traefik to route all traffic for registry.gitlab.example.com to port 5000 of the container


Configure container registry under an existing GitLab domain

Update the Traefik config
traefik/docker-compose.yaml
volumes:
  letsencrypt:

services:
  traefik:
    image: traefik:3
    container_name: traefik
    restart: always
    network_mode: host
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt:/letsencrypt
    command:
      - --log.level=INFO

      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https

      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls.certresolver=letsencrypt
        
      - --entrypoints.container-registry.address=:5050
      - --entrypoints.container-registry.http.tls.certresolver=letsencrypt

      - --providers.docker=true
      - --providers.docker.exposedByDefault=false

      - --certificatesresolvers.letsencrypt.acme.email=<[email protected]>
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web

This setup will:

  • add a new entry point for the container registry
  • apply the TLS configuration to all routes in this entrypoint

Update the GitLab config
gitlab/docker-compose.yaml
services:
  gitlab:
    image: gitlab/gitlab-ce:<version>-ce.0
    container_name: gitlab
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gitlab.rule=Host(`<gitlab.example.com>`)"
      - "traefik.http.routers.gitlab.entrypoints=web,websecure"
      - "traefik.http.routers.gitlab.service=gitlab"
      - "traefik.http.services.gitlab.loadbalancer.server.port=80"
      - "traefik.http.routers.container-registry.rule=Host(`gitlab.example.com`)"
      - "traefik.http.routers.container-registry.entrypoints=container-registry"
      - "traefik.http.routers.container-registry.service=container-registry"
      - "traefik.http.services.container-registry.loadbalancer.server.port=5000"
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url '<https://gitlab.example.com>'

        letsencrypt['enable'] = false

        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = {
          "X-Forwarded-Proto" => "https",
          "X-Forwarded-Ssl" => "on"
        }

        gitlab_rails['gitlab_shell_ssh_port'] = 2424

        registry_external_url '<https://gitlab.example.com:5050>'
        registry_nginx['enable'] = false
    ports:
      - '2424:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

This setup will:

  • set the registry external url
  • disable the nginx for the registry
  • instruct Traefik to route all traffic from port 5050 to port 5000 of the container

Author

Robert Juzak , B.Sc.

Characteristics

released:

July 16, 2025

categories:

What moves us, Infrastructure

Tags:

DevOps
previous article
For developers, for store operators - We don't work with arbitrary frameworks, libraries, plugins and programming languages. We love it professionally. Software created at Helm & Walter should be powerful and lean. Here are our favorites: PHP (recursive acronym for PHP: hypertext preprocessor) is a widely used programming language for ...
June 27, 2017
Bernd Helm