Почитал несколько "мануалов" в которых как обычно ничего не объясняется, а просто написано сделайте так и будет все работать. Также вижу misconfiguration в таких статьях, но авторы утверждают, что все работает. Потому наверное в каких-то версиях работают их варианты. Версия gitlab на момент написания 13.4.3.

ЧТО К ЧЕМУ

Разбираться что такое реестр контейнеров в рамках этой статьи не буду, но надо понимать как это работает внутри gitlab. Гугля на данную тему наткнулся на следующую ссылку, где расписана вся архитектура и как оно работает. Для тех кто не знает по ингличански разжевываю. По официальной документации Container Registry можно настроить на том же домене на котором висит git. От части это правда, но если у вас пользователи получают доступ к git через интернет (reverse proxy), то необходимо заводить для него отдельный домен, что будет более правильным решением т.е. позволит избежать конфликта доменных имен в настройке nginx. Собственно у меня так и есть потому сразу завел домен аля registry.git.example.com, выписал ssl сертификат, который синхронизируется с reverse proxy на сервер git по пути /etc/letsencrypt. Итого как это должно работать: пользователь поднимает где-нить на внешке runner, который сливает образ по пути registry.git.example.com/project/repository, который попадает в reverse proxy, который проксирует на nginx gitlab omnibus, который проксирует на localhost:5000. Собственно последнее звено и есть Container Registry. Теперь когда все стало ясно можно приступать к настройке.

НАСТРОЙКА GITLAB

Сперва настраиваем gitlab, чтобы он знал где висит реестр контейнеров:

###############################
# Container registry settings #
###############################
# see http://docs.gitlab.com/ce/administration/container_registry.html
#

# Settings used by GitLab application
gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "localhost"
gitlab_rails['registry_port'] = "5000"
gitlab_rails['registry_api_url'] = "http://localhost:5000"
gitlab_rails['registry_key_path'] = "/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key"
gitlab_rails['registry_path'] = "/opt/gitlab/registry"
gitlab_rails['registry_issuer'] = "git.example.com"
gitlab_rails['gitlab_default_projects_features_container_registry'] = false

Здесь registry_issuer должен совпадать с значением issuer указанными в файле /var/opt/gitlab/registry/config.yml (обычно это просто git.example.com т.е. доменный адрес гита).

Теперь настраиваем Container Registry:

# Settings used by Registry application
registry['enable'] = true
registry['username'] = "registry"
registry['group'] = "registry"
registry['dir'] = "/opt/gitlab/registry"
registry['storage_delete_enabled'] = true
registry['registry_http_addr'] = "localhost:5000"
registry['log_directory'] = "/var/log/gitlab/registry"
registry['log_level'] = "info"
registry['rootcertbundle'] = "/var/opt/gitlab/registry/gitlab-registry.crt"

Чтобы было понятнее. Сперва мы сказали гиту что хост на котором работает реестр это localhost, а порт 5000. Далее указали, что доступ к api можно получить по ссылке http://localhost:5000. Далее сказали реестру, что он должен слушать localhost:5000 по протоколу http (registry_http_addr).

Теперь настраиваем nginx:

registry_external_url 'https://registry.git.example.com'
registry_nginx['ssl_certificate'] = "/etc/letsencrypt/live/git.example.com/fullchain.pem"
registry_nginx['ssl_certificate_key'] = "/etc/letsencrypt/live/git.example.com/privkey.pem"

НАСТРОЙКА REVERSE PROXY

Тут нет ничего хитрого. Просто редирект с http на https на всякий пожарный и обычное проксирование до gitlab:

server {
        listen 80;
        server_name registry.git.example.com;
        server_tokens off;
        rewrite ^(.*) https://$host$1;
}

server {
        listen 443 ssl http2;
        server_name registry.git.example.com;
        server_tokens off;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_certificate /usr/local/etc/letsencrypt/live/git.example.com/fullchain.pem;
        ssl_certificate_key /usr/local/etc/letsencrypt/live/git.example.com/privkey.pem;
        add_header Strict-Transport-Security max-age=2592000;
        access_log logs/git_access.log main buffer=16k;
        error_log  logs/git_error.log;
        location / {
                add_header  X-Robots-Tag "noindex, nofollow, nosnippet, noarchive";
                proxy_pass https://10.14.19.11;
                proxy_request_buffering off;
                proxy_max_temp_file_size 0;
                proxy_redirect off;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-Protocol $scheme;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Url-Scheme $scheme;
                client_max_body_size 10G;
                client_body_buffer_size 128k;
                proxy_connect_timeout 1800;
                proxy_send_timeout 1800;
                proxy_read_timeout 1800;
                proxy_buffer_size 4k;
                proxy_buffers 4 32k;
                proxy_busy_buffers_size 64k;
                proxy_temp_file_write_size 64k;
        }
        location ^~ /.well-known {
                alias /var/www/html/.well-known;
                charset off;
                add_header Content-Type text/plain;
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
                return 418;
        }
}

Если у вас возникнет вопрос "а работает ли это с Pages с регуляркой server_name ~^(?.*).git.example.com$", то ответ "да, это работает".