FreeBSD
FreeBSD install
This is the path I actually run on ti.sspaeti.duckdns.org. If you’re starting
fresh on Linux, the Linux guide is much shorter — FreeBSD
needs a source build of DuckDB because the upstream project ships no FreeBSD
binaries and duckdb-go-bindings/v2 covers only darwin/linux/windows.
Flow
The install needs root on the FreeBSD host. Because SSH non-TTY sessions can’t prompt for a sudo/doas password, the flow is split: laptop pushes the files, then you SSH in interactively and run the install as root.
# 1. From your laptop:
make push-installer
# 2. Then on the FreeBSD host (the makefile output reminds you):
ssh ti
su root # enter root password
cd /home/sspaeti/survey-src
make install-on-server # ~20 min first time (DuckDB build)
exit # leave root
exit # leave sshWhat make install-on-server does
Idempotently, in this order:
- Installs build deps via
pkg:go,rsync,git,cmake,ninja,gmake,python3. Also runspkg upgradeon those packages to heal stale dep skew (e.g. an installedgitlinked against an olderlibpcre2than what’s now on disk — a real failure I hit). - Builds DuckDB from source under
/usr/local/src/duckdb-<ver>, then installs under/usr/local. Skipped on reruns once/usr/local/bin/duckdbreports the right version. Two FreeBSD-specific patches applied automatically:- DuckDB’s
Makefileuses GNU-make-only syntax, so the build invokesgmake(not BSDmake). - DuckDB’s vendored mbedtls calls
explicit_bzerowithout including<strings.h>— fine on Linux glibc but breaks on FreeBSD. The script prepends the include.
- DuckDB’s
- Creates the
surveysystem user and the/var/db/survey,/var/log/surveydirectories. - Writes
/usr/local/etc/survey/survey.envwith a freshly-generated 32-byte Quack token. Prints the token to stdout once — save it. Binds the service to0.0.0.0:8080(HTTP click handler) and0.0.0.0:9494(Quack) so an external reverse proxy can reach it across the LAN. - Installs
/usr/local/etc/rc.d/surveyand enables it viasysrc. - Writes
/usr/local/etc/sudoers.d/survey-deployso thatmake deploy,make logs,make statuswork passwordless from the laptop. The grant is restricted to the specific survey-related commands those targets call.
Skip the source build: use FreeBSD’s pkg
DuckDB is in the FreeBSD ports tree (databases/duckdb). If the pkg
branch you’re on has the version install-on-server.sh is configured for
(DUCKDB_VER), the script will install it via pkg and skip the
download/build paths entirely.
The catch: the default branch is quarterly which updates every 3
months, so it can be a minor version behind. The latest branch
tracks current ports.
Check your branch and what pkg has:
grep url /etc/pkg/FreeBSD.conf # quarterly or latest?
pkg search -q duckdb # shows the version pkg would installSwitch to latest (one-time, idempotent override of the base config):
mkdir -p /usr/local/etc/pkg/repos
cat > /usr/local/etc/pkg/repos/FreeBSD.conf <<'EOF'
FreeBSD: {
url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest",
mirror_type: "srv",
signature_type: "fingerprints",
fingerprints: "/usr/share/keys/pkg",
enabled: yes
}
EOF
pkg update -f
pkg upgrade -y # aligns all installed packages with `latest`Warning: latest is a per-host decision. Future pkg upgrade will pull
newer versions of every installed package (Caddy, Listmonk, etc.). On a
single-purpose VM that’s fine; on a host running other production services
you may want to stay on quarterly and use the GHA prebuild path below.
The FreeBSD port installs headers under /usr/local/include/duckdb/, but
the Go binding expects duckdb.h directly in /usr/local/include/. The
script symlinks them automatically after a pkg install.
Set SKIP_PKG=1 to bypass this path and force download/build.
Skip the source build: download a prebuilt libduckdb
For small/low-RAM FreeBSD hosts (mine is 512 MB), the from-source DuckDB
build can OOM or take many hours. There’s a GitHub Actions workflow that
builds libduckdb.so + headers + the duckdb CLI inside a FreeBSD VM on a
beefy GitHub-hosted runner and publishes them as a release asset.
Trigger the build once:
- Go to GitHub → Actions → build-freebsd-libduckdb → Run workflow.
- Enter the DuckDB version (default
1.5.3). Click Run workflow. - ~20 min later, a release tagged
freebsd-libduckdb-v<ver>appears with afreebsd-libduckdb-v<ver>.tar.gzasset attached.
Use it:
install-on-server.sh step 2 tries fetch from the release URL before
falling back to a source build. So once the release exists for your
DUCKDB_VER, make install-on-server will download the ~50 MB tarball
(seconds) instead of compiling (hours).
Override the source repo if you forked: LIBDUCKDB_REPO=youruser/yourfork make install-on-server.
Force a from-source build instead: SKIP_PREBUILT=1 make install-on-server.
Build-time gotchas you’ll hit
sudo: a terminal is required to read the password— only happens if you try to runmake install-on-serverfrom the laptop side over SSH. Do it inside an interactivesu rootsession instead.- DuckDB source build is slow on small hosts. On a low-RAM VM with heavy
swap, the 479-step compile can take an hour or more. Detach with
tmuxand check back. The build is idempotent —gmakeresumes from whereninjalast stopped. pkg upgrade -y git pcre2if git fails to start. Stale lib-version skew is a recurring FreeBSD issue; the script attempts a targeted upgrade and bails with a useful error ifgit --versionstill fails.
Override DuckDB version
make push-installer DUCKDB_VER=1.5.4
# then on the host:
make install-on-server DUCKDB_VER=1.5.4The two version overrides must match — the Go binary links dynamically
against the system libduckdb.so via -tags=duckdb_use_lib.
Why FreeBSD specifically
I have a FreeBSD machine already running other services
(ti.sspaeti.duckdns.org). Reusing it costs nothing. The complexity above
is the cost of FreeBSD’s second-class status in the DuckDB ecosystem — not
inherent complexity in the survey tool itself. On Linux, the whole install
collapses to a go build + a systemd unit.