Pair programming with an AI agent under close supervision will drastically improve your efficiency and throughput at completing tasks. While at the same time providing you with an interactive learning environment for new skills or improving existing ones. I'm currently a fan of the Windsurf IDE - recently rebranded as Devin - with its Cascade AI. However it does not natively run on FreeBSD (despite it being a VSCode fork).
Note: Windsurf has been rebranded to Devin. The application, its server component, and its data directories were renamed in the process (for example windsurf-server became devin-server and ~/.windsurf-server became ~/.devin-server). This guide has been updated to reflect the new naming. On your first connection after upgrading, the IDE automatically migrates your old ~/.windsurf-server data directory to ~/.devin-server.
The next best thing is to connect to a remote FreeBSD development server from Windows (yes Windows! We do not use Linux here!). However there are some hoops to jump through. This guide explains the steps to get full Cascade functionality working with FreeBSD over SSH.
This guide is also available as an Agent Skill (an open standard) that you can use to automate the setup process.
Requirements
- FreeBSD 15.0 or later (required for inotify emulation)
- SSH access to the FreeBSD machine
- Devin IDE (formerly Windsurf) installed on your local machine (I use Windows)
FreeBSD versions prior to 15.0 do not implement Linux inotify syscalls, which causes the Cascade language server to fail with "function not implemented" errors. If you're running an older version, you'll need to upgrade first.
Install Required Utilities
A few packages are needed before we begin. FreeBSD doesn't include sudo by default, so we'll install security/sudo first along with the other required packages. Note that security/doas (the modern alternative commonly used on FreeBSD) won't work here since Cascade specifically invokes sudo.
Run these commands as root:
pkg install sudo flock bash curlAdd your user to the wheel group and enable passwordless sudo access. This allows Cascade to operate in turbo mode without prompting you to enter a password:
pw groupmod wheel -m yourusername
echo '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' > /usr/local/etc/sudoers.d/yourusernameYou'll also need to change your default shell on the FreeBSD system to Bash. Devin expects shells/bash, and the default FreeBSD shell (sh) will cause issues.
chsh -s /usr/local/bin/bashEnable Linux Compatibility Layer
Devin's server components are Linux binaries, so you'll need the Linux compatibility layer enabled.
sudo kldload linux64
sudo sysrc linux_enable="YES"
sudo service linux startThe first command loads the 64-bit Linux kernel module. The second makes it persistent across reboots.
Install Linux Userland (Rocky Linux 9)
As of this writing, Rocky Linux 9 (emulators/linux_base-rl9) provides glibc 2.28+, which is required by Devin's Node.js runtime. Do NOT use CentOS 7 - its glibc is too old and you'll see errors like GLIBC_2.28 not found.
sudo pkg install linux_base-rl9
sudo service linux restartManually Install the Devin Server
Devin does not auto-install on FreeBSD. You must download and extract the Linux server manually. The commit ID and version numbers change with each Devin update, so you'll need to find the current values from Devin's connection logs.
To find the current commit ID and version:
- Attempt to connect with Devin
- Open the Output panel (Ctrl+Shift+U)
- Select Remote-SSH from the dropdown
- Look for lines containing DISTRO_COMMIT and DISTRO_WINDSURF_VERSION
Once you have those values, run the following commands on your FreeBSD server:
DISTRO_WINDSURF_VERSION="3.0.12"
DISTRO_COMMIT="a335ac3d8c6b04d563d8bd757cadc86d305e3b12"
mkdir -p ~/.devin-server/bin/${DISTRO_COMMIT}
cd ~/.devin-server/bin/${DISTRO_COMMIT}
curl -L -o devin-server.tar.gz \
"https://windsurf-stable.codeiumdata.com/linux-reh-x64/stable/${DISTRO_COMMIT}/devin-reh-linux-x64-${DISTRO_WINDSURF_VERSION}.tar.gz"
tar -xzf devin-server.tar.gz --strip-components=1
rm devin-server.tar.gzReplace the DISTRO_COMMIT and DISTRO_WINDSURF_VERSION values with what you found in the logs.
Verify the Server Works
Test that the server runs under Linux emulation:
~/.devin-server/bin/${DISTRO_COMMIT}/bin/devin-server --versionYou should see output like:
1.110.1
a335ac3d8c6b04d563d8bd757cadc86d305e3b12
x64Verify the Cascade Language Server Works
This is the critical test. If this fails, Cascade won't work.
~/.devin-server/bin/${DISTRO_COMMIT}/extensions/windsurf/bin/language_server_linux_x64 --versionIf this fails with function not implemented, you are not running FreeBSD 15.0+ with inotify support.
Note: even after the rebrand, the bundled extension directory is still named windsurf (it contains the Cascade language server), so the path above is correct.
Connect from Devin
With the server installed, connecting is straightforward:
- Open Devin on your local machine
- Open Command Palette (Ctrl+Shift+P)
- Run Remote-SSH: Connect to Host...
- Enter or select your FreeBSD host
Cascade should now be fully functional.
Configure the Devin Terminal
By default, Devin's integrated terminal won't work correctly on FreeBSD because it looks for bash at /bin/bash, but FreeBSD installs bash to /usr/local/bin/bash. You'll need to update Devin's settings on your local machine.
Open Devin's settings (Ctrl+,) and add the following to your settings.json:
"terminal.integrated.profiles.linux": {
"bash": {
"path": "/usr/local/bin/bash",
"icon": "terminal-bash"
}
},
"terminal.integrated.defaultProfile.linux": "bash"This tells Devin where to find bash on FreeBSD and sets it as the default terminal profile for Linux-compatible remote hosts.
SSH Configuration (Optional)
For passwordless authentication, generate an SSH key pair on your local machine:
ssh-keygen -t ed25519 -C "your_email@example.com"Accept the default location and optionally set a passphrase. Then copy the public key to your FreeBSD server:
ssh-copy-id user@freebsd-hostOn Windows, if ssh-copy-id is not available, you can manually append your public key to the server:
type %USERPROFILE%\.ssh\id_ed25519.pub | ssh user@freebsd-host "cat >> ~/.ssh/authorized_keys"For easier connections, add an entry to your SSH config file. On Windows, this is %USERPROFILE%\.ssh\config. On Linux/MacOS, it's ~/.ssh/config.
Host freebsd-dev
HostName 192.168.1.233
User admin
IdentityFile ~/.ssh/id_ed25519Then you can simply select freebsd-dev from the host list.
Known Issues
Kernel Messages in dmesg
You may see messages like the following in dmesg:
linux: jid 0 pid 2598 (node): syscall io_uring_setup not implemented
linux: jid 0 pid 2602 (language_server_lin): unsupported prctl option 1398164801
linux: jid 0 pid 2690 (libuv-worker): unsupported ioctl TIOCGPTPEERThese are harmless. The Linux compatibility layer logs messages when applications attempt to use syscalls or ioctls that aren't implemented. Devin and Cascade function correctly despite these warnings.
Updates
Every time you update the Devin client, it will try to auto-install a matching server on the FreeBSD host and fail with Error freebsd needs manual installation of remote extension host. You then have to repeat the manual install with the new commit ID and version. The next section automates that.
Automating Server Updates
Rather than editing commit IDs by hand after every upgrade, you can drop a small helper script on the FreeBSD host. It relies on two facts:
- When the client fails to connect, it has already created an empty ~/.devin-server/bin/<commit>/ directory. That tells us exactly which commit the client wants - no guesswork.
- The CDN publishes a per-commit manifest at https://windsurf-stable.codeiumdata.com/linux-reh-x64/stable/manifest-<commit>.json containing the exact download URL, version, and SHA-256 checksum.
Important: do not use the old https://windsurf-stable.codeium.com/api/update/... endpoint to discover the "latest" build. After the rebrand that channel still serves the final Windsurf release, which will never match a Devin client. The per-commit manifest is the source of truth.
Save the following as ~/bin/update-devin-server and make it executable with chmod +x ~/bin/update-devin-server:
#!/bin/sh
# update-devin-server - install the Devin remote server that matches your client
set -eu
SERVER_BIN_DIR="${HOME}/.devin-server/bin"
CDN="https://windsurf-stable.codeiumdata.com"
COMMIT="${1:-}"
# A failed FreeBSD connect leaves an empty ~/.devin-server/bin/<commit>/ .
# Auto-detect that commit when one isn't passed on the command line.
if [ -z "${COMMIT}" ] && [ -d "${SERVER_BIN_DIR}" ]; then
for d in $(ls -1t "${SERVER_BIN_DIR}"); do
if [ -d "${SERVER_BIN_DIR}/${d}" ] && [ ! -x "${SERVER_BIN_DIR}/${d}/bin/devin-server" ]; then
COMMIT="${d}"; break
fi
done
fi
[ -n "${COMMIT}" ] || { echo "Pass a commit hash, or click Connect once so it can be auto-detected."; exit 1; }
# The per-commit manifest is the source of truth for URL, version and checksum.
MANIFEST="$(curl -sf "${CDN}/linux-reh-x64/stable/manifest-${COMMIT}.json")"
URL="$(echo "${MANIFEST}" | jq -r .url)"
SHA="$(echo "${MANIFEST}" | jq -r .sha256hash)"
VERSION="$(echo "${MANIFEST}" | jq -r .windsurfVersion)"
INSTALL_DIR="${SERVER_BIN_DIR}/${COMMIT}"
echo "Installing Devin server ${VERSION} (${COMMIT})"
mkdir -p "${INSTALL_DIR}"
curl -L -f -o "${INSTALL_DIR}/server.tar.gz" "${URL}"
[ "$(sha256 -q "${INSTALL_DIR}/server.tar.gz")" = "${SHA}" ] || { echo "checksum mismatch"; exit 1; }
tar -xzf "${INSTALL_DIR}/server.tar.gz" --strip-components=1 -C "${INSTALL_DIR}"
rm -f "${INSTALL_DIR}/server.tar.gz"
"${INSTALL_DIR}/bin/devin-server" --version
echo "Done. Reconnect from Devin."The workflow after each upgrade becomes: click Connect in Devin (it fails and creates the placeholder directory), run update-devin-server on the host, then reconnect. If you prefer, you can also pass the commit explicitly: update-devin-server <commit>.
Enjoy
If you found this helpful, you can say thanks by signing up for Devin (formerly Windsurf) using my referral link.
- Log in to post comments

.png)