github twitter linkedin
Self Hosting Gitlab using Quadlets
Oct 26, 2025
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.


Back to posts