# Switching from Docker Hub to GitLab Container Registry

_Continuous Delivery_

Author: Georg Ledermann  
Published: 2018-04-05  
Tags: Docker, Deployment, CI/CD  
Canonical: https://ledermann.dev/blog/2018/04/05/switching-from-docker-hub-to-gitlab-container-registry/

Docker Hub had become the bottleneck in my pipeline: slow builds and large files shuffled across the Atlantic. Moving to GitLab's container registry fixed both.

---

An automated build process is essential for software development. After every single change in the source code – no matter how small – the software is completely assembled and tested automatically. In my case, the result is usually a Docker image.

So far I've used [Docker Hub](https://hub.docker.com/) to manage my Docker images. This was quite comfortable but had some disadvantages:

- It took up to 30 minutes between the commit and the built image. This is because I used Docker Hub not only as a registry (to store the images) but also to build the images: A build was initiated by a webhook at the GitHub repo. Because the CPU resources provided by Docker Hub are limited, the process of building images was very slow.
- Docker images are quite large and are stored in the USA. The frequent transfer of large files between Germany and the USA causes a lot of traffic.
- More and more often the building failed because of an **Internal server error** at Docker Hub. This is very bad when it comes to production deployment.
- The service is not free, Docker charges a small fee for hosting of private images.

After I recently [said goodbye to DockerCloud](/blog/2018/03/29/migration-from-docker-cloud-to-portainer/), I don't want to use Docker Hub for private repositories anymore. Since I've been using GitLab CE for several years for Continuous Integration, it is about time to use the **GitLab Container Registry** for storing Docker images.

## 1. Setup GitLab with Container Registry

The GitLab Container Registry was [introduced in 2016 with GitLab 8.8](https://about.gitlab.com/2016/05/23/gitlab-container-registry/). It just needs to be enabled.

I'm using the [official Docker image for GitLab CE](https://hub.docker.com/r/gitlab/gitlab-ce/) to run GitLab on my own server behind [nginx-proxy](https://github.com/jwilder/nginx-proxy) with the [letsencrypt-nginx-proxy-companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion). To install GitLab with the enabled registry, I use the following Docker Compose file (reduced to the relevant parts):

```yaml
version: "3.4"
services:
  web:
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.example.com'
        registry_external_url 'http://registry.gitlab.example.com'
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = { 'X-Forwarded-Proto' => 'https', 'X-Forwarded-Ssl' => 'on' }
      VIRTUAL_HOST: gitlab.example.com,registry.gitlab.example.com
      VIRTUAL_PORT: 80
      LETSENCRYPT_EMAIL: info@example.com
      LETSENCRYPT_HOST: gitlab.example.com,registry.gitlab.example.com
      CERT_NAME: gitlab.example.com
    image: "gitlab/gitlab-ce:latest"
    ports:
      - "22"
      - "80"
```

Now Gitlab is available with the browser at **https://gitlab.example.com**, the registry API is available at **https://registry.gitlab.example.com**. After enabling the registry for a given project, we can push an image to our own registry like this:

```bash
~ $ docker login registry.gitlab.example.com -u myusername -p mypassword
~ $ cd myrepo
~ $ docker build -t registry.gitlab.example.com/name/repo:latest .
~ $ docker push registry.gitlab.example.com/name/repo:latest
```

Nice. Next step is automation.

## 2. Setting up a GitLab Runner

For the GitLab Runner I'm using a small virtual machine at [Hetzner](https://www.hetzner.de/cloud/). For about five bucks per month, you get the CX21, a virtual Linux host with 2 vCPU, 4GB RAM and 40GB SSD - enough for GitLab Runner. [Installing GitLab Runner](https://docs.gitlab.com/runner/install/linux-repository.html) is quite simple, I'll skip this here.

## 3. Configure project

For each project to be tested with GitLab CI, a file **.gitlab-ci.yml** is required in the root of the repo. It also covers the pushing of the image. The following minimalistic one creates the image, performs the tests and pushes the image to the registry.

```yaml
test:
  before_script:
    - echo $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY

  script:
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    - docker run --rm $CI_REGISTRY_IMAGE:latest bundle exec rake test
    - docker push $CI_REGISTRY_IMAGE:latest
```

Read on for [details about the available CI/CD variables](https://docs.gitlab.com/ce/ci/variables/) or the [configuration of the jobs](https://docs.gitlab.com/ce/ci/yaml/).

## The result

Overall, my build process has improved in several ways:

- Now it takes less than 10 minutes from commit to the finished Docker image
- The images are transferred within the Hetzner data center only (fast and without traffic costs)
- There are no costs for an external registry service
