GitLab CE Self-Hosted Installation
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 respondright after install or reconfigure: Unicorn is still starting. Wait a minute; if it persists,sudo gitlab-ctl tail unicornandfree -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 rerungitlab-ctl reconfigure. git cloneover SSH asks for a password: your key isn't under User Settings → SSH Keys, or sshd'sAllowUsersexcludes thegituser.Permission denied (publickey)with the key added, on CentOS: SELinux denying sshd access to/var/opt/gitlab/.ssh/authorized_keys.gitlab-ctl reconfiguresets the context, but checkausearch -m avc -ts recentif something changed later.- No email and
Net::SMTPAuthenticationErrorin the Rails console: the provider wants an app-specific password, or TLS on port 465. Setsmtp_tls = trueandsmtp_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.