Andrew Mercer
on this page

Dual-Booting FreeBSD and Linux

How to put Debian (the same approach works for most Linux distributions) next to an existing FreeBSD install on the same disk, boot either one, and share data between them. The main path assumes a UEFI machine with a GPT disk, which covers anything built in the last decade; legacy BIOS/MBR notes are in Section 7.

1. Plan the Disk Space First

Neither of FreeBSD's root filesystems can be shrunk:

  • ZFS: a pool can't be shrunk by making its partition smaller.
  • UFS: growfs only grows; there's no shrink tool.

So the space for Linux has to be unpartitioned free space that already exists. Either:

  • Leave free space when installing FreeBSD (choose Manual or Shell partitioning in bsdinstall, or set a smaller ZFS partition size in the guided installer), or
  • Add a second disk for Linux. This is simpler and safer: each OS gets its own disk, and a reinstall of one can't touch the other.

If FreeBSD already fills the disk, back it up and reinstall with room to spare, or use a second disk.

Check what you have:

gpart show ada0      # or nda0 for NVMe
=>        40  500118112  ada0  GPT  (238G)
          40     532480     1  efi  (260M)
      532520       1024     2  freebsd-boot  (512K)
      533544  4194304     3  freebsd-swap  (2.0G)
     4727848 167772160     4  freebsd-zfs  (80G)
   172500008 327618144        - free -  (156G)

The - free - line is where Linux will go. Note the efi partition (index 1) - Linux will share it.

2. Prepare FreeBSD

2.1 Confirm the FreeBSD EFI loader location

Recent FreeBSD installers mount the EFI system partition (ESP) at /boot/efi and install the loader in two places:

ls -R /boot/efi/efi
# /boot/efi/efi/boot/bootx64.efi       <- the removable-media fallback path
# /boot/efi/efi/freebsd/loader.efi     <- FreeBSD's own entry

If you only have efi/boot/bootx64.efi, copy it to its own directory so the Linux installer can't overwrite FreeBSD's only loader, and register it with the firmware:

mkdir -p /boot/efi/efi/freebsd
cp /boot/loader.efi /boot/efi/efi/freebsd/loader.efi
efibootmgr -a -c -l /boot/efi/efi/freebsd/loader.efi -L FreeBSD

If /boot/efi isn't mounted, mount the ESP first: mount -t msdosfs /dev/ada0p1 /boot/efi.

2.2 Optionally pre-create the Linux partitions

You can let the Debian installer partition the free space itself (simplest, Section 3), or create labelled partitions from FreeBSD so there's no doubt which space is which:

gpart add -t linux-swap -s 4G  -l linuxswap ada0
gpart add -t linux-data -s 60G -l linuxroot ada0
gpart add -t linux-data        -l linuxhome ada0      # rest of the free space
gpart show -l ada0

New partitions take the next free index numbers, so existing FreeBSD partitions keep their names (ada0p4 stays ada0p4).

2.3 Take a safety net

bectl create pre-dualboot                   # ZFS root: a boot environment to fall back to
gpart backup ada0 > /root/ada0.gpt          # partition table backup (copy it off the machine too)
efibootmgr -v > /root/efibootmgr.before     # record current boot entries

3. Install Linux

Boot the Debian installer from USB in UEFI mode (the firmware boot menu usually lists the stick twice; pick the "UEFI:" entry). The steps that matter:

  1. At Partition disks, choose Manual. Avoid the guided options that offer to "use entire disk."
  2. Assign the free space, or the partitions you created: - linuxroot -> ext4, mount point / - linuxhome -> ext4, mount point /home (optional) - linuxswap -> swap area
  3. Select the existing EFI System Partition and set it to "Use as: EFI System Partition". Do not format it - it holds FreeBSD's loader.
  4. Leave the FreeBSD partitions (freebsd-boot, freebsd-swap, freebsd-zfs/freebsd-ufs) set to "do not use."
  5. Finish the install. GRUB installs to /EFI/debian/ on the shared ESP and makes itself the first firmware boot entry.

4. Add FreeBSD to the GRUB Menu

os-prober doesn't reliably detect FreeBSD, so add a menu entry by hand. Boot into Debian, then find the ESP's filesystem UUID:

sudo blkid /dev/sda1          # or: sudo grub-probe --target=fs_uuid /boot/efi
# /dev/sda1: UUID="ABCD-1234" TYPE="vfat" PARTLABEL="efiboot0" ...

Add a chainload entry to /etc/grub.d/40_custom (append below the existing header lines):

menuentry "FreeBSD" {
    insmod part_gpt
    insmod fat
    search --no-floppy --fs-uuid --set=root ABCD-1234
    chainloader /efi/freebsd/loader.efi
}

Regenerate the config:

sudo update-grub

GRUB hands off to FreeBSD's own loader.efi, which then shows the normal FreeBSD boot menu (including boot environments on ZFS). GRUB doesn't need to understand ZFS or UFS for this to work.

To make the GRUB menu wait long enough to choose, set GRUB_TIMEOUT=10 in /etc/default/grub and run update-grub again.

5. Managing Boot Order Without GRUB

You don't have to route FreeBSD through GRUB. The firmware boot manager can choose between them directly, and both systems can edit it.

From FreeBSD:

efibootmgr -v                 # list entries: Boot0001* FreeBSD, Boot0002* debian, ...
efibootmgr -o 0001,0002       # set the default order (FreeBSD first)
efibootmgr -n -b 0002         # boot Debian once on the next reboot only
efibootmgr -a -b 0001         # mark an entry active

From Linux:

sudo efibootmgr -v
sudo efibootmgr -o 0001,0002
sudo efibootmgr -n 0001       # boot FreeBSD once on the next reboot

The one-time -n boot is handy for remote machines: reboot into the other OS once, and a second reboot returns to the default.

Most firmware also has a boot menu key (often F8, F11, F12 or Esc) for picking an entry at power-on. A graphical boot manager such as rEFInd is another option if you'd rather not depend on either OS's loader.

6. Sharing Data Between FreeBSD and Linux

6.1 Reading Linux filesystems from FreeBSD

FreeBSD's ext2fs driver handles ext2, ext3 and ext4:

kldload ext2fs
mkdir -p /mnt/linux
mount -t ext2fs /dev/gpt/linuxhome /mnt/linux

FreeBSD doesn't replay the ext4 journal, so only write to a Linux filesystem that Linux shut down cleanly; mount with -o ro if in doubt. More detail in FreeBSD SysAdmin Essentials (section 4.3).

6.2 Reading FreeBSD filesystems from Linux

UFS (read-only is the safe choice; Linux's UFS write support is experimental):

sudo mount -t ufs -o ufstype=ufs2,ro /dev/sda4 /mnt/freebsd

ZFS with OpenZFS on Linux (zfsutils-linux on Debian):

sudo zpool import                              # list importable pools
sudo zpool import -N -o readonly=on -R /mnt/freebsd zroot
sudo zfs mount zroot/usr/home                  # mount just what you need
sudo zpool export zroot                        # always export before booting FreeBSD again
  • Import read-only unless you have a reason not to, and always export before rebooting into FreeBSD. A pool left imported by Linux needs zpool import -f from FreeBSD, and on a root pool that means a trip to the rescue shell.
  • Never run zpool upgrade from one OS without checking the other supports every feature it enables, or the other OS will refuse to import the pool. The OpenZFS compatibility pool property can pin a pool to a feature set both sides support.
  • Mounting FreeBSD's root boot environment on Linux is rarely useful; mount data datasets like zroot/usr/home.

6.3 A shared data partition

For files you use from both systems, a dedicated partition avoids cross-OS filesystem caveats entirely:

  • ext4: native on Linux, read-write on FreeBSD via ext2fs. Good default.
  • exFAT: native on Linux, FUSE on FreeBSD (fusefs-exfat). Good if the data also travels on USB to Windows/macOS.
  • ZFS (a separate small pool): both systems are first-class, as long as you mind the feature-flag rule above.

6.4 Clock

FreeBSD and Debian both keep the hardware clock in UTC by default, so they agree out of the box. If one shows the wrong time after switching, check FreeBSD for /etc/wall_cmos_clock (its presence means "hardware clock is local time") and Linux with timedatectl (RTC in local TZ: no is what you want).

7. Legacy BIOS / MBR Systems

On an old machine booting in legacy BIOS mode with an MBR disk, the pieces are different:

  • Partitions are MBR slices. FreeBSD typically lives in slice 1 (ada0s1, type 165/freebsd), with its own BSD partitions inside (ada0s1a, ...). Add Linux slices in free space with gpart add -t linux-data ada0 (MBR type 131) and gpart add -t linux-swap ada0 (type 130). MBR allows only four primary slices.
  • GRUB 2 chainloads FreeBSD's boot block:

menuentry "FreeBSD" { insmod part_msdos set root=(hd0,msdos1) chainloader +1 }

  • Writing the MBR from a running FreeBSD system fails with "Unable to write data to disk" or Operation not permitted, because GEOM protects disks with mounted partitions. Temporarily allow it, do the write, then turn the protection back on:

sh sysctl kern.geom.debugflags=16 # ... gpart bootcode / write the MBR ... sysctl kern.geom.debugflags=0

  • FreeBSD's boot manager (boot0) can also choose between slices without GRUB: gpart bootcode -b /boot/boot0 ada0 gives an F1 FreeBSD / F2 Linux menu at power-on, with GRUB installed to the Linux slice rather than the MBR.

Old notes that install sysutils/grub (GRUB legacy) on FreeBSD, use menu.lst, or boot the Debian installer from vmlinuz/initrd.gz files placed in /boot are from the floppy and GRUB 0.97 era. Booting the installer from USB and letting Debian install GRUB 2 replaces all of that.

8. Troubleshooting

Symptom Likely cause Fix
Machine boots straight into Debian with no FreeBSD option Debian's GRUB is first in the firmware order and has no FreeBSD entry Add the GRUB entry (Section 4), or efibootmgr -o to put FreeBSD first (Section 5)
GRUB "FreeBSD" entry errors with file not found Wrong ESP UUID or loader path Re-check blkid and that /efi/freebsd/loader.efi exists on the ESP (paths are case-insensitive on FAT)
FreeBSD no longer boots at all after the Linux install ESP was formatted, or the fallback bootx64.efi was overwritten and was FreeBSD's only loader Boot FreeBSD install media -> Shell, mount the ESP, copy /boot/loader.efi back to efi/freebsd/loader.efi and efibootmgr -c a new entry
FreeBSD boots but can't mount root /etc/fstab refers to a partition that was changed during the Linux install Boot to single-user, fix /etc/fstab; prefer /dev/gpt/<label> names
Debian installer only offers "use entire disk" Installer booted in legacy BIOS mode on a UEFI/GPT system Reboot and pick the "UEFI:" entry for the USB stick
ZFS pool won't import on FreeBSD after using it from Linux Pool still marked as imported by Linux, or upgraded with Linux-only features zpool import -f; for features, there's no downgrade - restore from backup
Unable to write data to disk when editing partitions on BIOS/MBR GEOM write protection on a disk in use sysctl kern.geom.debugflags=16, then set back to 0 (Section 7)

References