FreeBSD for Developers: Complete Environment Guide
FreeBSD is an excellent development platform. The base system includes Clang/LLVM, a solid toolchain, and DTrace for performance analysis. The ports and packages collection provides every major language runtime and development tool. Jails give you isolated, reproducible development environments that are faster than Docker.
This guide sets up a complete developer workstation on FreeBSD 14.
Base Development Tools
Compilers and Build Tools
FreeBSD ships with Clang/LLVM in the base system:
shcc --version clang --version
Install additional compilers and build tools:
shpkg install gcc13 cmake ninja meson autoconf automake libtool pkgconf gmake
FreeBSD uses make (BSD make) by default. GNU make is installed as gmake. Many open-source projects expect GNU make:
shgmake -j$(sysctl -n hw.ncpu)
Debuggers
GDB is not in the base system. Install it:
shpkg install gdb
LLDB ships with the base system:
shlldb ./myprogram
Editors
Neovim
shpkg install neovim py311-pynvim ripgrep fd-find
Create a basic configuration:
shmkdir -p ~/.config/nvim cat > ~/.config/nvim/init.lua << 'EOF' vim.opt.number = true vim.opt.relativenumber = true vim.opt.expandtab = true vim.opt.shiftwidth = 4 vim.opt.tabstop = 4 vim.opt.smartindent = true vim.opt.termguicolors = true vim.opt.signcolumn = "yes" -- Bootstrap lazy.nvim local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then vim.fn.system({"git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath}) end vim.opt.rtp:prepend(lazypath) require("lazy").setup({ "neovim/nvim-lspconfig", "nvim-treesitter/nvim-treesitter", "nvim-telescope/telescope.nvim", "lewis6991/gitsigns.nvim", }) EOF
VS Code (via code-server)
For a browser-based VS Code experience:
shpkg install code-server sysrc code_server_enable="YES" sysrc code_server_user="youruser" service code-server start
Access at http://localhost:8080.
Emacs
shpkg install emacs-nox
Or with GUI:
shpkg install emacs
Git Configuration
Install and Configure
shpkg install git git-lfs
shgit config --global user.name "Your Name" git config --global user.email "you@example.com" git config --global init.defaultBranch main git config --global core.editor nvim git config --global pull.rebase true git config --global fetch.prune true git config --global diff.colorMoved zebra
SSH Key for GitHub/GitLab
shssh-keygen -t ed25519 -C "you@example.com" cat ~/.ssh/id_ed25519.pub
Add the public key to your Git hosting provider.
Git Credential Helper
shpkg install git-credential-manager git-credential-manager configure
Useful Git Aliases
shgit config --global alias.st status git config --global alias.co checkout git config --global alias.br branch git config --global alias.lg "log --oneline --graph --all --decorate" git config --global alias.unstage "reset HEAD --" git config --global alias.last "log -1 HEAD --stat"
Python Development
Install Python
shpkg install python311 py311-pip py311-virtualenv py311-setuptools
Multiple Python versions are available:
shpkg install python39 python310 python312
Virtual Environments
Always use virtual environments for projects:
shpython3.11 -m venv /path/to/project/venv source /path/to/project/venv/bin/activate pip install -r requirements.txt
pyenv for Version Management
shpkg install pyenv
Add to ~/.profile:
shcat >> ~/.profile << 'EOF' export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" EOF
Common Python Packages
shpip install black ruff mypy pytest ipython jupyter
LSP for Python
shpip install python-lsp-server pylsp-mypy
Rust Development
Install Rust via rustup
shfetch -o /tmp/rustup-init.sh https://sh.rustup.rs sh /tmp/rustup-init.sh -y source ~/.cargo/env
Alternatively, use the FreeBSD package:
shpkg install rust cargo
The rustup approach is preferred because it keeps Rust up to date independently of FreeBSD package cycles.
Rust Tools
shrustup component add rustfmt clippy rust-analyzer cargo install cargo-watch cargo-edit cargo-audit
Build a Test Project
shcargo new hello-freebsd cd hello-freebsd cargo run cargo test cargo clippy cargo fmt --check
Cross-Compilation
Install cross-compilation targets:
shrustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu
You need linkers for the target platforms:
shpkg install aarch64-none-elf-gcc
Go Development
Install Go
shpkg install go
Set up the workspace:
shcat >> ~/.profile << 'EOF' export GOPATH=$HOME/go export PATH=$PATH:$GOPATH/bin EOF source ~/.profile
Go Tools
shgo install golang.org/x/tools/gopls@latest go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest go install golang.org/x/tools/cmd/goimports@latest go install github.com/go-delve/delve/cmd/dlv@latest
Build and Test
shmkdir -p ~/go/src/hello cat > ~/go/src/hello/main.go << 'EOF' package main import "fmt" func main() { fmt.Println("Hello from FreeBSD") } EOF cd ~/go/src/hello go mod init hello go build go test ./... go vet ./...
Node.js Development
Install Node.js
shpkg install node20 npm-node20
Or use nvm for version management:
shpkg install nvm
Add to ~/.profile:
shcat >> ~/.profile << 'EOF' export NVM_DIR="$HOME/.nvm" [ -s "/usr/local/share/nvm/nvm.sh" ] && . "/usr/local/share/nvm/nvm.sh" EOF source ~/.profile nvm install 20 nvm use 20
Global Tools
shnpm install -g typescript ts-node eslint prettier nodemon
Project Setup
shmkdir myapp && cd myapp npm init -y npm install express npm install -D typescript @types/node @types/express npx tsc --init
Jail-Based Development Environments
Jails are FreeBSD's answer to containers. They are lighter than VMs, faster than Docker, and provide real OS-level isolation.
Create a Development Jail
sh# Create a thin jail from the base system mkdir -p /jails/dev bsdinstall jail /jails/dev
Or fetch and extract a base:
shfetch https://download.freebsd.org/releases/amd64/14.1-RELEASE/base.txz -o /tmp/base.txz mkdir -p /jails/dev tar -xf /tmp/base.txz -C /jails/dev
Configure the Jail
shcat >> /etc/jail.conf << 'EOF' dev { host.hostname = "dev.jail"; path = "/jails/dev"; ip4.addr = "lo1|10.0.0.2/24"; exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; mount.devfs; allow.raw_sockets; allow.sysvipc; # Mount project directory into the jail mount += "/home/youruser/projects /jails/dev/home/youruser/projects nullfs rw 0 0"; } EOF
Create the loopback interface:
shifconfig lo1 create ifconfig lo1 inet 10.0.0.1/24 sysrc cloned_interfaces+="lo1" sysrc ifconfig_lo1="inet 10.0.0.1/24"
Start the jail:
shsysrc jail_enable="YES" service jail start dev
Install Development Tools in the Jail
shjexec dev pkg install -y python311 go rust node20 git neovim
Per-Project Jails
Create isolated environments per project:
shfor project in api frontend ml-pipeline; do zfs clone zroot/jails/dev@clean zroot/jails/${project} done
Using ZFS clones, each project jail starts as an instant copy of the base development jail. Changes are copy-on-write, using minimal additional disk space.
Poudriere for Custom Package Builds
If you need packages built with custom options:
shpkg install poudriere
Configure a build jail:
shpoudriere jail -c -j 14amd64 -v 14.1-RELEASE -a amd64 poudriere ports -c -p default
Build packages with custom options:
shpoudriere options -j 14amd64 -p default databases/postgresql16-server poudriere bulk -j 14amd64 -p default databases/postgresql16-server
DTrace for Performance Analysis
DTrace is built into FreeBSD. It requires no installation and no application modification.
Enable DTrace
shkldload dtraceall echo 'dtraceall_load="YES"' >> /boot/loader.conf
Basic Probes
List available probes:
shdtrace -l | head -20 dtrace -l | wc -l
System Call Tracing
See what system calls a process makes:
shdtrace -n 'syscall:::entry /pid == 1234/ { @[probefunc] = count(); }'
File I/O Analysis
Which files is a program reading?
shdtrace -n 'syscall::read:entry /pid == 1234/ { @[fds[arg0].fi_pathname] = count(); }'
Function Timing
Measure how long functions take:
shdtrace -n 'pid$target::function_name:entry { self->ts = timestamp; } pid$target::function_name:return /self->ts/ { @["ns"] = quantize(timestamp - self->ts); self->ts = 0; }' -p 1234
One-Liners for Development
CPU profiling (where is CPU time spent):
shdtrace -n 'profile-997 /pid == 1234/ { @[ustack()] = count(); }' -o /tmp/stacks.out
Memory allocation tracking:
shdtrace -n 'pid$target::malloc:entry { @[ustack(5)] = sum(arg0); }' -p 1234
Network I/O by process:
shdtrace -n 'syscall::sendto:entry { @[execname, pid] = sum(arg2); }'
DTrace with Python
shdtrace -n 'python$target:::function-entry { @[copyinstr(arg1)] = count(); }' -p 1234
This shows which Python functions are called most frequently.
Database Development
PostgreSQL
shpkg install postgresql16-server postgresql16-client sysrc postgresql_enable="YES" service postgresql initdb service postgresql start su - postgres -c "createdb devdb" su - postgres -c "psql devdb"
Redis
shpkg install redis sysrc redis_enable="YES" service redis start redis-cli ping
SQLite
shpkg install sqlite3
SQLite is available everywhere and perfect for local development databases.
CI/CD on FreeBSD
GitHub Actions Self-Hosted Runner
Run CI builds on FreeBSD:
shpkg install gh
Download the runner:
shmkdir -p /opt/actions-runner && cd /opt/actions-runner fetch https://github.com/actions/runner/releases/download/v2.319.0/actions-runner-freebsd-x64-2.319.0.tar.gz tar xzf actions-runner-freebsd-x64-2.319.0.tar.gz ./config.sh --url https://github.com/your-org/your-repo --token YOUR_TOKEN
Run as a service:
sh./svc.sh install ./svc.sh start
Jenkins Agent
shpkg install openjdk17
Configure the FreeBSD machine as a Jenkins agent via SSH.
Local CI with Act (GitHub Actions Locally)
For testing GitHub Actions workflows locally:
shpkg install act act -l # List available jobs act # Run the default push event
Shell Environment
Install a Better Shell
shpkg install bash zsh fish
Set as default:
shchsh -s /usr/local/bin/zsh youruser
Zsh Configuration
shpkg install zsh-autosuggestions zsh-syntax-highlighting
Add to ~/.zshrc:
shcat > ~/.zshrc << 'EOF' # History HISTSIZE=50000 SAVEHIST=50000 HISTFILE=~/.zsh_history setopt SHARE_HISTORY setopt HIST_IGNORE_DUPS # Completion autoload -Uz compinit && compinit zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' # Plugins source /usr/local/share/zsh-autosuggestions/zsh-autosuggestions.zsh source /usr/local/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh # Prompt autoload -Uz vcs_info precmd() { vcs_info } zstyle ':vcs_info:git:*' formats '%b ' setopt PROMPT_SUBST PROMPT='%F{blue}%~%f %F{green}${vcs_info_msg_0_}%f$ ' # Aliases alias ll='ls -la' alias gs='git status' alias gd='git diff' alias gl='git lg' EOF
tmux
shpkg install tmux
Basic ~/.tmux.conf:
shcat > ~/.tmux.conf << 'EOF' set -g default-terminal "screen-256color" set -g mouse on set -g history-limit 50000 set -g base-index 1 setw -g pane-base-index 1 bind r source-file ~/.tmux.conf \; display "Reloaded" bind | split-window -h -c "#{pane_current_path}" bind - split-window -v -c "#{pane_current_path}" EOF
Useful Developer Packages Summary
shpkg install \ git git-lfs \ neovim ripgrep fd-find fzf bat eza jq \ python311 py311-pip py311-virtualenv \ go rust cargo \ node20 npm-node20 \ postgresql16-client redis sqlite3 \ tmux zsh zsh-autosuggestions zsh-syntax-highlighting \ htop btop tree ncdu \ cmake ninja meson gdb \ curl wget
FAQ
Q: Is FreeBSD a good development platform?
A: Yes. The base system is clean and predictable. DTrace is unmatched for performance analysis. Jails provide lightweight isolated environments. The package collection covers every major language and tool.
Q: How does FreeBSD compare to Linux for development?
A: Most open-source software builds on both. FreeBSD advantages: DTrace, ZFS, jails, cleaner base system. Linux advantages: Docker (native), wider CI/CD ecosystem, more commercial tool support. Most developers who try FreeBSD for development keep using it.
Q: Can I run Docker on FreeBSD?
A: Not natively. Docker depends on Linux-specific kernel features (cgroups, namespaces). Use jails instead -- they are faster and provide equivalent isolation. For Docker-dependent workflows, run a Linux VM with bhyve.
Q: How do I handle Linux-only dependencies?
A: FreeBSD's Linux compatibility layer (linux_enable="YES") runs many Linux binaries. For development, most languages (Python, Rust, Go, Node) have native FreeBSD support. Rarely do you need the Linux layer for development tools.
Q: Can I use VS Code natively on FreeBSD?
A: Not the Electron-based desktop version (Microsoft does not ship FreeBSD builds). Use code-server for a browser-based VS Code, or use Neovim/Emacs with LSP plugins for equivalent functionality.
Q: How do I profile my application on FreeBSD?
A: DTrace for system-level profiling (no application modification needed). gprof for compile-time profiling. perf-equivalent: pmcstat for hardware performance counters. Valgrind is available for memory analysis.
Q: Are there FreeBSD-specific things I need to know when writing C code?
A: FreeBSD uses Clang by default (not GCC). Some GCC-specific extensions need porting. FreeBSD uses make (BSD make), not GNU make. The base system headers are in /usr/include, ports headers in /usr/local/include.
Q: How do I set up a reproducible build environment?
A: Create a jail with the exact packages your project needs. Use ZFS snapshots to version the environment. Or use Poudriere to build custom packages with specific options. Commit the package list and jail configuration to your repository.