KB
DNS

GitLab Pages: DNS Configuration for Single-Domain Sites

4 min read790 words16 code blocks

At a glance#

  • Purpose: Configure GitLab Pages to serve sites from a single domain with namespace-in-path URLs, including TLS.
  • Applies to: Self-hosted GitLab (Omnibus) 16.7 and later.
  • Risk: Medium — gitlab-ctl reconfigure restarts services; existing Pages URLs change format.
  • Time: About 45 minutes plus DNS propagation.

Overview#

GitLab Pages can serve project sites in one of two mutually exclusive modes:

ModeURL formatDNS requirement
Wildcard (default)namespace.pages.example.com/projectWildcard DNS record and wildcard TLS certificate
Single-domainpages.example.com/namespace/projectOne A record and one standard certificate

Single-domain mode is the simpler option when you cannot obtain a wildcard certificate, or when the DNS provider does not support wildcard records.

Warning: These modes cannot run side by side. Enabling namespace_in_path disables wildcard-style access, and every existing Pages URL changes format. Anything linking to the old URLs — documentation, bookmarks, CI jobs — will break. Communicate the change before making it.

Before you start#

  • Root access to the GitLab server.
  • Ability to create DNS records for the domain.
  • A TLS certificate for the Pages domain, if serving over HTTPS.
  • A maintenance window — gitlab-ctl reconfigure restarts GitLab services.

Back up the configuration first:

bash
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-$(date +%F)

Procedure#

1. Create the DNS A record#

At your DNS provider, add an A record pointing the Pages domain at the GitLab Pages instance:

text
pages.example.com.    1800    IN    A    192.0.2.10

Replace pages.example.com with your domain and 192.0.2.10 with the actual IP address of the Pages instance. If Pages runs on the same host as GitLab, this is the GitLab server's IP.

Confirm it resolves before continuing:

bash
dig +short pages.example.com

2. Enable namespace-in-path#

bash
sudo nano /etc/gitlab/gitlab.rb
ruby
gitlab_pages['namespace_in_path'] = true

3. Install the TLS certificate#

Place the certificate and key in /etc/gitlab/ssl/. GitLab looks for filenames matching the Pages domain by default:

bash
sudo mkdir -p /etc/gitlab/ssl
sudo cp pages.example.com.crt /etc/gitlab/ssl/
sudo cp pages.example.com.key /etc/gitlab/ssl/
sudo chmod 600 /etc/gitlab/ssl/pages.example.com.key
sudo chmod 644 /etc/gitlab/ssl/pages.example.com.crt

The .crt file must contain the full chain — your certificate followed by any intermediates. See Installing a Sectigo SSL Certificate on Self-Hosted GitLab.

4. Configure Pages with TLS#

In /etc/gitlab/gitlab.rb:

ruby
external_url "https://gitlab.example.com"
pages_external_url "https://pages.example.com"

pages_nginx['redirect_http_to_https'] = true
gitlab_pages['namespace_in_path'] = true
Warning: pages_external_url must not be a subdomain of external_url. If GitLab is at gitlab.example.com, Pages cannot be at pages.gitlab.example.com. Use a sibling domain such as pages.example.com. This is a hard requirement — sharing a parent domain allows Pages content to set cookies for the GitLab instance, which is a security problem.

5. Point at custom certificate filenames (optional)#

Only needed if the files are not named after the domain:

ruby
pages_nginx['ssl_certificate']     = "/etc/gitlab/ssl/pages-nginx.crt"
pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/pages-nginx.key"

6. Update OAuth settings if access control is enabled#

Only applies if GitLab Pages access control is in use.

  1. In the GitLab admin area, update the OAuth redirect URI to use HTTPS:
text
https://pages.example.com/projects/auth
  1. Remove the stale gitlab_pages block so the secret is regenerated:
bash
sudo cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak
sudo nano /etc/gitlab/gitlab-secrets.json

Delete the gitlab_pages section, then save.

Note: Back up gitlab-secrets.json before editing. It holds encryption keys for the whole instance — a mistake here affects far more than Pages.

7. Apply the configuration#

bash
sudo gitlab-ctl reconfigure

This takes several minutes and restarts services. Watch for errors in the output.

Verification#

Confirm services are healthy:

bash
sudo gitlab-ctl status

Check that Pages is listening:

bash
sudo ss -tulpn | grep pages

Test the certificate:

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

Look for Verify return code: 0 (ok).

Then load an actual Pages site in a browser. The URL format is now:

text
https://pages.example.com/<namespace>/<project_slug>

Confirm HTTP redirects to HTTPS:

bash
curl -I http://pages.example.com

This should return 301 with an https:// Location header.

Troubleshooting#

SymptomCause and fix
404 on every Pages sitenamespace_in_path set but sites not yet redeployed. Re-run the Pages CI job for the project.
pages_external_url cannot be a subdomain of external_urlExactly what it says — change the Pages domain to a sibling rather than a child.
Certificate errors in the browserChain incomplete, or filenames do not match the domain. Set the paths explicitly as in step 5.
OAuth login loopsRedirect URI still on HTTP, or the stale secret was not removed. Repeat step 6.
gitlab-ctl reconfigure failsRuby syntax error in gitlab.rb. The output names the line. Restore the backup if needed.
Old wildcard URLs stopped workingExpected — the modes are mutually exclusive. Update any links.