KB
SSL & Certificates

Installing a Sectigo SSL Certificate on Self-Hosted GitLab

3 min read659 words7 code blocks

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 .crt files)
  • [ ] 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.

bash
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:

bash
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.

bash
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.key
Permissions matter: the certificate is 644 (world-readable), the private key is 600 (owner-only). nginx runs as root and will read both.

Step 3 — Configure gitlab.rb#

Edit /etc/gitlab/gitlab.rb:

ruby
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'] = false
Setting external_url to https:// is what actually enables SSL. The explicit ssl_certificate lines are optional if your files follow the <hostname>.crt / <hostname>.key naming convention — GitLab finds them automatically — but stating them avoids surprises.

Step 4 — Apply & Restart#

bash
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart nginx

Step 5 — Verify#

bash
openssl s_client -connect git.example.com:443 -servername git.example.com

Look 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):

bash
openssl x509 -noout -modulus -in git.example.com.crt | openssl md5
openssl rsa  -noout -modulus -in git.example.com.key | openssl md5

Troubleshooting#

SymptomLikely CauseFix
unable to get local issuer certificateIntermediate bundle missing or out of orderRebuild the chain in Step 1, leaf → intermediate → root
nginx refuses to start after reconfigureKey and certificate don't matchRun the modulus check above
Browser shows "not secure" / connection refusedPort 443 blockedOpen 443 on the firewall
Certificate works but chain incomplete on mobile/older clientsMissing intermediatesEnsure 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:

  1. Generate a new CSR (or reuse the existing key if policy allows)
  2. Repeat Steps 1–4 with the new certificate files
  3. 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 ``