How to create and install a complete SSL certificate chain in GitLab
Description
GitLab requires a complete certificate chain to establish trust between the server and clients. You might need to build a full chain certificate under the following situations:
- You are setting up a new GitLab instance and want to add an SSL certificate
- You need to rotate an expiring SSL certificate in an existing GitLab instance
If using SSL certificates from untrusted or unrecognized certificate authorities (CAs) you might see:
- Security warnings in web browsers when accessing the GitLab user interface.
- Certificate-related errors during Git operations over HTTPS.
- Common SSL errors listed in our documentation.
Environment
-
Impacted offerings:
- GitLab Self-Managed
Prerequisites
SSL certificate files from your Certificate Authority (CA):
- Domain certificate (
domain.crt
) - Private key (
domain.key
) - Intermediate certificate(s) (
intermediate.crt
) - Root certificate (
root.crt
)
Some CAs may combine the intermediate/root certificates into a single file like chain.crt
or similar.
All certificates must be in PEM format. PEM-encoded certificates can use '.crt' or '.pem' file extensions. However, the file content, not the extension, determines the format. To verify format:
- Open the certificate file in a text editor.
- Confirm it starts with
-----BEGIN CERTIFICATE-----
.
Resolution
-
Combine the certificates into one file in the following order:
- Domain certificate (your site's certificate)
- Intermediate certificate(s) (if you have them - there might be multiple)
- Root certificate
Note: GitLab will automatically use your certificate without additional configuration if the filename matches the
external_url
set in/etc/gitlab/gitlab.rb
. For example, if yourexternal_url
is'https://gitlab.example.com'
, name your certificategitlab.example.com.crt
. To use a different naming convention, review the configure HTTPS manually docs.# If your certificates are all in separate files cat domain.crt intermediate.crt root.crt > gitlab.example.com.crt # If your CA has provided you with a chain already cat domain.crt chain.crt > gitlab.example.com.crt
The resulting
gitlab.example.com.crt
file should look like this:-----BEGIN CERTIFICATE----- Your Domain certificate (or main SSL certificate): domain.crt -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- Your Intermediate certificate: intermediate.crt -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- Your Root certificate: root.crt -----END CERTIFICATE-----
-
Verify the full chain certificate:
-
To view the certificate chain from top to bottom, run:
openssl crl2pkcs7 -nocrl -certfile gitlab.example.com.crt | openssl pkcs7 -print_certs -noout | grep -E "subject=|issuer="
-
Confirm the chain output
- The top certificate
subject
should match the domain name of the GitLab instance (gitlab.example.com
) - The
issuer
should match the next certificate'ssubject
(DigiCert Intermediate CA
). Confirm this for each certificate in the chain - The chain ends with the root certificate, where subject and issuer are identical (
DigiCert Root CA Inc
)
# Example output # Domain Certificate (Your GitLab instance certificate) subject=CN = gitlab.example.com issuer=CN = DigiCert Intermediate CA # Intermediate Certificate subject=CN = DigiCert Intermediate CA issuer=CN = DigiCert Root CA Inc # Root Certificate subject=CN = DigiCert Root CA Inc issuer=CN = DigiCert Root CA Inc
- The top certificate
-
-
Copy the certificate chain and private key to
/etc/gitlab/ssl
:sudo cp gitlab.example.com.crt /etc/gitlab/ssl/ sudo cp domain.key /etc/gitlab/ssl/gitlab.example.com.key
-
Set the correct permissions:
sudo chmod 600 /etc/gitlab/ssl/gitlab.example.com.* sudo chown root:root /etc/gitlab/ssl/gitlab.example.com.*
-
Reconfigure and restart NGINX:
sudo gitlab-ctl reconfigure sudo gitlab-ctl restart nginx # If you are updating an existing SSL certificate, you can reload NGINX instead sudo gitlab-ctl hup nginx
-
Access your GitLab instance via your browser to confirm there are no certificate warnings.