Troubleshooting GitLab SSH Clone Failures: Docker, Cloudflare, and Port Conflicts
Deploying GitLab using Docker Compose is a popular choice for self-hosting, but it often leads to a frustrating experience when you try to git clone via SSH for the first time. You might see errors like Connection refused, Connection timed out, or the dreaded kex_exchange_identification: Connection closed by remote host.
Usually, this isn't a bug in GitLab. It’s a configuration mismatch between your Docker port mapping, your GitLab settings, and your Cloudflare proxy.
In this guide, we’ll break down exactly why this happens and how to fix it permanently.
1. The Port Conflict (Docker vs. Host)
By default, SSH runs on port 22. When you install Linux on a server, your host OS is already using port 22 for your remote terminal access.
If you try to map GitLab’s SSH to port 22 in your docker-compose.yml like this: ports: - "22:22", the container will likely fail to start because the port is already "bound" by the host.
The Fix: Port Remapping
We must map a different host port (e.g., 2222) to the container's port 22.
# docker-compose.yml snippet
services:
gitlab:
image: gitlab/gitlab-ce:latest
ports:
- "80:80"
- "443:443"
- "2222:22" # Mapping Host 2222 to Container 22
2. The Identity Crisis (Internal Config)
Even if you map the port to 2222, GitLab doesn't "know" that. When you browse your GitLab UI, the clone URL will still look like [email protected]:group/project.git.
If you try to run that command, your computer will try port 22 on your server, which is your Host SSH, not your GitLab SSH.
The Fix: Update Omnibus Configuration
You need to tell GitLab to include the port in its generated metadata. Update your environment section in docker-compose.yml:
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.example.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
Now, GitLab will generate clone URLs like: ssh://[email protected]:2222/group/project.git.
3. The Cloudflare Wall (The "Orange Cloud" Trap)
This is where most users get stuck. If your domain is managed by Cloudflare and you have the Proxy (Orange Cloud) enabled, your SSH connection will fail.
Why?
Cloudflare’s standard proxy only understands HTTP (80) and HTTPS (443). When you try to send SSH traffic (a raw TCP protocol) through a Cloudflare-proxied hostname, Cloudflare doesn't know what to do with it and drops the connection.
The Solution: The "Grey Cloud" Subdomain
You want the security of Cloudflare for your web interface, but you need a direct path for SSH.
- Create a New DNS Record: In Cloudflare, add an A record called
ssh.gitlab.example.compointing to your server IP. - Turn Off Proxy: Ensure this specific record is set to DNS Only (Grey Cloud).
- Update GitLab Config: Tell GitLab to use this specific hostname for SSH specifically:
# Add this to your GITLAB_OMNIBUS_CONFIG
gitlab_rails['gitlab_ssh_host'] = 'ssh.gitlab.example.com'
