Introduction

FreeBSD 14 is a UNIX-derived operating system renowned for its network stack performance, ZFS integration, and Jail isolation primitives. Setting up set up a git server with gitea on freebsd 14 on FreeBSD 14 follows the rc.conf/service(8) paradigm rather than systemd, which means enabling a service and configuring its startup options are done differently from any Linux distribution. This guide walks through every step for a freshly installed FreeBSD 14 system.

Prerequisites

Before you begin, ensure you have a freshly installed FreeBSD 14 host with root or wheel-group access. Run pkg update && pkg upgrade -y so you start from a fully patched baseline. The FreeBSD Handbook recommends enabling the binary package repository over compiling from ports for production servers — this guide follows that recommendation. You will need an active internet connection to reach the pkg.freebsd.org mirror.

Step 1: Update FreeBSD 14 Packages

Always refresh the pkg repository metadata and upgrade installed packages before adding anything new to FreeBSD 14. This ensures you receive the latest binary packages and avoids dependency conflicts. Log all session commands with script -a /root/install-$(date +%Y%m%d).log before starting so you have a full audit trail for the change-management record — especially important on FreeBSD where base OS and ports updates can change library ABIs.

pkg update
pkg upgrade -y

Step 2: Install Required Packages

Install the required packages from the FreeBSD binary package repository. pkg(8) resolves dependencies automatically. For packages not in the binary repository, the equivalent port is available in the Ports Collection under /usr/ports. On FreeBSD 14 with ZFS, consider placing service data directories on a dedicated ZFS dataset with compression=lz4 and recordsize tuned for the workload — databases typically benefit from recordsize=8k while file stores prefer recordsize=1M.

pkg install -y gitea

Step 3: Enable the Service in rc.conf

FreeBSD does not use systemd. Services are enabled by setting the appropriate variable in /etc/rc.conf. The recommended way is sysrc(8) which edits the file safely without manual text editing. After enabling, start the service with service(8). On FreeBSD, the pkg-message(5) file for many packages contains post-install notes about required rc.conf entries and recommended configuration steps — always read it with pkg info -D packagename before proceeding.

sysrc gitea_enable="YES"
service gitea start
service gitea status

Step 4: Apply the Initial Configuration

Edit the service configuration file. On FreeBSD 14, most third-party package configurations live under /usr/local/etc/. The package may also install a sample configuration — check /usr/local/etc/servicename.conf.sample and copy it before editing. On FreeBSD, the pkg-message(5) file for many packages contains post-install notes about required rc.conf entries and recommended configuration steps — always read it with pkg info -D packagename before proceeding.

cp /usr/local/etc/gitea.conf.sample /usr/local/etc/gitea.conf
nano /usr/local/etc/gitea.conf
service gitea restart

Step 5: Open the Required Port in pf

FreeBSD 14 uses pf(4) as its primary packet filter. Firewall rules are defined in /etc/pf.conf. Add a pass rule for the required port, reload the ruleset, and verify with pfctl(8). Log all session commands with script -a /root/install-$(date +%Y%m%d).log before starting so you have a full audit trail for the change-management record — especially important on FreeBSD where base OS and ports updates can change library ABIs.

# Add to /etc/pf.conf:
echo "pass in proto tcp to any port 3000" >> /etc/pf.conf
pfctl -f /etc/pf.conf
pfctl -sr | grep 3000

Step 6: Consider FreeBSD Jail Isolation

One of FreeBSD 14’s most powerful features is Jails — lightweight OS-level virtualisation that isolates services without the overhead of a VM. Consider running this service inside a Jail using iocage or cbsd for production deployments to limit the blast radius of a compromise. On FreeBSD, the pkg-message(5) file for many packages contains post-install notes about required rc.conf entries and recommended configuration steps — always read it with pkg info -D packagename before proceeding.

pkg install -y iocage
iocage fetch -r 15.0-RELEASE
iocage create -r 15.0-RELEASE -n servicejail ip4_addr="em0|192.168.1.10/24"
iocage start servicejail
iocage console servicejail

Step 7: Monitor Logs

FreeBSD 14 services log to /var/log/ via syslog(3) and newsyslog(8) handles rotation. Use tail -F to follow the log in real time and diagnose startup errors. On FreeBSD, the pkg-message(5) file for many packages contains post-install notes about required rc.conf entries and recommended configuration steps — always read it with pkg info -D packagename before proceeding.

tail -F /var/log/messages
tail -F /var/log/gitea.log 2>/dev/null || tail -F /var/log/messages

Additional Configuration Options

Once the basic deployment is stable on FreeBSD 14, consider these production-hardening steps: enable periodic(8) maintenance scripts (periodic daily weekly monthly) so the system self-audits; tune newsyslog(8) in /etc/newsyslog.conf to rotate service logs to a remote syslog server using syslogd’s @host syntax; snapshot the service ZFS dataset before each upgrade with zfs snapshot tank/data@pre-upgrade-$(date +%Y%m%d); and review the MAC/Biba or MAC/MLS policy framework if your threat model requires label-based access control beyond standard Unix DAC permissions.

zfs list -t snapshot
periodicconf_enable="YES"  # add to /etc/rc.conf
service periodic onestart daily

Troubleshooting Common Issues

Common issues on FreeBSD 14: if a service fails to start, check /var/log/messages and the rc.conf entry (service servicename rcvar). If a shared library is missing (Shared object "libXXX.so.N" not found), run pkg check -d servicename to identify broken dependencies and reinstall. For pf rule errors, run pfctl -n -f /etc/pf.conf (dry-run parse) before loading. If a Jail cannot reach the network, verify pf_enable="YES" is set before jail_enable and that the pf ruleset passes traffic from the jail’s IP. Use sockstat -4l to confirm a service is listening on the expected port and interface.

sockstat -4l
pfctl -n -f /etc/pf.conf
pkg check -da
cat /var/log/messages | tail -50

Best Practices and Hardening

For production FreeBSD 14 deployments: enable pf with a default-deny policy (block all at the top of pf.conf, then explicit pass rules); run services inside Jails with minimal network access; use GELI disk encryption for data at rest; enable FreeBSD’s built-in security.bsd sysctl hardening knobs (security.bsd.see_other_uids=0, security.bsd.hardlink_check_uid=1); subscribe to the FreeBSD Security Advisory mailing list and apply errata patches promptly with freebsd-update fetch install.

sysctl security.bsd.see_other_uids=0
sysctl security.bsd.see_other_gids=0
sysctl security.bsd.hardlink_check_uid=1
freebsd-update fetch install

Verification

Run this checklist after every deployment on FreeBSD 14: confirm the service is running with service servicename status, verify the listening socket with sockstat -4l | grep :PORT, check the pf ruleset with pfctl -sr, and make an end-to-end client request. Review /var/log/messages for any warnings logged during startup.

service servicename status
sockstat -4l
pfctl -sr
tail /var/log/messages

Conclusion

By following this guide you now have a working set up a git server with gitea on freebsd 14 setup on FreeBSD 14 that starts automatically via rc.conf, logs to syslog, and is reachable only through the pf rules you added. Consider snapshotting the ZFS dataset containing the service data after each configuration change so you can roll back in seconds: zfs snapshot zpool/data@post-install.

As a next step, consider encoding this setup as an Ansible role using the community.general.pkgng and community.general.sysrc modules so it can be applied to an entire FreeBSD fleet. Add a Prometheus node_exporter jail to collect system metrics, and include the service data ZFS dataset in a daily zfs send | ssh backup-host zfs recv job so data is protected from the first moment the service is in production.