Run GitLab under Docker

My current project includes migrating my corporate site at will between cloud providers – including my own personal dev environment. So I could use OpenShift, possibly Cloud9, etc. but I already have so much running fine as a standalone VM. What I really want to do is to make sure I can provision my web site automatically…and I choose Git in conjunction with Puppet!

More specifically – I will use GitLab as a web front-end and to manage team access to the web site source, and so I want to run GitLab in my dev environment. But…I want to run GitLab as a Docker VM. And my dev environment is based on Mac OS X – and that is a problem! According to numerous posts it can’t be solved without an NFS mount (for example, see But I’m here to tell you that yes, you can run GitLab (including the PostgreSQL backend) under Docker under the Mac. And – by extension – easily on any other system.

The problem is that when you run docker-machine under the Mac (or Windows) you – of course – are running a proxy VM for the Docker hypervisor (in my case, via VirtualBox on my Mac). Moreover, docker-machine will permit you to mount host directories to the managed VM (and get /Users mounted automatically) – but you are unable to manage permissions from the VM. So when you install / run GitLab from Sameer Naik’s excellent work you will run into the problem of PostgreSQL trying to change ownership on mounted file volumes. It simply doesn’t work under docker-machine.

You can get around the problem by making the VM larger (larger disk) but then if you dump your GitLab VM you lose your Git repositories! Not a good plan.

So my approach? It is to mount additional, persistent disks to the docker-machine VM and then present those disks to the relevant containers.

Step 1 – Create a Persistent Disk

My “persistent disk” is simply a VDI created through VirtualBox in a known folder. Here’s the code:

# create local folder on host which can be shared with docker.
# see for mac osx description
if [ ! -s /srv/docker/sab-data-01.vdi ]; then
  echo 'Create VDI...'
  sudo mkdir -p /srv/docker
  sudo VBoxManage createhd --filename /srv/docker/sab-data-01.vdi --size 65536 --format vdi --variant Standard
  sudo chmod 777 /srv/docker/sab-data-01.vdi

I create the VDI to be a max of 64GB – but it’s a dynamic disk so won’t be any bigger than is necessary.

Step 2 – Start / Attach docker-machine

When you run docker-machine, you very well can get a brand-new proxy VM created for you. So my next task is to make sure that docker-machine is running and then attach my VDI to it. Here’s some code:

# are we started?
l_docker_machine=$(docker-machine ls --quiet)
if docker-machine status $l_docker_machine 2>/dev/null | grep --quiet -e 'Stopped'; then
  echo 'Start Docker machine...'
  docker-machine start
  l_docker_machine=$(docker-machine ls --quiet)

# are we attached?
if ! VBoxManage showvminfo $l_docker_machine 2>/dev/null | grep --quiet -e '/srv/docker/sab-data-01.vdi'; then
  echo 'Attach to Docker...'
  VBoxManage storageattach $l_docker_machine --storagectl "SATA" --device 0 --port 2 --type hdd --medium /srv/docker/sab-data-01.vdi

Step 3: Initialize the Disk

On first-run, we have an uninitialized disk attached to the docker-machine VM. Let’s initialize it using good old ext4 (use anything you like; xfs, btrfs).

# do we need to init the disk?
l_sdb1_init=$(docker-machine ssh $l_docker_machine '! blkid /dev/sdb1 && echo 1' 2>/dev/null | grep UUID)
if [ x"$l_sdb1_init" = x ]; then
  echo 'Initialize sdb1 on Docker...'
  docker-machine ssh $l_docker_machine '(echo n;echo p;echo 1;echo;echo;echo w; echo q) | sudo fdisk /dev/sdb' 2>/dev/null
  docker-machine restart $l_docker_machine 
  docker-machine ssh $l_docker_machine 'sudo mkfs.ext4 /dev/sdb1' 2>/dev/null
l_sdb1_UUID=$(docker-machine ssh $l_docker_machine 'sudo blkid /dev/sdb1' 2>/dev/null | awk '{print $2}' | sed -e 's#^UUID="\([^"]\+\).*#\1#')

Once the disk is initialized, I have found I must restart the docker-machine VM:

if [ $l_needs_restart -eq 1 ]; then
  echo 'Restart Docker machine...'
  docker-machine restart $l_docker_machine 

Next – I must ensure that the disk is mounted. This *appears* to be persistent (remounted) across docker-machine VM restarts, but (of course) must be performed both on first-run as well as any time that the actual underlying docker-machine VM is recreated (which can happen regularly).

# we do need to mount the disk...check
if ! docker-machine ssh $l_docker_machine 'sudo mount' 2>/dev/null | grep --quiet -e '/dev/sdb1'; then
  echo 'Mount /dev/sdb1...'
  docker-machine ssh $l_docker_machine 'sudo mount /dev/sdb1 /mnt/sdb1'

Finally – let’s initialize the folder structure on the disks. We want space for all the GitLab components: PostgreSQL, Redis, and GitLab itself:

# create our folders - logic is in there to clean out existing folders (you'd need a backup!)
l_needs_sab_gitlab_folders=$(docker-machine ssh $l_docker_machine '[ ! -d /mnt/sdb1/sab-gitlab ] && echo 1' 2>/dev/null)
if [ x"$l_needs_sab_gitlab_folders" = x1 ]; then
  echo 'Create sab-gitlab folders....'
  docker-machine ssh $l_docker_machine 'sudo mkdir -p /mnt/sdb1/sab-gitlab' 2>/dev/null
  docker-machine ssh $l_docker_machine 'sudo mkdir -p /mnt/sdb1/sab-gitlab/postgresql /mnt/sdb1/sab-gitlab/redis /mnt/sdb1/sab-gitlab/data' 2>/dev/null
  docker-machine ssh $l_docker_machine 'sudo mkdir -p /mnt/sdb1/sab-gitlab/postgresql /mnt/sdb1/sab-gitlab/redis /mnt/sdb1/sab-gitlab/data; sudo chmod -R 777 /mnt/sdb1/sab-gitlab' 2>/dev/null

Create GitLab Containers

The next step is to create / restart our GitLab containers. I use three of them as mentioned above.

First – let’s make sure we can use the docker command (by evaluating the docker-machine environment):

# load docker command
eval $(docker-machine env)

In the following examples, I specify “volumes” (automatically-mounted host directories) from the VDI I configured on the docker-machine VM.

First – let’s start the PostgreSQL container. Our logic checks to see if we already have the container and – if so – we restart it:

# launch postgresql or restart - important to provide pg_trgm extension
if docker ps -a -f 'name=sab-gitlab-postgresql' 2>/dev/null | grep --quiet -e 'sab-gitlab-postgresql$'; then
  echo 'Start postgresql...'
  docker start 'sab-gitlab-postgresql'
  echo 'Run postgresql...'
  docker run --name sab-gitlab-postgresql -d \
    --env 'DB_NAME=gitlabdb' \
    --env 'DB_EXTENSION=unaccent,pg_trgm' \
    --env 'DB_USER=gitlab' --env 'DB_PASS=password' \
    --volume /mnt/sdb1/sab-gitlab/postgresql:/var/lib/postgresql \

Next – let’s do the same thing for Redis:

# Launch a redis container
if docker ps -a -f 'name=sab-gitlab-redis' 2>/dev/null | grep --quiet -e 'sab-gitlab-redis$'; then
  echo 'Start redis...'
  docker start 'sab-gitlab-redis'
  echo 'Run redis...'
  docker run --name sab-gitlab-redis -d \
    --volume /mnt/sdb1/sab-gitlab/redis:/var/lib/redis \

And, of course, we want to run GitLab. Note that I’m publishing ports 10080 (Web) and 10022 (for SSH access). Also, note that I use the --link option to connect the GitLab container to the Redis and PostgreSQL containers:

# launch gitlab
if docker ps -a -f 'name=sab-gitlab' 2>/dev/null | grep --quiet -e 'sab-gitlab$'; then
  echo 'Start gitlab...'
  docker start 'sab-gitlab'
  echo 'Run gitlab...'
  docker run --name sab-gitlab -d \
    --link sab-gitlab-postgresql:postgresql --link sab-gitlab-redis:redisio \
    --publish 10022:22 --publish 10080:80 \
    --env 'GITLAB_PORT=10080' --env 'GITLAB_SSH_PORT=10022' \
    --env 'GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alpha-numeric-string' \
    --volume /mnt/sdb1/sab-gitlab/data:/home/git/data \

And it all works. I’ve taken the liberty of adding a new /etc/hosts entry that points to sad-gitlab.softwareab.local.

Here’s the login page:

And here’s my web page project:

That is all.

Team-oriented systems mentor with deep knowledge of numerous software methodologies, technologies, languages, and operating systems. Excited about turning emerging technology into working production-ready systems. Focused on moving software teams to a higher level of world-class application development. Specialties:Software analysis and development...Product management through the entire lifecycle...Discrete product integration specialist!

Leave a Reply

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