Folks: Let's get Claude Code native installer working for FreeBSD

Hello everyone.

This is the way I do it.
It works on 15.1 and it's easy.

# Switch to the latest branch
Code:
grep -h '^[A-Za-z-]*:' /etc/pkg/FreeBSD.conf
mkdir -p /usr/local/etc/pkg/repos
cat > /usr/local/etc/pkg/repos/FreeBSD-ports.conf <<"EOF"
FreeBSD-ports: {
  url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
  mirror_type: "srv",
  signature_type: "fingerprints",
  fingerprints: "/usr/share/keys/pkg",
  enabled: yes
}
EOF
pkg update -f

# Activate Linuxulator permanently
Code:
sysrc linux_enable=YES
service linux start

# Mount fdescfs with linrdlnk (without that, claude will freeze)
Code:
printf "fdesc\t/dev/fd\tfdescfs\trw,linrdlnk\t0\t0\n" >> /etc/fstab
mount -t fdescfs -o linrdlnk fdesc /dev/fd

# Install claude-code
Code:
pkg install claude-code

# Deactivate the auto-updater
Code:
cat >> ~/.profile <<'EOF'
export DISABLE_AUTOUPDATER=1
EOF

# Set your API key
Code:
export ANTHROPIC_API_KEY="sk-ant-api..."
echo 'export ANTHROPIC_API_KEY="sk-ant-api..."' >> ~/.profile

# Start Claude code
Code:
claude
 
I got codex CLI runing on ghostBSD nativelly, havent tested it on freebsd yet (I dont know all the differences yet). If anyone is interested let me know, I will put it on github public this weekend if anyone wants it. It took an awful long time to build though because my test laptop has low ram (16 GB) I couldnt build it in paralelle, took about 4 + hours to build. GPT-SOL and GPT-Astra runing beautifully ! If you have enough RAM to handle all thread build it will probably compile in minutes.
 
I have gone out of my way to "leverage" frontier models in various ways such as vacation planning, vibe coding and general debugging only to find that:
1. It took way longer than anticipated to get to a semi-desirable outcome;
2. I spent a consider amount of time manually verifying the AI's output which usually required many iterations with AI and then using good 'ol search engines (which are slowly turning into trash);
3. I didn't learn anything useful during the process.

I am shocked at how much money has been spent on AI thus far and what is earmarked for the short term. I'm also shocked on something that is so innovative and revolutionary that it doesn't have a native *BSD installer. With how powerful AI is then shouldn't it take just a few moments to have a perfect port for FreeBSD?
 
pkg install codex Work well on FreeBSD 16.0-CURENT amd64

AIs require a significant amount of time for setup and familiarization; one must write rules and templates for code generation and then verify the output. This process should not take longer than writing the code directly.
 
What happens when you need to maintain that code ? Bug fixes and such... Or is it easier to ask the AI to rewrite from scratch but this go around tell it do not make any mistakes ?
 
Hi all,
Since 2.1.282, Claude Code under the Linuxulator hangs after a few prompts or minutes (GitHub #96931, #102110). The cause is a Linuxulator bug: epoll_pwait2 with a sigmask leaves a stale signal mask, and Bun/JSC’s SIGPWR GC handshake deadlocks (PR 298878). The fix is in main and stable/15 only. It is not in releng/15.1 or 14.x, and won’t come to 13.x (EOL).

Running Claude Code’s JS on native FreeBSD Bun avoids the Linuxulator. That failed before for two reasons:
  • Recent releases are a split ES-module graph, not a single cli.js.
  • The TUI needs Bun.ant.CellSegmenter, a native API that only exists in Anthropic’s private Bun build. Stock Bun shows a blank screen.
The attached script combines the pieces into a working setup:
  1. It downloads the official @anthropic-ai/claude-code-linux-x64 (or arm64) package from npm and verifies its sha512.
  2. It downloads the official FreeBSD build of Bun from oven-sh releases and verifies it against SHASUMS256. Claude is built on an internal Bun that can be ahead of public releases (2.1.304 uses 1.4.4, public is 1.4.3), so the script picks the newest public release that isn’t newer.
  3. It unpacks and patches the module graph using the helper scripts from claude-termux by 0xBrsm (https://github.com/0xBrsm/claude-termux). That project contains the JS stand-in for Bun.ant.CellSegmenter. The helpers are downloaded at a pinned commit and sha256-checked, not bundled, because that repo has no license.
  4. It checks claude --version and runs a shim self-test, then switches a current symlink. Previous versions are kept for rollback.

It runs as a normal user and installs only under $HOME. Please read the script before running it.

Code:
pkg install ripgrep bash
sh claude-native-freebsd.sh            # latest
sh claude-native-freebsd.sh 2.1.304    # specific version
sh claude-native-freebsd.sh --list
sh claude-native-freebsd.sh --use 2.1.301   # rollback
export PATH="$HOME/bin:$PATH"
claude

Tested: FreeBSD 13.5-RELEASE jail, amd64, Claude Code 2.1.304 with Opus 5.5. The interactive TUI works, the Bash, Grep and file tools work, and there are no hangs. The official FreeBSD Bun builds target 14.x, but this one runs on 13.5. Feedback from 14.x, 15.x and arm64 is welcome.

Limitations:
  • Unofficial and unsupported by Anthropic. Personal use with your own account.
  • Startup is slower (no embedded bytecode).
  • The shim ignores bidi reordering, so RTL text may render imperfectly.
  • No clipboard image paste or voice input (Linux-only native add-ons), no sandbox, no auto-update. Re-run the script to upgrade.
  • If a release changes the code the patch relies on, the install stops and the current version stays active.
If you prefer the Linuxulator: pin 2.1.281 (supports Opus 5.5 and is stable for most people), or backport the epoll fix to your kernel’s linux64.ko.

Credits: 0xBrsm (claude-termux, the shim), skair72 (not-rusty-claude, for the Bun.ant analysis), and YukiTachi and others in #96931 for tracking down the kernel bug.
 

Attachments

Hi all,
Since 2.1.282, Claude Code under the Linuxulator hangs after a few prompts or minutes (GitHub #96931, #102110). The cause is a Linuxulator bug: epoll_pwait2 with a sigmask leaves a stale signal mask, and Bun/JSC’s SIGPWR GC handshake deadlocks (PR 298878). The fix is in main and stable/15 only. It is not in releng/15.1 or 14.x, and won’t come to 13.x (EOL).

Running Claude Code’s JS on native FreeBSD Bun avoids the Linuxulator. That failed before for two reasons:
  • Recent releases are a split ES-module graph, not a single cli.js.
  • The TUI needs Bun.ant.CellSegmenter, a native API that only exists in Anthropic’s private Bun build. Stock Bun shows a blank screen.
The attached script combines the pieces into a working setup:
  1. It downloads the official @anthropic-ai/claude-code-linux-x64 (or arm64) package from npm and verifies its sha512.
  2. It downloads the official FreeBSD build of Bun from oven-sh releases and verifies it against SHASUMS256. Claude is built on an internal Bun that can be ahead of public releases (2.1.304 uses 1.4.4, public is 1.4.3), so the script picks the newest public release that isn’t newer.
  3. It unpacks and patches the module graph using the helper scripts from claude-termux by 0xBrsm (https://github.com/0xBrsm/claude-termux). That project contains the JS stand-in for Bun.ant.CellSegmenter. The helpers are downloaded at a pinned commit and sha256-checked, not bundled, because that repo has no license.
  4. It checks claude --version and runs a shim self-test, then switches a current symlink. Previous versions are kept for rollback.

It runs as a normal user and installs only under $HOME. Please read the script before running it.

Code:
pkg install ripgrep bash
sh claude-native-freebsd.sh            # latest
sh claude-native-freebsd.sh 2.1.304    # specific version
sh claude-native-freebsd.sh --list
sh claude-native-freebsd.sh --use 2.1.301   # rollback
export PATH="$HOME/bin:$PATH"
claude

Tested: FreeBSD 13.5-RELEASE jail, amd64, Claude Code 2.1.304 with Opus 5.5. The interactive TUI works, the Bash, Grep and file tools work, and there are no hangs. The official FreeBSD Bun builds target 14.x, but this one runs on 13.5. Feedback from 14.x, 15.x and arm64 is welcome.

Limitations:
  • Unofficial and unsupported by Anthropic. Personal use with your own account.
  • Startup is slower (no embedded bytecode).
  • The shim ignores bidi reordering, so RTL text may render imperfectly.
  • No clipboard image paste or voice input (Linux-only native add-ons), no sandbox, no auto-update. Re-run the script to upgrade.
  • If a release changes the code the patch relies on, the install stops and the current version stays active.
If you prefer the Linuxulator: pin 2.1.281 (supports Opus 5.5 and is stable for most people), or backport the epoll fix to your kernel’s linux64.ko.

Credits: 0xBrsm (claude-termux, the shim), skair72 (not-rusty-claude, for the Bun.ant analysis), and YukiTachi and others in #96931 for tracking down the kernel bug.
Update: script v3 — fix for hosts running a FreeBSD kernel older than 13.1


A new issue turned up on a jail whose host runs 13.0 (jail userland 13.5). Claude Code started, but refused every file access. Bash failed with task output swap refused (tasks dir moved or linked), and Read/Grep reported that the path “changed after its permission was checked”, even for /etc/fstab.


Cause: the official FreeBSD Bun build resolves a file’s path from its descriptor with fcntl(fd, F_KINFO, ...). That call was added in the 13.1 kernel. A jail uses the host’s kernel, so on a 13.0 host every fs.realpath() fails with EINVAL, and Claude Code’s path checks refuse access for safety. The realpath utility in the shell still works because it uses a different syscall, which makes this confusing to debug.


Quick check (inside the jail):
Code:
uname -K          # kernel version; < 1301000 means no F_KINFO
~/.local/opt/claude-native/current/bun -e ‘try{console.log(require(“fs”).realpathSync(“/etc/fstab”))}catch(e){console.log(“ERR”,e.code)}’
ERR EINVAL means your kernel is affected.


Fix in v3 (attached): when uname -K is below 1301000, the script builds a small LD_PRELOAD library (needs cc). It intercepts only fcntl(F_KINFO): when the kernel returns EINVAL, it builds the same struct kinfo_file from kinfo_getfile(3) (sysctl kern.proc.filedesc). All other fcntl() calls pass straight through. The launcher then loads it. On 13.1+ kernels nothing changes. You can override the detection with CLAUDE_NATIVE_FKINFO=0|1.


To update an existing install:
Code:
sh claude-native-freebsd.sh --force
rm -rf /tmp/claude-0/*      # stale session dirs from failed runs


Tested: 13.0-RELEASE-p5 host kernel, 13.5-RELEASE-p2 jail userland, amd64. realpath works again with the shim, and Claude Code reads files and runs Bash.


The real fix is upgrading the host: 13.0 has had no security updates since 2022, and a 13.5 userland on a 13.0 kernel is not a supported combination anyway. The shim only covers this one gap.
 

Attachments

Back
Top