| pick your distro, get ZFS on root
kldload — your platform, your way, free
Source
← Back to Overview

Edge / Branch Office Server — your branch office is a self-contained datacenter that fits in a shoebox.

A branch office needs DNS, DHCP, file sharing, and a VPN tunnel back to HQ. Traditionally that means four appliances, a support contract, and a prayer. With kldload, one box does everything — and it replicates itself to headquarters every hour so you can rebuild it from scratch if it catches fire. Install the server profile, configure the services, point syncoid at HQ, and walk away.

On ext4, replicating a branch office means rsync, cron, and hoping nothing changes mid-copy. With ZFS, the entire branch site is a set of datasets that replicate atomically to HQ over WireGuard with syncoid. Every service — DNS config, file shares, VPN keys — travels as a checksummed, incremental zfs send stream. If the branch box dies, you zfs recv onto a replacement, boot it, and the office is back. Air-gapped deployment from USB means the initial install needs zero network — the WireGuard tunnel only matters once you want replication.

The branch office as a replicable unit.

Traditional branch IT: ship a server, VPN to HQ, push config with Ansible, set up file sharing, configure DNS, deploy apps. If the server dies, rebuild everything from scratch. With ZFS: the entire branch is a dataset hierarchy. File shares, DNS zones, app configs, user data — all datasets. syncoid -r rpool/srv/branch hq:tank/branches/vancouver replicates the whole thing to HQ hourly. Server dies? zfs recv onto new hardware. Boot. The branch is back. No rebuild. No Ansible run. No "what was the config again?"

The WireGuard tunnel is the branch's lifeline. Every branch server boots with a WireGuard interface. Replication flows over it. Management traffic flows over it. The public internet never sees your data. If the internet goes down, the branch keeps running on local data. When it comes back, syncoid catches up with an incremental send. No special handling for intermittent connectivity — ZFS send is idempotent. Run it again and it picks up where it left off.

Deploy 50 branch offices from one USB image. Build the image once with the branch office stack baked in. Each branch gets a seed disk with its hostname, WireGuard keys, and site-specific config. Same ISO, different seed disk. 50 branches, 50 seed disks, one image. Ship them in the mail. Plug in and boot. The branch configures itself and phones home over WireGuard. No IT person needs to travel.

The recipe

Step 1: Install the server profile

# Boot the kldload USB, pick server profile
# After install, create datasets for branch office services
kdir /srv/branch
kdir /srv/branch/shares
kdir /srv/branch/dns
kdir /srv/branch/vpn
Each service gets its own dataset. Snapshots, quotas, and replication are per-service — not one big blob.

Step 2: Configure dnsmasq for DNS + DHCP

# Install dnsmasq
kpkg install dnsmasq

# Configure DNS + DHCP for the branch LAN
cat > /etc/dnsmasq.d/branch.conf <<'DNSMASQ'
# Listen on the LAN interface only
interface=eth1
bind-interfaces

# DHCP range for the branch
dhcp-range=10.20.1.100,10.20.1.200,255.255.255.0,12h

# Gateway and DNS
dhcp-option=option:router,10.20.1.1
dhcp-option=option:dns-server,10.20.1.1

# Local domain
domain=branch01.corp.local
local=/branch01.corp.local/

# Forward everything else to HQ DNS over the tunnel
server=10.0.0.1
DNSMASQ

systemctl enable --now dnsmasq
One config file. DNS resolution for the branch, DHCP for every device, and upstream forwarding to HQ. No clicking through web UIs.

Step 3: NFS file shares

# Create shared directories
kdir /srv/branch/shares/engineering
kdir /srv/branch/shares/sales
kdir /srv/branch/shares/common

# Set quotas
zfs set quota=100G rpool/srv/branch/shares/engineering
zfs set quota=50G rpool/srv/branch/shares/sales
zfs set quota=20G rpool/srv/branch/shares/common

# Export via NFS
cat > /etc/exports <<'NFS'
/srv/branch/shares/engineering  10.20.1.0/24(rw,sync,no_subtree_check)
/srv/branch/shares/sales        10.20.1.0/24(rw,sync,no_subtree_check)
/srv/branch/shares/common       10.20.1.0/24(rw,sync,no_subtree_check)
NFS

systemctl enable --now nfs-server
exportfs -a

Step 4: WireGuard tunnel to HQ

# Generate keys
wg genkey | tee /srv/branch/vpn/private.key | wg pubkey > /srv/branch/vpn/public.key
chmod 600 /srv/branch/vpn/private.key

# Configure the tunnel
cat > /etc/wireguard/wg-hq.conf <<'WG'
[Interface]
PrivateKey = BRANCH_PRIVATE_KEY_HERE
Address = 10.0.0.10/24
DNS = 10.0.0.1

[Peer]
PublicKey = HQ_PUBLIC_KEY_HERE
Endpoint = hq.corp.example.com:51820
AllowedIPs = 10.0.0.0/24,172.16.0.0/16
PersistentKeepalive = 25
WG

# Bring it up and keep it up
systemctl enable --now wg-quick@wg-hq
WireGuard is a 20-line config file that replaces a dedicated VPN appliance. It runs in the kernel, it's always on, and it reconnects automatically.

Step 5: ZFS replication to HQ for disaster recovery

# Install Sanoid for snapshot management
kpkg install sanoid

# Snapshot policy for branch data
cat > /etc/sanoid/sanoid.conf <<'SANOID'
[rpool/srv/branch]
  use_template = branch
  recursive = yes

[template_branch]
  autosnap = yes
  autoprune = yes
  hourly = 24
  daily = 30
  weekly = 4
  monthly = 6
SANOID

systemctl enable --now sanoid.timer

# Replicate to HQ over the WireGuard tunnel
cat > /etc/cron.d/syncoid-hq <<'CRON'
0 * * * * root syncoid --recursive --no-sync-snap rpool/srv/branch hq-backup:tank/branches/branch01/srv >> /var/log/syncoid.log 2>&1
30 * * * * root syncoid --recursive --no-sync-snap rpool/ROOT hq-backup:tank/branches/branch01/ROOT >> /var/log/syncoid.log 2>&1
CRON
Branch office burns down? Spin up a new box at HQ, receive the replica, ship it to the new site. Every file, every config, every service — restored from the last hourly sync.

What you get

One box, all services

DNS, DHCP, NFS, VPN — running on a single server with ZFS underneath. No appliance sprawl, no management planes, no web portals to click through.

Hourly replication to HQ

Syncoid sends only changed blocks over the WireGuard tunnel. A branch with 500GB of data and 1GB of daily changes? The hourly sync takes seconds.

Per-service datasets

Engineering gets 100GB. Sales gets 50GB. Each has its own snapshots, its own quota, its own replication stream. One department can't fill the disk for everyone.

Rebuildable from zero

The entire branch server — OS, configs, data — replicates to HQ. If the hardware dies, receive the backup onto a new box and ship it out. Full recovery in hours, not weeks.