The Perpetual Amateur

Self Hosting Gitlab using Quadlets

| 3 minutes read

§ What are Quadlets?

I self-host most of my services important to me personally. Lately, I have been using Quadlets for this. I came across the concept of Quadlets from Immich.

For git repositories, I have been just using git init --bare in a server and plain SSH to work them: git clone git@<user>:sources/<repo>.

Having used Quadlets with Immich, I thought of trying using them with Gitlab. And this post talks about how to do that.

§ Podman Setup

Before setting up the quadlet, there are some packages required for podman to function fine with networking. I installed: aardvark-dns, dbus-user-session, passt, podman, uidmap. But some packages from this list may not be needed for gitlab as it’s just a single container.

§ Gitlab Setup

Basically, all that we need to do is define: .config/containers/systemd/gitlab.container

[Unit]
Description=GitLab Container
After=network-online.target
Wants=network-online.target
# This ensures that the container won't start unless the directories are
# available.
# I need this since my file system is double encrypted - FDE with LUKS for
# system) and file-level encryption, managed by `ansible`. You might not need
# this if this path is guaranteed to be available.
ConditionPathExists=<path-to-data-directory>/repositories

[Container]
Image=docker.io/gitlab/gitlab-ce:latest
ContainerName=gitlab
ShmSize=256m

PublishPort=3939:80
PublishPort=2222:22

Volume=<path-to-data-directory>/repositories/config:/etc/gitlab:Z
Volume=<path-to-data-directory>/repositories/logs:/var/log/gitlab:Z
Volume=<path-to-data-directory>/repositories/data:/var/opt/gitlab:Z

# Explaining, GITLAB_OMNIBUS_CONFIG
# `external_url`: I have an external reverse proxy managed by my Ansible roles.
# `nginx['listen_port']: Just listen on `80`, so that I can manage the actual
# SSL from my reverse proxy outside.
# `nginx['listen_https']: Ask `nginx` to not do SSL inside the container.
# `gitlab_rails['gitlab_shell_ssh_port']: The port I use for listening for SSH.
Environment=GITLAB_OMNIBUS_CONFIG="external_url 'https://<my-hostname>'; nginx['listen_port'] = 80; nginx['listen_https'] = false; gitlab_rails['gitlab_shell_ssh_port'] = 2222;"

[Service]
Restart=always
TimeoutStartSec=900

[Install]
WantedBy=default.target

§ Gitlab Omnibus Config

There’s quite a bit going on in the GITLAB_OMNIBUS_CONFIG. My Ansible setup takes care of setting up SSL and exposing it over 443, so I don’t forward that to gitlab. That’s what I configure external_url to. The nginx specific configuration asks nginx to not do SSL and listen on 80 which I forward to 3939 outside. For SSH, the port I forward is 2222 as my node itself does its ssh on 22.

§ Starting the Service

Once this unit file is set up, you should be able to start it with:

$ systemctl --user daemon-reload
$ systemctl --user enable --now gitlab.service

For the first launch, getting the image will take some time. You can look at the status using: systemctl status --user gitlab.

Once it is up and running, use journalctl --user -fu gitlab to look at the service logs.

Now, ideally if you set up your reverse proxy right, you should be able to access gitlab over your hostname.

§ Gitlab Configuration

We do need to take care of one more thing, that is the root password. You can find it under your config directory in the file initial_root_password. Use this password with the username root for further configuration as per next steps documentation.

§ Why Quadlets?

Podman and Quadlets can manage a significantly more complex configuration (for instance, Immich via this handbook). I find this much easier to manage than docker-compose.yaml, which isn’t ergonomic with Ansible. Additionally, podman doesn’t need any daemon running like Docker for it to be useful.