Install gitlab-ce (community edition) in docker container with HTTPS and docker registry

This article is a howto install of the official docker gitlab-ce (GitLab Community Edition). GitLab maintains a docker image in the Docker registry and this is the best way to install GitLab.
In this article you are going to learn how:

  • to install the GitLab CE in docker
  • to enable HTTPS (SSL) web support to your GitLab
  • to enable the docker registry functionality of GitLab

To install GitLab docker image in your Linux distribution all you need is a working docker environment and started docker daemon. As you know, installing software with docker will allow you to keep your main system clean and let you use a fined tuned installation from the official developer (creator). As mentioned already, the GitLab maintains an official GitLab image in the Docker Registry so you may expect everything to work smoothly and better than if you make an installation in a clean Linux distribution like CentOS, Ubuntu and so on. In this article, we will include the most important docker commands to control and configure the GitLab docker container and even if you are not familiar with the Docker software they are simple enough to use them and prefer this method over GitLab normal installation.

GitLab has integrated the Docker Container Registry in GitLab Container Registry and now with GitLab you can have a local Docker registry containing all project’s docker images!

Just to note, the Docker Registry is the place for the Docker (aka Linux) images.
Using GitLab Container Registry with CI/CD (continuous integration and continuous delivery) you can create automatically test, staging, development and production docker images.

STEP 1) Install and run docker.

Install and run docker service for your Linux distribution.

STEP 2) Download and run the GitLab Docker image

It’s a simple command:

sudo docker run --detach \
  --hostname gitlab.mydomain.com \
  --publish 443:443 --publish 80:80 --publish 1022:22 --publish 4567:4567 \
  --name gitlab \
  --restart always \
  --volume /srv/gitlab/config:/etc/gitlab \
  --volume /srv/gitlab/logs:/var/log/gitlab \
  --volume /srv/gitlab/data:/var/opt/gitlab \
  gitlab/gitlab-ce:latest

The docker will try to find the gitlab-ce latest version (marked with tag latest is always the latest stable GitLab release). First, it will download the official Docker GitLab image and then will run it in a new container with name “gitlab”.

  • –hostname gitlab.mydomain.com – sets the hostname of the container. The hostname should point to the server hosting the docker container.
  • –publish 443:443 –publish 80:80 –publish 1022:22 — publish 4567:4567 – Maps the host server ports to the docker container. So when someone access the port 80 (plain web), 443 (ssl web), 22 (ssh, used to access the git, 22 is used for the host openssh server, so we map another unused port as 1022), 4567 (docker registry) using the IP of the host server he will be redirected to the services in the docker, which listen on these posts. These ports are really difficult to change (add or remove!), so it is important to include them at frist start of this command!
  • –name gitlab – the name of the docker. Each docker container has ID and name
  • –volume /srv/gitlab/config:/etc/gitlab –volume /srv/gitlab/logs:/var/log/gitlab –volume /srv/gitlab/data:/var/opt/gitlab – Maps directories from the host to the docker root directory. If you remove the docker these directories will not be deleted because they are not part of the docker “root file system”.
  • gitlab/gitlab-ce:latest – the docker image and the tag (aka version) in the official Docker Registry, which you want to download and run as a container in your host. Your docker will contact the offcial Docker Registry to download the image if it exists. “run” command will make a container based on this image and start the container. Here it is the offcial GitLab-ce Docker image – https://hub.docker.com/r/gitlab/gitlab-ce. Always use ONLY offcial images due to security reasons! All versions you could install are here: https://hub.docker.com/r/gitlab/gitlab-ce/tags – just replace the word “latest” from the above command with the version you like from the URL. Here we use latest and at present it points to “12.4.2-ce.0”, so all configurations and install are valid for this version (and probably for future versions, but there might be changes).
myuser@my-server-pc:~# sudo docker run --detach \
>   --hostname gitlab.mydomain.com \
>   --publish 443:443 --publish 80:80 --publish 1022:22 --publish 4567:4567 \
>   --name gitlab \
>   --restart always \
>   --volume /srv/gitlab/config:/etc/gitlab \
>   --volume /srv/gitlab/logs:/var/log/gitlab \
>   --volume /srv/gitlab/data:/var/opt/gitlab \
>   gitlab/gitlab-ce:latest
Unable to find image 'gitlab/gitlab-ce:latest' locally
latest: Pulling from gitlab/gitlab-ce
e80174c8b43b: Pull complete 
d1072db285cc: Pull complete 
858453671e67: Pull complete 
3d07b1124f98: Pull complete 
1abbbf4783f5: Pull complete 
38a43d00563b: Pull complete 
8bbea5a60f40: Pull complete 
176bd574f7c7: Pull complete 
a8646c9c80ee: Pull complete 
089fe821c806: Pull complete 
Digest: sha256:88f1bcc39aa9917ac4b19022af441b64265d50e1f0c0fa2616d29a2cb82fb41a
Status: Downloaded newer image for gitlab/gitlab-ce:latest
5d025e7f93a45a50dbbaa87c55d7cdbbf6515bbe1d45ff599074f1cdcf320a0c

There is no locally copy of the needed image so it is downloaded and then a container is created with the ID=5d025e7f93a45a50dbbaa87c55d7cdbbf6515bbe1d45ff599074f1cdcf320a0c. If no error is shown in the command line output the container is successfully running. Check it with command:

myuser@my-server-pc:~# sudo docker ps
CONTAINER ID        IMAGE                     COMMAND             CREATED             STATUS                   PORTS                                                                                    NAMES
5d025e7f93a4        gitlab/gitlab-ce:latest   "/assets/wrapper"   5 minutes ago       Up 5 minutes (healthy)   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:4567->4567/tcp, 0.0.0.0:1022->22/tcp   gitlab

And may take a while before the GitLab answer to querries. You can monitor the progress with

myuser@my-server-pc:~# sudo docker logs -f gitlab
.....
.....
==> /var/log/gitlab/gitlab-rails/production.log <==
Started GET "/-/metrics" for 127.0.0.1 at 2019-11-11 00:18:11 +0000
Processing by MetricsController#index as HTML
Completed 200 OK in 21ms (Views: 1.2ms | ActiveRecord: 0.0ms | Elasticsearch: 0.0ms)

==> /var/log/gitlab/gitlab-rails/sidekiq_exporter.log <==
[2019-11-11T00:18:12.367+0000] 127.0.0.1 - - [11/Nov/2019:00:18:12 UTC] "GET /metrics HTTP/1.1" 200 11108 "-" "Prometheus/2.12.0"

STEP 3) Login as Administrator.

On the first load of http://gitlab.mydomain.com/ (OR use the IP of the host server), the GitLab web interface will offer you to change the administrator’s password. Change the password and log in with username “root” and the new password you’ve just set. The administrator’s name is “root”.
Because in many cases the gitlab.mydomain.com (or the domain you set) is not a valid domain, the SSL may not be working, open the web without the SSL – HTTP. The next step is going to install a manual singed certificate.

SCREENSHOT 1) Change the administrator’s password on first login. The default GitLab administrator’s username is “root”.

Write your new password and click on “Change your password”.

main menu
First Login – Change password

SCREENSHOT 2) Login as an administrator (use “root” for administrator’s name).

main menu
administrator login – username root

SCREENSHOT 3) This is the GitLab Welcome page.

Main activies are “Create a project”, “Create a group”, “Add people” and “Configure Gitlab”.

main menu
GitLab Welcome page – root

STEP 3) Install SSL certificate to enable HTTPS.

It is absolutely mandatory to enable and use only SSL connections to your repository. Gitlab is fully integrated with Let’s Encrypt SSL certificates, but because it is easier and in most cases, you cannot use it in an internal (closed) network or test machines and so on we preferred to show how to install manual certificate here. To install manually a certificate edit the configuration file “/etc/gitlab/gitlab.rb” with

docker exec -it gitlab editor /etc/gitlab/gitlab.rb

which will open a text editor (nano) to edit the file. Set the domain you want to use for the GitLab installation:

.....
external_url "https://gitlab.mydomain.com"
.....
letsencrypt['enable'] = false
.....
nginx['redirect_http_to_https'] = true
nginx['redirect_http_to_https_port'] = 80
.....
nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.mydomain.com.crt"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.mydomain.com.key"

Note “external_url” expect the
The line probably is commented so remove the leading “#” and set your domain. Save the configuration.
Put the private key and the certificate in /etc/gitlab/ssl/ or generate them manually with:

myuser@my-server-pc:~# sudo docker exec -it gitlab /bin/bash
root@gitlab:/# . /etc/profile
root@gitlab:/# mkdir /etc/gitlab/ssl/
root@gitlab:/# cd /etc/gitlab/ssl/
root@gitlab:/etc/gitlab/ssl# openssl genrsa -out gitlab.mydomain.com.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
........................................................................................................................................................................+++++
...............................+++++
e is 65537 (0x010001)
root@gitlab:/etc/gitlab/ssl# openssl req -new -key ./gitlab.mydomain.com.key -out ./gitlab.mydomain.com.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New York
Locality Name (eg, city) []:New York
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Internet
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:gitlab.mydomain.com
Email Address []:admin@gitlab.mydomain.com    

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
root@gitlab:/etc/gitlab/ssl# openssl x509 -req -days 365 -in ./gitlab.mydomain.com.csr -signkey ./gitlab.mydomain.com.key -out ./gitlab.mydomain.com.crt
Signature ok
subject=C = US, ST = New York, L = New York, O = Internet, CN = gitlab.mydomain.com.key, emailAddress = admin@gitlab.mydomain.com
Getting Private key
root@gitlab:/etc/gitlab/ssl# chmod 400 gitlab.mydomain.com.*
root@gitlab:/etc/gitlab/ssl# ls -al
total 20
drwxr-xr-x 2 root root 4096 Nov 11 00:36 .
drwxrwxr-x 4 root root 4096 Nov 11 00:33 ..
-r-------- 1 root root 1330 Nov 11 00:36 gitlab.mydomain.com.crt
-r-------- 1 root root 1062 Nov 11 00:35 gitlab.mydomain.com.csr
-r-------- 1 root root 1679 Nov 11 00:34 gitlab.mydomain.com.key
exit

Reconfigure the GitLab instance with

myuser@my-server-pc:~# sudo docker exec -it gitlab gitlab-ctl reconfigure

After a while you’ll see some screens of information what is doing the reconfiguration:

root@my-server-pc:~# sudo docker exec -it gitlab gitlab-ctl reconfigure
Starting Chef Client, version 14.13.11
resolving cookbooks for run list: ["gitlab"]
Synchronizing Cookbooks:
  - gitlab (0.0.1)
  - package (0.1.0)
  - postgresql (0.1.0)
  - redis (0.1.0)
  - monitoring (0.1.0)
  - registry (0.1.0)
  - mattermost (0.1.0)
  - consul (0.1.0)
  - nginx (0.1.0)
  - runit (4.3.0)
  - acme (4.0.0)
  - gitaly (0.1.0)
  - praefect (0.1.0)
  - crond (0.1.0)
  - letsencrypt (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
......
......
Recipe: monitoring::grafana
  * runit_service[grafana] action enable
    * ruby_block[restart_service] action nothing (skipped due to action :nothing)
    * ruby_block[restart_log_service] action nothing (skipped due to action :nothing)
    * ruby_block[reload_log_service] action nothing (skipped due to action :nothing)
    * directory[/opt/gitlab/sv/grafana] action create (up to date)
    * template[/opt/gitlab/sv/grafana/run] action create (up to date)
    * directory[/opt/gitlab/sv/grafana/log] action create (up to date)
    * directory[/opt/gitlab/sv/grafana/log/main] action create (up to date)
    * template[/opt/gitlab/sv/grafana/log/run] action create (up to date)
    * template[/var/log/gitlab/grafana/config] action create (up to date)
    * ruby_block[verify_chown_persisted_on_grafana] action nothing (skipped due to action :nothing)
    * directory[/opt/gitlab/sv/grafana/env] action create (up to date)
    * ruby_block[Delete unmanaged env files for grafana service] action run (skipped due to only_if)
    * template[/opt/gitlab/sv/grafana/check] action create (skipped due to only_if)
    * template[/opt/gitlab/sv/grafana/finish] action create (skipped due to only_if)
    * directory[/opt/gitlab/sv/grafana/control] action create (up to date)
    * link[/opt/gitlab/init/grafana] action create (up to date)
    * file[/opt/gitlab/sv/grafana/down] action delete (up to date)
    * directory[/opt/gitlab/service] action create (up to date)
    * link[/opt/gitlab/service/grafana] action create (up to date)
    * ruby_block[wait for grafana service socket] action run (skipped due to not_if)
     (up to date)
Recipe: <Dynamically Defined Resource>
  * service[unicorn] action restart
    - restart service service[unicorn]
  * service[sidekiq] action restart
    - restart service service[sidekiq]
Recipe: gitlab::gitlab-rails
  * execute[clear the gitlab-rails cache] action run^[[B
    - execute /opt/gitlab/bin/gitlab-rake cache:clear
Recipe: <Dynamically Defined Resource>
  * service[nginx] action restart
    - restart service service[nginx]
  * service[grafana] action restart
    - restart service service[grafana]

Running handlers:
Running handlers complete
Chef Client finished, 14/663 resources updated in 46 seconds
gitlab Reconfigured!

There are no errors and the reconfiguration is successful. Now you may use GitLab with HTTPS. If you try to load the page with HTTP you are going to be redirected to the external URL you set before https://gitlab.mydomain.com. You can use the GitLab interface by IP, too, just use the HTTPS to be sure no redirection will happen.

STEP 4*) Enable Container Registry in the GitLab docker installation.

If you do not need Registry Container (or do not know what is it) skip this step. The port for our Docker Conatiner Registry is 4567 (the default is 5000).
Edit the GitLab configuration with:

docker exec -it gitlab editor /etc/gitlab/gitlab.rb

And change the line:

.....
registry_external_url 'https://gitlab.mydomain.com:4567'
.....
registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.mydomain.com.crt"
registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.mydomain.com.key"

Add the last two lines if they are missing. Save the file and exit. Reconfigure the GtiLab instance with:

myuser@my-server-pc:~# sudo docker exec -it gitlab gitlab-ctl reconfigure

You should see no errors and last lines such as:

.....
.....
Recipe: <Dynamically Defined Resource>
  * service[unicorn] action restart
    - restart service service[unicorn]
  * service[sidekiq] action restart
    - restart service service[sidekiq]
Recipe: gitlab::gitlab-rails
  * execute[clear the gitlab-rails cache] action run
    - execute /opt/gitlab/bin/gitlab-rake cache:clear
Recipe: <Dynamically Defined Resource>
  * service[nginx] action restart
    - restart service service[nginx]
  * service[registry] action restart
    - restart service service[registry]

Running handlers:
Running handlers complete
Chef Client finished, 38/737 resources updated in 49 seconds
gitlab Reconfigured!

Login in the Container Registry to see if everything is working. Because we use self-signed certificate:

myuser@my-server-pc:~# sudo mkdir -p "/etc/docker/certs.d/gitlab.mydomain.com:4567"
myuser@my-server-pc:~# sudo cp /srv/gitlab/config/ssl/gitlab.mydomain.com.crt /etc/docker/certs.d/gitlab.mydomain.com\:4567/ca.crt
myuser@my-server-pc:~# docker login gitlab.mydomain.com:4567
Username: root        
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

Now you have your own Docker Registry you may use for your private images.
*Note: using self-signed certificate may require system-level trust, so in these cases, you should execute:
for Ubuntu:

update-ca-certificates

for Red Hat / CentOS:

update-ca-trust

Restart the Docker GitLab instance

myuser@my-server-pc:~# sudo docker restart gitlab
gitlab
myuser@my-server-pc:~# sudo docker ps
CONTAINER ID        IMAGE                     COMMAND             CREATED             STATUS                            PORTS                                                                                    NAMES
5d025e7f93a4        gitlab/gitlab-ce:latest   "/assets/wrapper"   2 hours ago         Up 4 seconds (health: starting)   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:4567->4567/tcp, 0.0.0.0:1022->22/tcp   gitlab

You may use the container name “gitlab” or the ID (short – 5d025e7f93a4 or the full – 5d025e7f93a45a50dbbaa87c55d7cdbbf6515bbe1d45ff599074f1cdcf320a0c)

OS Information

This release of GitLab (GitLab Community Edition 12.4.2) uses Ubuntu 16.04.6 LTS:

myuser@my-server-pc:~# sudo docker exec -it gitlab /bin/bash
root@gitlab:/# cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
root@gitlab:/#

Up and running GitLab-CE

Here is what you can expect to have in the Docker container with Ubuntu 18 LTS host.

root@my-server-pc:~# pstree
systemd─┬─accounts-daemon───2*[{accounts-daemon}]
        ├─agetty
        ├─atd
        ├─containerd─┬─containerd-shim─┬─dumb-init───gitlab-runner───16*[{gitlab-runner}]
        │            │                 └─10*[{containerd-shim}]
        │            ├─containerd-shim─┬─wrapper─┬─bundle─┬─6*[bundle───9*[{bundle}]]
        │            │                 │         │        └─2*[{bundle}]
        │            │                 │         ├─gitlab-ctl───omnibus-ctl───sh───xargs───tail
        │            │                 │         └─runsvdir─┬─runsv─┬─sshd
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─redis-server───2*[{redis-server}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─bundle───42*[{bundle}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─gitlab-unicorn-───sleep
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─gitlab-workhors───11*[{gitlab-workhors}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─gitaly-wrapper─┬─gitaly─┬─2*[ruby───39*[{ruby}]]
        │            │                 │                    │       │                │        └─16*[{gitaly}]
        │            │                 │                    │       │                └─8*[{gitaly-wrapper}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─postgres───18*[postgres]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─postgres_export───7*[{postgres_export}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─registry───10*[{registry}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─nginx───5*[nginx]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─gitlab-exporter───5*[{gitlab-exporter}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─gitlab-logrotat───sleep
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─grafana-server───15*[{grafana-server}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─alertmanager───12*[{alertmanager}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    ├─runsv─┬─prometheus───13*[{prometheus}]
        │            │                 │                    │       └─svlogd
        │            │                 │                    └─runsv─┬─redis_exporter───9*[{redis_exporter}]
        │            │                 │                            └─svlogd
        │            │                 └─10*[{containerd-shim}]
        │            └─18*[{containerd}]
        ├─cron
        ├─dbus-daemon
        ├─dockerd─┬─docker-proxy───6*[{docker-proxy}]
        │         ├─docker-proxy───7*[{docker-proxy}]
        │         ├─2*[docker-proxy───5*[{docker-proxy}]]
        │         └─26*[{dockerd}]
        ├─irqbalance───{irqbalance}
        ├─lvmetad
        ├─lxcfs───3*[{lxcfs}]
        ├─networkd-dispat───{networkd-dispat}
        ├─polkitd───2*[{polkitd}]
        ├─rsyslogd───3*[{rsyslogd}]
        ├─snapd───17*[{snapd}]
        ├─sshd───sshd───sshd───bash───sudo───su───bash───pstree
        ├─systemd───(sd-pam)
        ├─systemd-journal
        ├─systemd-logind
        ├─systemd-network
        ├─systemd-resolve
        ├─systemd-timesyn───{systemd-timesyn}
        ├─systemd-udevd
        └─unattended-upgr───{unattended-upgr}

Leave a Reply

Your email address will not be published. Required fields are marked *