Building a Self-Hosted GitLab Server on a Jetson Nano

10 July 2024

Most web-based git repositories like GitHub and GitLab impose size limits on repositories, often charging based on storage or bandwidth usage. My projects, however, frequently involve textures, meshes, and other media that easily exceed these limits. I had an unused NVIDIA Jetson Nano from my robotics AI experiments, so I thought, “Why not try this?”

A Journey into the Unknown: Setting Up a LAN-Accessible GitLab Server

Setting up a GitLab server was surprisingly straightforward using their official Docker Compose instructions here. I ran into a bit of a problem though, since their Docker images only support AMD64 architecture, which didn’t work on my Jetson Nano’s ARM64 CPU. I found an alternative ARM64 image here, based on GitLab’s own Dockerfile. These could serve as a great base for custom builds.

I ended up with this docker-compose.yml configuration:

services:
  gitlab:
    image: zengxs/gitlab:ce
    container_name: gitlab
    restart: always
    hostname: '<hostname>'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        sidekiq['concurrency'] = 4
        puma['worker_processes'] = 2
        postgresql['shared_buffers'] = "256MB"
        prometheus_monitoring['enable'] = false
        gitlab_rails['env'] = {'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000'}
        gitaly['env'] = {'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000'}
    ports:
      - '80:80'
      - '443:443'
      - '22:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

Bridging the Gap: Making It Accessible Internationally

The next challenge was exposing my GitLab server to the internet. With my home router behind CGNAT, inbound traffic just bounced off my ISP’s NAT servers. I turned to WireGuard for a solution, following this guide here and here.

I set up a VPS with an OVHCloud Starter tier here and configured the following docker-compose.yml for the WireGuard server:

volumes:
  etc_wireguard:

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy
    container_name: wg-easy
    volumes:
      - etc_wireguard:/etc/wireguard
    ports:
      - "80:80/tcp"
      - "443:443/tcp"
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1

Overcoming Technical Hurdles

Connecting my Jetson Nano to the VPS proved tricky. Installing WireGuard via apt failed due to architecture mismatches, and compiling from source hit header issues. I found a workaround using a userspace implementation here and modified the executable to remove architecture-specific code.

After configuring /etc/wireguard/wg0.conf and running sudo systemctl start wg-quick@wg0, I finally had a working setup. The final iptables rules for port forwarding looked like this:

iptables -A FORWARD -i eth0 -o wg0 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT &
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.8.0.2 &
iptables -t nat -A POSTROUTING -o wg0 -p tcp --dport 80 -d 10.8.0.2 -j SNAT --to-source 10.8.0.1 &

iptables -A FORWARD -i eth0 -o wg0 -p tcp --syn --dport 443 -m conntrack --ctstate NEW -j ACCEPT &
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.8.0.2 &
iptables -t nat -A POSTROUTING -o wg0 -p tcp --dport 443 -d 10.8.0.2 -j SNAT --to-source 10.8.0.1

Securing the Server

To avoid the “Not Secure” warning, I enabled SSL by updating the GitLab config:

  gitlab:
    ...
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        ...
        external_url '<url>'
        letsencrypt['contact_emails'] = ['<email>']
        ...

Next Steps

With this setup, I can now host my GitLab instance for practically no cost. I might eventually add an nginx reverse proxy for additional services or redirects to GitHub pages. Using a nonstandard device like the Jetson Nano definitely complicated the process, but it turned into an interesting learning experience.

For now, I’m savoring the achievement of running a fully functional GitLab server with LFS support on a low-cost, low-power device.