Today I will share how to install and setup a GitLab server on FreeBSD. Most people just use Microslopft GitHub these days but this approach has one big drawback β its cloud only solution. When it comes to on premise solutions there are GitLab and there is also Gitea. GitLab is closest to what GitHub provides while Gitea is very light and smaller brother trying to achieve the same goals by doing less. Good to have alternatives.

The Table of Contents is more or less like that:
- Bhyve VM
- Packages
- PostgreSQL
- Redis
- GitLab
- Nginx
- Final GitLab Tweaks
- GitLab Web Interface
- Summary
This guide was inspired by information from many places β but the biggest one is Matthias Fechner https://gitlab.com/mfechner/freebsd-gitlab-docu page.
Bhyve VM
We will use a dedicated Bhyve VM for that β with sysutils/vm-bhyve-devel help.
FreeBSD project provided ready to use FreeBSD-15.1-RELEASE-amd64-zfs.raw.xz VM image will be a good starting point.
host # vm create -t freebsd gitlab host # env EDITOR=cat vm config gitlab loader="bhyveload" cpu=4 memory=16G network0_type="virtio-net" network0_switch="public" network0_mac="58:9c:fc:01:fb:f8" disk0_type="nvme" disk0_name="disk0.img" uuid="15cd2fff-7942-4ca4-a29f-6ed48f063371" host # xz -d -c FreeBSD-15.1-RELEASE-amd64-zfs.raw.xz > /vm/gitlab/disk0.img host # truncate -s 30g /vm/gitlab/disk0.img host # vm start gitlab Starting gitlab * found guest in /vm/gitlab * booting... host # vm console gitlab login: root root@gitlab:~ # :> ~/.hushlogin root@gitlab:~ # gpart show -p => 34 62914486 nda0 GPT (30G) 34 347 nda0p1 freebsd-boot (174K) 381 66584 nda0p2 efi (33M) 66965 2097152 nda0p3 freebsd-swap (1.0G) 2164117 60750403 nda0p4 freebsd-zfs (29G)
To expand the disk/partition/pool this is the guide You are looking for β Expand GELI Encrypted Bhyve VM ZFS Disk β just omit the GELI part as its not used here.
Back to the VM configuration.
root@gitlab:~ # cat /etc/rc.conf # NETWORK hostname="gitlab.local" ifconfig_vtnet0="inet 10.1.1.44/24 up" defaultrouter="10.1.1.1" # DAEMONS zfs_enable="YES" root@gitlab:~ # sed -i '' s/quarterly/latest/g /etc/pkg/FreeBSD.conf root@gitlab:~ # echo nameserver 1.1.1.1 > /etc/resolv.conf root@gitlab:~ # netstat -Win -f inet Name Mtu Network Address Ipkts Ierrs Idrop Opkts Oerrs Coll vtnet0 - 10.1.1.0/24 10.1.1.44 905 - - 466 - - lo0 - 127.0.0.0/8 127.0.0.1 633774 - - 633774 - -
Packages
Needed packages installation.
root@gitlab:~ # \
pkg install -y \
gitlab-ce \
postgresql18-server \
postgresql18-client \
postgresql18-contrib \
nginx \
redis
PostgreSQL
Now PostgreSQL database setup.
root@gitlab:~ # service postgresql enable
root@gitlab:~ # service postgresql initdb
root@gitlab:~ # service postgresql start
root@gitlab:~ # psql -d template1 -U postgres \
-c "CREATE USER git CREATEDB SUPERUSER PASSWORD 'password';"
root@gitlab:~ # psql -d template1 -U postgres \
-c "CREATE DATABASE gitlabhq_production OWNER git;"
Now lets try to connect to new database with new user.
root@gitlab:~ # psql -U git -d gitlabhq_production psql (18.4) Type "help" for help. gitlabhq_production=#
Seems to work.
Now connect to PostgreSQL GitLab database and enable pg_trgm extension.
root@gitlab:~ # psql -U postgres -d gitlabhq_production \
-c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
root@gitlab:~ # psql -U postgres -d gitlabhq_production \
-c "CREATE EXTENSION IF NOT EXISTS btree_gist;"
root@gitlab:~ # psql -U postgres -d gitlabhq_production \
-c "CREATE EXTENSION IF NOT EXISTS plpgsql;"
Next enable 200 connections to PostgreSQL database.
root@gitlab:~ # psql -U postgres -c "ALTER SYSTEM SET max_connections TO '200';"
root@gitlab:~ # psql -U postgres -c "SELECT pg_reload_conf();"
root@gitlab:~ # service postgresql restart
Verify it is now set to 200.
root@gitlab:~ # psql -U postgres -c "show max_connections;"
max_connections
-----------------
200
(1 row)
Redis
Setup Redis config.
root@gitlab:~ # echo 'unixsocket /var/run/redis/redis.sock' >> /usr/local/etc/redis.conf
Grant permission to socket to all members of the redis group.
root@gitlab:~ # echo 'unixsocketperm 770' >> /usr/local/etc/redis.conf
Add git user to redis group.
root@gitlab:~ # pw groupmod redis -m git
Allow Redis to be started and start it.
root@gitlab:~ # service redis enable
root@gitlab:~ # service redis start
Find number of CPU cores.
root@gitlab:~ # sysctl hw.ncpu
hw.ncpu: 4
Change amount of workers to 4 next.
root@gitlab:~ # grep '^workers' /usr/local/www/gitlab/config/puma.rb
workers 4
Configure Git global settings for git user.
root@gitlab:~ # su -l git -c "git config --global core.autocrlf input"
Disable 'git gc --auto' because GitLab already runs 'git gc' when needed.
root@gitlab:~ # su -l git -c "git config --global gc.auto 0"
Enable packfile bitmaps.
root@gitlab:~ # su -l git -c "git config --global repack.writeBitmaps true"
Enable push options.
root@gitlab:~ # su -l git -c "git config --global receive.advertisePushOptions true"
Enable fsyncObjectFiles to reduce risk of repository corruption if server crashes.
root@gitlab:~ # su -l git -c "git config --global core.fsync objects,derived-metadata,reference"
Make sure SSH directory exists for GitLab user.
root@gitlab:~ # su -l git -c "mkdir -p /usr/local/git/.ssh"
Make sure repositories directory exists with correct permissions.
root@gitlab:~ # su -l git -c "mkdir -p /usr/local/git/repositories"
root@gitlab:~ # chown git:git /usr/local/git/repositories
root@gitlab:~ # chmod 2770 /usr/local/git/repositories
Change passwords.
root@gitlab:~ # sed -i '' -e 's/secure password/password/g' \
/usr/local/www/gitlab/config/database.yml
Show used passwords.
root@gitlab:~ # grep password: /usr/local/www/gitlab/config/database.yml
password: "password"
password: "password"
password: "password"
password: "password"
password: "password"
password: "password"
password:
password:
Proper owner for GitLab shell.
root@gitlab:~ # chown git /usr/local/share/gitlab-shell
Setup GitLab with its password.
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \ && env DISABLE_DATABASE_ENVIRONMENT_CHECK=1 \ rake gitlab:setup RAILS_ENV=production \ GITLAB_ROOT_PASSWORD=password" (...) Do you want to continue (yes/no)? yes (...) Could not create the default administrator account: --> Password must not contain commonly used combinations of words and letters
I wanted this guide to be a generic setup β with βmust to changeβ password as password but GitLab knows better β lets cope with that.
Lets try something more sophisticated:
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \ && env DISABLE_DATABASE_ENVIRONMENT_CHECK=1 \ rake gitlab:setup RAILS_ENV=production \ GITLAB_ROOT_PASSWORD=GtFO/wTh!sBu112!t" (...) Do you want to continue (yes/no)? yes Dropped database 'gitlabhq_production' Created database 'gitlabhq_production' == Seed from /usr/local/www/gitlab/db/fixtures/production/001_application_settings.rb Creating the default ApplicationSetting record. == /usr/local/www/gitlab/db/fixtures/production/001_application_settings.rb took 0.70 seconds == Seed from /usr/local/www/gitlab/db/fixtures/production/002_default_organization.rb OK == /usr/local/www/gitlab/db/fixtures/production/002_default_organization.rb took 0.08 seconds == Seed from /usr/local/www/gitlab/db/fixtures/production/003_admin.rb Administrator account created: login: root password: ****** == /usr/local/www/gitlab/db/fixtures/production/003_admin.rb took 2.61 seconds == Seed from /usr/local/www/gitlab/db/fixtures/production/010_settings.rb Saved CI JWT signing key Saved CI Job Token signing key == /usr/local/www/gitlab/db/fixtures/production/010_settings.rb took 0.67 seconds == Seeding took 4.06 seconds root@gitlab:~ # echo $? 0 root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab && rake gitlab:env:info RAILS_ENV=production" you have mail System information System: Current User: git Using RVM: no Ruby Version: 3.4.9 Gem Version: 4.0.15 Bundler Version:4.0.15 Rake Version: 13.4.2 Redis Version: 8.8.0 Sidekiq Version:7.3.9 Go Version: unknown GitLab information Version: 19.1.1 Revision: Unknown Directory: /usr/local/www/gitlab DB Adapter: PostgreSQL DB Version: 18.4 URL: http://localhost HTTP Clone URL: http://localhost/some-group/some-project.git SSH Clone URL: git@localhost:some-group/some-project.git Using LDAP: no Using Omniauth: yes Omniauth Providers: GitLab Shell Version: 14.54.0 Repository storages: - default: unix:/usr/local/www/gitlab/tmp/sockets/private/gitaly.socket GitLab Shell path: /usr/local/share/gitlab-shell (...)
The terminal output is much more colorful.

Better.
Now lets start it.
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \
&& rake gitlab:env:info RAILS_ENV=production"
Compile needed assets β¦ it will take some time.
If something breaks β¦ just run it again β should help.
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \
&& yarn config set python /usr/local/bin/python3.12"
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \
&& yarn install --production --pure-lockfile --ignore-optional"
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \
&& node scripts/frontend/infection_scanner/infection_scanner.mjs"
root@gitlab:~ # su -l git -c "cd /usr/local/www/gitlab \
&& RAILS_ENV=production NODE_ENV=production \
USE_DB=false SKIP_STORAGE_VALIDATION=true \
bundle exec rake gitlab:assets:compile"
Remove superuser rights from PostgreSQL database user.
root@gitlab:~ # psql -d template1 -U postgres -c "ALTER USER git WITH NOSUPERUSER;"
β¦ and enable and start GitLab service.
root@gitlab:~ # service gitlab enable
root@gitlab:~ # service gitlab start
GitLab Registry Key missing. Generating...
(...)
Starting GitLab web server (puma)
Starting GitLab Sidekiq
Starting GitLab Workhorse
Starting Gitaly
(...)
Started in 53s.
The GitLab web server with pid 70182 is running.
The GitLab Sidekiq job dispatcher with pid 72791 is running.
The GitLab Workhorse with pid 74932 is running.
Gitaly with pid 76120 is running.
GitLab and all its components are up and running.
Nginx
Site Configuration β include provided configuration in your Nginx configuration.
root@gitlab:~ # vi /usr/local/etc/nginx/nginx.conf
Within 'http' configuration block add this:
include /usr/local/www/gitlab/lib/support/nginx/gitlab;
Looks more or less like this.
root@gitlab:~ # diff -u /usr/local/etc/nginx/nginx.conf.ORG /usr/local/etc/nginx/nginx.conf --- /usr/local/etc/nginx/nginx.conf.ORG 2026-07-07 09:46:08.835333000 +0000 +++ /usr/local/etc/nginx/nginx.conf 2026-07-07 09:46:32.884929000 +0000 @@ -37,6 +37,8 @@ #gzip on; + include /usr/local/www/gitlab/lib/support/nginx/gitlab; + server { listen 80; server_name localhost;
Validate Nginx config file.
root@gitlab:~ # nginx -t
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
This above is the message You expect to see.
Next enable and start Nginx server.
root@gitlab:~ # service nginx enable
root@gitlab:~ # service nginx restart
Final GitLab Tweaks
Next create GitLab secret.
root@gitlab:~ # su -l git -c "openssl rand -base64 32 > /usr/local/www/gitlab/.gitlab_pages_secret"
root@gitlab:~ # chmod 640 /usr/local/www/gitlab/.gitlab_pages_secret
root@gitlab:~ # chgrp gitlab-pages /usr/local/www/gitlab/.gitlab_pages_secret
root@gitlab:~ # cat /usr/local/www/gitlab/.gitlab_pages_secret
p+IUsbCnDrpltlyPGq8gBzrHIkKN3++/CD+4C76Wlnw=
Lets check who is listening and on which port.
root@gitlab:~ # sockstat -l4
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
www nginx 15826 8 tcp4 *:80 *:*
root nginx 15825 8 tcp4 *:80 *:*
git ruby34 2224 11 tcp4 127.0.0.1:8082 *:*
git ruby34 2223 16 tcp4 127.0.0.1:8092 *:*
redis redis-serv 1143 6 tcp4 127.0.0.1:6379 *:*
postgres postgres 1084 7 tcp4 127.0.0.1:5432 *:*
root syslogd 914 7 udp4 *:514
GitLab Web Interface
We can now access the GitLab web interface finally.
Login with root user and GtFO/wTh!sBu112!t password.

You can just click Skip here and cover it later.

Same here.

There are some Acknowledgements here to be taken care of.

Seems its all ready.

You now have your GitLab instance ready to be used for whatever you want to use it for.
The final /etc/rc.conf config file after all these actions looks as follows.
root@gitlab:~ # cat /etc/rc.conf # NETWORK hostname="gitlab.local" ifconfig_vtnet0="inet 10.1.1.44/24 up" defaultrouter="10.1.1.1" # DAEMONS zfs_enable="YES" postgresql_enable="YES" redis_enable="YES" gitlab_enable="YES" nginx_enable="YES"
Summary
I assume You will want to add some certificate to Nginx config to make it HTTPS way β¦ and that is a good thing to start.
Not much more to be added here β feel free to share Your thoughts of course.
EOF