DB-유

GitLab CE Self-Hosted Installation

· linux, gitlab, git, devops

Our team wanted private repos, merge requests, issues and CI without per-user pricing or the code sitting on someone else's servers, so I set up GitLab CE. The Omnibus package bundles nginx, PostgreSQL, Redis, Sidekiq and the Rails app into one RPM or DEB with one config file. The real work is sizing the machine and getting TLS and email right.

This is CentOS 7 with Omnibus, Let's Encrypt handled by GitLab itself, and SMTP for notifications. Ubuntu 18.04 differences are noted.

Before you start, plan for these. At least 4 GB RAM and 2 cores; 2 GB "works" only with swap and is unpleasant. A DNS name like gitlab.example.com pointing at the server, with ports 80 and 443 reachable if you want Let's Encrypt. And port 22 free for Git over SSH, or a plan to move GitLab's SSH elsewhere.

Dependencies and firewall

sudo yum -y install curl policycoreutils-python openssh-server postfix
sudo systemctl enable --now sshd postfix
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

Postfix is optional if you'll use an external SMTP server (see the email section), which I recommend. Mail from a random VPS mostly lands in spam.

On Ubuntu: apt-get install curl openssh-server ca-certificates and ufw allow http https OpenSSH.

Repository and package

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.example.com" yum -y install gitlab-ce

On Ubuntu, use script.deb.sh and apt-get install gitlab-ce. EXTERNAL_URL seeds /etc/gitlab/gitlab.rb, and since it starts with https:// the package tries Let's Encrypt on the first reconfigure. It takes a few minutes and ends with the GitLab ASCII logo and a message to visit the URL.

gitlab.rb

Everything lives in /etc/gitlab/gitlab.rb. These are the settings I change on every install:

external_url 'https://gitlab.example.com'

letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['admin@example.com']
letsencrypt['auto_renew'] = true

gitlab_rails['time_zone'] = 'Asia/Seoul'
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'

# Backups
gitlab_rails['backup_path'] = '/var/opt/gitlab/backups'
gitlab_rails['backup_keep_time'] = 604800

# Trim memory use on a small box
unicorn['worker_processes'] = 3
sidekiq['concurrency'] = 15
postgresql['shared_buffers'] = "256MB"
prometheus_monitoring['enable'] = false

Turning off the bundled Prometheus saves a few hundred megabytes on a small server; turn it back on if you want the built-in metrics. Apply changes with:

sudo gitlab-ctl reconfigure

Every edit to gitlab.rb needs a reconfigure. It rewrites the component configs and restarts whatever changed. I've edited the file, wondered why nothing happened, and remembered this more than once.

Outbound email

Notifications, password resets and CI results all go through SMTP. With a transactional provider or the company mail server:

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.example.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "gitlab@example.com"
gitlab_rails['smtp_password'] = "smtp-password"
gitlab_rails['smtp_domain'] = "example.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true

Reconfigure, then test from the Rails console:

sudo gitlab-rails console -e production
Notify.test_email('you@example.com', 'GitLab test', 'It works').deliver_now

First login and locking down

Browse to the URL; the first screen asks for the root password. Then go to Admin Area → Settings → General → Sign-up restrictions and disable public sign-up unless you want it. Consider requiring two-factor auth for everyone under Sign-in restrictions.

Set up the backup cron job now, not later:

echo '0 2 * * * root /opt/gitlab/bin/gitlab-rake gitlab:backup:create CRON=1' | sudo tee /etc/cron.d/gitlab-backup

The backup archive leaves out /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json. Copy those two files off the machine yourself, because without the secrets file a restored database can't decrypt stored CI variables and tokens.

Is everything up?

sudo gitlab-ctl status
sudo gitlab-rake gitlab:check SANITIZE=true

Every service should show run:. The check task walks through Git, database, Sidekiq and mail config and prints green ticks. Create a test project and clone it both ways:

git clone https://gitlab.example.com/root/test.git
git clone git@gitlab.example.com:root/test.git

Gotchas

  • 502 Whoops, GitLab is taking too much time to respond right after install or reconfigure: Unicorn is still starting. Wait a minute; if it persists, sudo gitlab-ctl tail unicorn and free -h. On 2 GB hosts it's almost always memory.
  • Let's Encrypt fails with letsencrypt_certificate[gitlab.example.com] ... acme_certificate ... Failed authorization procedure: DNS doesn't point here, or port 80 is blocked. Fix it and rerun gitlab-ctl reconfigure.
  • git clone over SSH asks for a password: your key isn't under User Settings → SSH Keys, or sshd's AllowUsers excludes the git user.
  • Permission denied (publickey) with the key added, on CentOS: SELinux denying sshd access to /var/opt/gitlab/.ssh/authorized_keys. gitlab-ctl reconfigure sets the context, but check ausearch -m avc -ts recent if something changed later.
  • No email and Net::SMTPAuthenticationError in the Rails console: the provider wants an app-specific password, or TLS on port 465. Set smtp_tls = true and smtp_port = 465.

Upkeep is yum update gitlab-ce about monthly (it runs its own migrations) and confirming the nightly backup actually produced a tarball. I check that one more often than the updates.