FreeBSD Setup
Deploying Clawdie on FreeBSD 15 with native jail isolation
Most AI tooling assumes Linux. Clawdie runs on FreeBSD — using native jails instead of Docker, PF instead of iptables, and rc.d instead of systemd. This guide documents the complete setup from bare metal to running agent.
FreeBSD 15.0-RELEASE-p4 on DigitalOcean VPS. Should work on any FreeBSD 14+ system.
Base system preparation
1.1 Update the system
freebsd-update fetch install
pkg update && pkg upgrade -y
1.2 Install required packages
pkg install -y \
node24 npm-node24 \
git \
nginx \
python311 \
sudo \
tmux \
pftop \
htop
1.3 Create the clawdie user
# Create user with home directory
pw useradd clawdie -m -s /bin/sh -G wheel,webmaster
passwd clawdie
# Set up SSH key authentication
mkdir -p /home/clawdie/.ssh
chmod 700 /home/clawdie/.ssh
1.4 Directory structure
/home/clawdie/
├── clawdie-cp/ # Control plane (AI runtime)
│ ├── src/ # TypeScript source
│ ├── container/ # Jail isolation layer
│ ├── .agent/skills/ # Applied customizations
│ ├── workspace/ # Identity + memory files
│ ├── logs/ # Runtime logs
│ ├── tmp/ # Project-local temp files
│ └── credentials/ # 600 permission files
│
├── clawdie-robot/ # Robot OS (planned)
│
└── vm/ # VM configs (planned)
└── bhyve/ # FreeBSD bhyve definitions
FreeBSD jail configuration
Clawdie uses FreeBSD jails to isolate agent execution — the same technology that inspired Docker, but native to FreeBSD with zero emulation overhead.
2.1 Create the jail base
# Create jail directory
mkdir -p /jails/clawdie-cp
# Install a minimal base system into the jail
fetch https://download.freebsd.org/releases/amd64/15.0-RELEASE/base.txz
tar -xf base.txz -C /jails/clawdie-cp
# Copy DNS resolution
cp /etc/resolv.conf /jails/clawdie-cp/etc/
2.2 Jail configuration
Add to /etc/jail.conf:
clawdie {
host.hostname = "clawdie.clawdie.si";
ip4.addr = "lo1|192.168.1.100/32";
path = "/jails/clawdie-cp";
mount.devfs;
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
allow.raw_sockets;
enforce_statfs = 2;
children.max = 0;
# Resource limits
# Memory: 2G, CPU: 2 cores, Processes: 1000
}
2.3 Mount points
Add to /etc/fstab.clawdie:
# Project source (read-only)
/home/clawdie/clawdie-cp /jails/clawdie-cp/workspace/project nullfs ro 0 0
# Agent workspace (read-write)
/home/clawdie/clawdie-cp/groups /jails/clawdie-cp/workspace/groups nullfs rw 0 0
# Device filesystem
devfs /jails/clawdie-cp/dev devfs rulenum=4 0 0
# Temp (tmpfs — not on disk)
tmpfs /jails/clawdie-cp/tmp tmpfs rw,size=256m 0 0
2.4 Start the jail
# Create the loopback interface for jail networking
ifconfig lo1 create
ifconfig lo1 inet 192.168.1.100/32
# Start the jail
service jail start clawdie
# Verify
jls
jexec clawdie hostname
Docker on FreeBSD requires Linux emulation (linuxulator), adding overhead and compatibility issues. Native jails provide the same isolation with zero emulation penalty, direct kernel support, and simpler configuration.
PF firewall
FreeBSD's PF (Packet Filter) provides stateful packet filtering with clean syntax. Our configuration supports both IPv4 and IPv6.
3.1 Configuration
Edit /etc/pf.conf:
ext_if="vtnet0"
tailscale_if="tailscale0"
set skip on lo0
block all
pass out all keep state
# SSH via Tailscale only (no public SSH)
pass in quick on $tailscale_if proto tcp to port 22 keep state
# Web traffic — Let's Encrypt + HTTPS
pass in quick on $ext_if inet proto tcp to port {80,443} keep state
pass in quick on $ext_if inet6 proto tcp to port {80,443} keep state
# Tailscale WireGuard UDP
pass in quick on $ext_if inet proto udp to port 41641 keep state
pass in quick on $ext_if inet6 proto udp to port 41641 keep state
# Jail NAT (allow jail to reach the internet)
nat on $ext_if from 192.168.1.0/24 to any -> ($ext_if)
3.2 Enable and load
# Enable PF in rc.conf
sysrc pf_enable="YES"
sysrc pflog_enable="YES"
# Load the rules
pfctl -f /etc/pf.conf
# Verify
pfctl -sr # show rules
pftop # live traffic monitor
Default deny policy. The block all rule means
everything is blocked unless explicitly allowed. Test your rules carefully
before applying — a mistake can lock you out of SSH.
Install Clawdie
4.1 Clone and install
cd /home/clawdie
git clone https://codeberg.org/Clawdie/clawdie-ai.git clawdie-cp
cd clawdie-cp
npm install
npm run build
4.2 Environment configuration
Create /home/clawdie/clawdie-cp/.env:
ASSISTANT_NAME=Clawdie
ASSISTANT_HAS_OWN_NUMBER=false
CONTAINER_RUNTIME=jail
JAIL_PATH=/jails/clawdie-cp
LOG_LEVEL=info
TZ=Europe/Ljubljana
# Telegram
TELEGRAM_BOT_TOKEN=your_bot_token
# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your_anon_key
# LLM Providers (configure one or more)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
OPENROUTER_API_KEY=sk-or-...
ZAI_API_KEY=...
4.3 Identity files
# Create workspace directory
mkdir -p /home/clawdie/clawdie-cp/workspace
# Identity system — these persist across redeployment
cat > workspace/SOUL.md # Personality definition
cat > workspace/IDENTITY.md # Self-concept
cat > workspace/USER.md # Information about operator
4.4 rc.d service
Create /usr/local/etc/rc.d/clawdie:
#!/bin/sh
#
# PROVIDE: clawdie
# REQUIRE: NETWORKING
# KEYWORD: shutdown
. /etc/rc.subr
name="clawdie"
rcvar="clawdie_enable"
pidfile="/var/run/${name}.pid"
command="/usr/local/bin/node"
command_args="/home/clawdie/clawdie-cp/dist/index.js"
clawdie_user="clawdie"
load_rc_config $name
run_rc_command "$1"
# Enable and start
chmod +x /usr/local/etc/rc.d/clawdie
sysrc clawdie_enable="YES"
service clawdie start
tmux glass-pane console
Clawdie's signature feature — a live terminal window into agent reasoning. Three panes: gateway output, interactive shell, system monitoring.
#!/bin/sh
# start-clawdie.sh — tmux glass-pane initialization
SESSION="clawdie"
tmux new-session -d -s $SESSION -n main
# Pane 0: Gateway (agent runtime)
tmux send-keys -t $SESSION "cd /home/clawdie/clawdie-cp && npm start" Enter
# Pane 1: Shell (interactive)
tmux split-window -h -t $SESSION
tmux send-keys -t $SESSION "cd /home/clawdie/clawdie-cp" Enter
# Pane 2: System monitor
tmux split-window -v -t $SESSION
tmux send-keys -t $SESSION "btop" Enter
tmux select-pane -t 0
tmux attach -t $SESSION
Attach from anywhere via SSH:
ssh clawdie@100.x.x.x -t "tmux attach -t clawdie"