FreeBSD.software
Home/Guides/FreeBSD for Developers: Complete Environment Guide
guide·2026-04-09·10 min read

FreeBSD for Developers: Complete Environment Guide

Set up a complete FreeBSD development environment: compilers, editors, Git, Python, Rust, Go, Node.js, jail-based dev environments, DTrace profiling, and CI pipelines.

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:

sh
cc --version clang --version

Install additional compilers and build tools:

sh
pkg 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:

sh
gmake -j$(sysctl -n hw.ncpu)

Debuggers

GDB is not in the base system. Install it:

sh
pkg install gdb

LLDB ships with the base system:

sh
lldb ./myprogram

Editors

Neovim

sh
pkg install neovim py311-pynvim ripgrep fd-find

Create a basic configuration:

sh
mkdir -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:

sh
pkg install code-server sysrc code_server_enable="YES" sysrc code_server_user="youruser" service code-server start

Access at http://localhost:8080.

Emacs

sh
pkg install emacs-nox

Or with GUI:

sh
pkg install emacs

Git Configuration

Install and Configure

sh
pkg install git git-lfs
sh
git 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

sh
ssh-keygen -t ed25519 -C "you@example.com" cat ~/.ssh/id_ed25519.pub

Add the public key to your Git hosting provider.

Git Credential Helper

sh
pkg install git-credential-manager git-credential-manager configure

Useful Git Aliases

sh
git 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

sh
pkg install python311 py311-pip py311-virtualenv py311-setuptools

Multiple Python versions are available:

sh
pkg install python39 python310 python312

Virtual Environments

Always use virtual environments for projects:

sh
python3.11 -m venv /path/to/project/venv source /path/to/project/venv/bin/activate pip install -r requirements.txt

pyenv for Version Management

sh
pkg install pyenv

Add to ~/.profile:

sh
cat >> ~/.profile << 'EOF' export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" EOF

Common Python Packages

sh
pip install black ruff mypy pytest ipython jupyter

LSP for Python

sh
pip install python-lsp-server pylsp-mypy

Rust Development

Install Rust via rustup

sh
fetch -o /tmp/rustup-init.sh https://sh.rustup.rs sh /tmp/rustup-init.sh -y source ~/.cargo/env

Alternatively, use the FreeBSD package:

sh
pkg install rust cargo

The rustup approach is preferred because it keeps Rust up to date independently of FreeBSD package cycles.

Rust Tools

sh
rustup component add rustfmt clippy rust-analyzer cargo install cargo-watch cargo-edit cargo-audit

Build a Test Project

sh
cargo new hello-freebsd cd hello-freebsd cargo run cargo test cargo clippy cargo fmt --check

Cross-Compilation

Install cross-compilation targets:

sh
rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu

You need linkers for the target platforms:

sh
pkg install aarch64-none-elf-gcc

Go Development

Install Go

sh
pkg install go

Set up the workspace:

sh
cat >> ~/.profile << 'EOF' export GOPATH=$HOME/go export PATH=$PATH:$GOPATH/bin EOF source ~/.profile

Go Tools

sh
go 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

sh
mkdir -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

sh
pkg install node20 npm-node20

Or use nvm for version management:

sh
pkg install nvm

Add to ~/.profile:

sh
cat >> ~/.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

sh
npm install -g typescript ts-node eslint prettier nodemon

Project Setup

sh
mkdir 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:

sh
fetch 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

sh
cat >> /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:

sh
ifconfig 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:

sh
sysrc jail_enable="YES" service jail start dev

Install Development Tools in the Jail

sh
jexec dev pkg install -y python311 go rust node20 git neovim

Per-Project Jails

Create isolated environments per project:

sh
for 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:

sh
pkg install poudriere

Configure a build jail:

sh
poudriere jail -c -j 14amd64 -v 14.1-RELEASE -a amd64 poudriere ports -c -p default

Build packages with custom options:

sh
poudriere 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

sh
kldload dtraceall echo 'dtraceall_load="YES"' >> /boot/loader.conf

Basic Probes

List available probes:

sh
dtrace -l | head -20 dtrace -l | wc -l

System Call Tracing

See what system calls a process makes:

sh
dtrace -n 'syscall:::entry /pid == 1234/ { @[probefunc] = count(); }'

File I/O Analysis

Which files is a program reading?

sh
dtrace -n 'syscall::read:entry /pid == 1234/ { @[fds[arg0].fi_pathname] = count(); }'

Function Timing

Measure how long functions take:

sh
dtrace -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):

sh
dtrace -n 'profile-997 /pid == 1234/ { @[ustack()] = count(); }' -o /tmp/stacks.out

Memory allocation tracking:

sh
dtrace -n 'pid$target::malloc:entry { @[ustack(5)] = sum(arg0); }' -p 1234

Network I/O by process:

sh
dtrace -n 'syscall::sendto:entry { @[execname, pid] = sum(arg2); }'

DTrace with Python

sh
dtrace -n 'python$target:::function-entry { @[copyinstr(arg1)] = count(); }' -p 1234

This shows which Python functions are called most frequently.

Database Development

PostgreSQL

sh
pkg 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

sh
pkg install redis sysrc redis_enable="YES" service redis start redis-cli ping

SQLite

sh
pkg 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:

sh
pkg install gh

Download the runner:

sh
mkdir -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

sh
pkg 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:

sh
pkg install act act -l # List available jobs act # Run the default push event

Shell Environment

Install a Better Shell

sh
pkg install bash zsh fish

Set as default:

sh
chsh -s /usr/local/bin/zsh youruser

Zsh Configuration

sh
pkg install zsh-autosuggestions zsh-syntax-highlighting

Add to ~/.zshrc:

sh
cat > ~/.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

sh
pkg install tmux

Basic ~/.tmux.conf:

sh
cat > ~/.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

sh
pkg 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.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.