Recently I saw the light, or something very bright at least, and decided it is time to use a central git repository to store my personal projects which span across many categories (code, documentation, translations, scripts, etc). Alas, as it usually is the case when you decide to change your workflow, you have to give it a bit more thought.

The thing about personal projects, is that there are too many of them and none has a known or at least certain development timeline. I may work on my backup script (mrbStudio) today and touch it again after two years, when I’ll need a new feature. Thus, the need for project management: documentation, issues, code snippets, etc. Anything that can help me get on track with a project I abandoned for some months -or more.

The simple (and probably correct) way would be to use a flat file policy, just add a README, ISSUES etc text file in the project’s directory. But I thought a dedicated solution could be better at this, making my workflow more efficient. It would make my projects index better looking too.

I checked some open source project management solutions, like Redmine and its fork, the ChiliProject, as well as indefero and others, but decided that for what would mostly be personal use, GitLab is the best choice out there.

GitLab is essentially a personal GitHub. You set up git and gitolite, and then GitLab sits on top of that, managing gitolite (users, projects, ssh keys), giving you a nice web interface and providing you some extra features, like wiki pages, a wall, issues management and others. It even recognizes and formats documents written in markdown (like the README.md files you see on GitHub) which is great for documentation writing.

To the code!

GitLab has a very good installation documentation but in order to keep it simple, the writers describe the process of installing GitLab mostly on a dedicated to GitLab server.

I chose to install it on a normal Ubuntu 10.04 based webserver which hosts some domains and is using Apache. So I will show you how to set (and manage) GitLab for such a setup while maintaining your style.

Through this guide, no reboots nor Apache downtime will occur.

Disclaimer: To use this guide, you must have the super power of… thinking. If you want to just copy – paste, it will probably not work for you and certainly you shouldn’t perform administration tasks on a public server. At every step check the possible variables of each command and set them for your setup.

1. Installing gitolite and GitLab

The developers of GitLab did a great job writing their installation guide and do their best to keep it updated. So it would be foul of me to just copy paste their work.

Just head over their GitHub page and read their installation instructions apart from the parts for the resque process and the web server (nginx, unicorn, init script) for the time being.

At the time of the writing of this article, GitLab is at version 2.6.0. You can find the installation instructions for the most recent stable release at the project’s wiki.

2. Deviating from the official guide for GitLab installation

As good as the above guide may be, you may have to deviate a bit or perform additional steps. Here are some I can think of.

Important: These steps would be better to be performed before you reach the Setup DB step of the official installation guide.

2.1. Installing the latest version of Ruby

The latest Ruby version for now is 1.9.3-p194, so you could use that instead of the 1.9.2-p290.

$ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.gz
$ tar xzvf ruby-1.9.3-p194.tar.gz
$ cd ruby-1.9.3-p194
$ ./configure
$ make
$ sudo make install

2.2. Configure gitlab.yml

You should edit your gitlab/config/gitlab.yml to at least reflect your real host under the git_host section. This is useful because it sets how GitLab will show your project’s URIs. Also set anything else you think of use.

2.3. If you use a custom ssh port

Usually in public servers we don’t use the default ssh port (22) in order to avoid some of the automated attacks out there, targeting known ports.

If that’s your case, start by setting your ssh port in gitlab.yml under the git_host section.

Then create the file:

/home/gitlab/gitlab/.ssh/config

And set it like this (assuming your ssh port is 10001):

Host localhost
  Port 100001

2.4. Create a MySQL database if you need one

GitLab’s installation guide doesn’t deal with that but you probably should know how to create a MySQL database and a user for it.

In case you don’t remember, you run MySQL:

$ mysql -u root -p

In the MySQL prompt that will appear, you type (change any variable you may need, like password):

mysql> create database gitlabdb;
mysql> create user 'gitlab'@'localhost' identified by 'password';
mysql> grant all privileges on gitlabdb.* to 'gitlab'@'localhost' with grant option;
mysql> exit;

Set your database credentials in gitlab/config/database.yml

3. Apache setup

Update: Originally this guide suggested to use Phusion Passenger to serve GitLab instead of Unicorn. Since then (yesterday that would be) I’ve found that Unicorn performs considerably faster and maybe consumes a bit less RAM. So we are going to use Unicorn.

3.1. Configure Unicorn

As the official installation guide instructs, create the unicorn configuration file:

$ sudo -u gitlab -H cp /home/gitlab/gitlab/config/unicorn.rb.orig /home/gitlab/gitlab/config/unicorn.rb

Now edit gitlab/config/unicorn.rb and add a listening port. Just uncomment the following line and set a custom port if you want:

listen "127.0.0.1:8080"

3.2. Enable and load needed Apache modules

We need the proxy, proxy_balancer and proxy_http Apache modules. Enable them:

$ sudo a2enmod proxy proxy_balancer proxy_http

In order for Apache to load the new modules, it has to be restarted. This is the only restart of the Apache service we are going to need:

$ sudo /etc/init.d/apache2 restart

3.3. Create a virtualhost for GitLab

Create a configuration file for GitLab’s virtualhost:

/etc/apache2/sites-available/gitlab.myserver.conf

Insert the lines below (adjusted accordingly) to your GitLab site’s configuration file (the one we created). If you don’t need a ssl section, remove it. If you want to keep it, I assume you know where your ssl certificates are. Notice that the SSL virtualhost needs a specific IP instead of generic. Also if you set a custom port for Unicorn, do not forget to set it at the BalanceMember line.

<VirtualHost *:80>
  ServerName gitlab.myserver.com
  ServerAlias www.gitlab.myserver.com
  DocumentRoot /home/gitlab/gitlab/public
  ErrorLog /var/log/apache2/gitlab.myserver.com_error_log
  CustomLog /var/log/apache2/gitlab.myserver.com_access_log combined

  <Proxy balancer://unicornservers>
      BalancerMember http://127.0.0.1:8080
  </Proxy>

  <Directory /home/gitlab/gitlab/public>
    AllowOverride All
    Options -MultiViews
  </Directory>

  RewriteEngine on
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]

  ProxyPass /uploads !
  ProxyPass / balancer://unicornservers/
  ProxyPassReverse / balancer://unicornservers/
  ProxyPreserveHost on

   <Proxy *>
      Order deny,allow
      Allow from all
   </Proxy>
</VirtualHost>

<VirtualHost MY_IP:443>
  ServerName gitlab.myserver.com
  ServerAlias www.gitlab.myserver.com
  DocumentRoot /home/gitlab/gitlab/public
  ErrorLog /var/log/apache2/gitlab.myserver.com_error_log
  CustomLog /var/log/apache2/gitlab.myserver.com_access_log combined

  <Proxy balancer://unicornservers>
      BalancerMember http://127.0.0.1:8080
      Header add X-Forwarded-Proto "https"
  </Proxy>

  <Directory /home/gitlab/gitlab/public>
    AllowOverride All
    Options -MultiViews
  </Directory>

  RewriteEngine on
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]

  ProxyPass /uploads !
  ProxyPass / balancer://unicornservers/
  ProxyPassReverse / balancer://unicornservers/
  ProxyPreserveHost on

   <Proxy *>
      Order deny,allow
      Allow from all
   </Proxy>

  SSLEngine on
  SSLCertificateFile /home/gitlab/gitlab/ssl.cert
  SSLCertificateKeyFile /home/gitlab/gitlab/ssl.key
</VirtualHost>

Enable your GitLab virtual host for Apache:

$ sudo a2ensite gitlab.myserver.conf

Reload Apache for your GitLab virtualhost to start:

$ sudo /etc/init.d/apache2 reload

While your GitLab virtual host is up now, it doesn’t work as GitLab hasn’t started yet. To the next section!

4. GitLab Unicorn and Resque init script

This part is almost identical to the official guide except the insserv directive which doesn’t work for Ubuntu.

Create the file:

/etc/init.d/gitlab

Put these lines inside it. They are copied from the official installation guide, so check the official installation guide for possible updates.

#!/bin/bash
### BEGIN INIT INFO
# Provides:          gitlab
# Required-Start:    $local_fs $remote_fs $network $syslog redis-server
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: GitLab git repository management
# Description:       GitLab git repository management
### END INIT INFO

DAEMON_OPTS="-c /home/gitlab/gitlab/config/unicorn.rb -E production -D"
NAME=unicorn
DESC="Gitlab service"
PID=/home/gitlab/gitlab/tmp/pids/unicorn.pid
RESQUE_PID=/home/gitlab/gitlab/tmp/pids/resque_worker.pid

case "$1" in
  start)
    CD_TO_APP_DIR="cd /home/gitlab/gitlab"
    START_DAEMON_PROCESS="bundle exec unicorn_rails $DAEMON_OPTS"
    START_RESQUE_PROCESS="./resque.sh"

    echo -n "Starting $DESC: "
    if [ `whoami` = root ]; then
      sudo -u gitlab sh -l -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS"
    else
      $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS
    fi
    echo "$NAME."
    ;;
  stop)
    echo -n "Stopping $DESC: "
    kill -QUIT `cat $PID`
    kill -QUIT `cat $RESQUE_PID`
    echo "$NAME."
    ;;
  restart)
    echo -n "Restarting $DESC: "
    kill -USR2 `cat $PID`
    kill -USR2 `cat $RESQUE_PID`
    echo "$NAME."
    ;;
  reload)
    echo -n "Reloading $DESC configuration: "
    kill -HUP `cat $PID`
    kill -HUP `cat $RESQUE_PID`
    echo "$NAME."
    ;;
  *)
    echo "Usage: $NAME {start|stop|restart|reload}" >&2
    exit 1
    ;;
esac

exit 0