Installing a Sectigo SSL Certificate on Self-Hosted GitLab
At a glance#
- Purpose: Install a commercial Sectigo SSL certificate on a self-hosted GitLab instance.
- Applies to: GitLab Omnibus on Linux.
- Risk: Medium - requires gitlab-ctl reconfigure, which restarts services.
- Time: About 45 minutes.
Scope: GitLab Omnibus package (bundled nginx), configured via /etc/gitlab/gitlab.rb. If GitLab sits behind a separate reverse proxy, the certificate belongs on that proxy instead — see the note at the bottom.
Example domain used throughout: git.example.com
Prerequisites#
Before you start, make sure you have:
- [ ] Your domain certificate from Sectigo (
git_example_com.crt) - [ ] The intermediate CA bundle (
git_example_com.ca-bundle, or separate intermediate.crtfiles) - [ ] The private key generated on your server when you created the CSR (
git.example.com.key) - [ ]
sudo/ root access to the GitLab server - [ ] Port 443 open on your firewall
The private key never comes from Sectigo. It was created locally when you ran openssl req. If you can't find it, you'll need to reissue the certificate with a new CSR.
Step 1 — Build the Full Certificate Chain#
GitLab's nginx needs your certificate followed by the intermediates, in a single file, ordered leaf → intermediate → root.
cat git_example_com.crt git_example_com.ca-bundle > git.example.com.crt<details> <summary>If Sectigo sent separate intermediate files instead of a bundle</summary>
Order matters — your certificate first, then intermediates from most-specific down to root:
cat git_example_com.crt \
SectigoRSADomainValidationSecureServerCA.crt \
USERTrustRSAAAACA.crt \
AAACertificateServices.crt \
> git.example.com.crt</details>
Step 2 — Place Files & Set Permissions#
GitLab expects certificates in /etc/gitlab/ssl/, named after the hostname.
sudo mkdir -p /etc/gitlab/ssl
sudo chmod 755 /etc/gitlab/ssl
sudo cp git.example.com.crt /etc/gitlab/ssl/
sudo cp git.example.com.key /etc/gitlab/ssl/
sudo chmod 644 /etc/gitlab/ssl/git.example.com.crt
sudo chmod 600 /etc/gitlab/ssl/git.example.com.keyPermissions matter: the certificate is644(world-readable), the private key is600(owner-only). nginx runs as root and will read both.
Step 3 — Configure gitlab.rb#
Edit /etc/gitlab/gitlab.rb:
external_url "https://git.example.com"
nginx['redirect_http_to_https'] = true
nginx['ssl_certificate'] = "/etc/gitlab/ssl/git.example.com.crt"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/git.example.com.key"
# Disable Let's Encrypt so GitLab doesn't override your cert
letsencrypt['enable'] = falseSettingexternal_urltohttps://is what actually enables SSL. The explicitssl_certificatelines are optional if your files follow the<hostname>.crt/<hostname>.keynaming convention — GitLab finds them automatically — but stating them avoids surprises.
Step 4 — Apply & Restart#
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart nginxStep 5 — Verify#
openssl s_client -connect git.example.com:443 -servername git.example.comLook for Verify return code: 0 (ok).
Pre-Flight Checks#
Run these before reconfiguring to avoid a failed nginx start.
Confirm the key and certificate match (the two hashes must be identical):
openssl x509 -noout -modulus -in git.example.com.crt | openssl md5
openssl rsa -noout -modulus -in git.example.com.key | openssl md5Troubleshooting#
| Symptom | Likely Cause | Fix |
|---|---|---|
unable to get local issuer certificate | Intermediate bundle missing or out of order | Rebuild the chain in Step 1, leaf → intermediate → root |
| nginx refuses to start after reconfigure | Key and certificate don't match | Run the modulus check above |
| Browser shows "not secure" / connection refused | Port 443 blocked | Open 443 on the firewall |
| Certificate works but chain incomplete on mobile/older clients | Missing intermediates | Ensure the full .ca-bundle is concatenated, not just the leaf cert |
Note: Reverse Proxy Setups#
If GitLab runs behind a separate reverse proxy (external nginx, Apache, Traefik, or Docker), the certificate goes on the proxy, not GitLab's bundled nginx — and GitLab's internal nginx is usually disabled instead. The chain-building steps above still apply; only the placement and config location change.
Renewal Reminder#
Sectigo certificates are typically issued for 1 year. When renewing:
- Generate a new CSR (or reuse the existing key if policy allows)
- Repeat Steps 1–4 with the new certificate files
- Restart nginx
Tip: Set a calendar reminder ~2 weeks before expiry. Check the current expiry date anytime with: ``bash openssl x509 -enddate -noout -in /etc/gitlab/ssl/git.example.com.crt ``