This topic is generally well documented in the relevant part of the FreeBSD Handbook Chapter 12. Except for a small detail which becomes important in certain configurations.

I was following instructions from the handbook but debootstrap would simply stop early in the process without any errors:

...
I: Extracting tar...
I: Extracting util-linux...
I: Extracting zlib1g...
~ %

I have already seen this symptom in the past but wasn't able to find the root cause. A possible solution is mentioned in this thread however there is no explanation provided and I have been bitten by this under slightly different circumstances: I was installing Ubuntu userland inside FreeBSD jail. Of course I have tried running debootstrap both with /dev mounted and not mounted and got the same results. Searching for a solution I have seen suggestions to use --second-stage mode of debootstrap or to run dpkg --force-depends -Ei /var/cache/apt/archives/*.deb. However I had various issues with both of these methods.

By pure accident I have noticed the /debootstrap/debootstrap.log file which debootsrap leaves under the target directory (/compat/ubuntu if you follow the Handbook). The last line of this file was

mount: devfs: Operation not permitted

All started to make sense at this point: debootstrap tries to mount devfs filesystem as /dev under the target directory and exits when this fails.

Mount would fail if it is already mounted which explains why one should run debootstrap before mounting /dev as suggested both in the Handbook and in the thread mentioned above.

In addition to this inside a jail mounting a filesystem in general and mounting devfs both require additional permissions hence the Operation not permitted error.

There are two simple solutions:

  • Either allow devfs mount in a jail (see allow.mount, allow.mount.devfs and enforce_statfs jail parameters in jail(8).
  • Or just run debootstrap in the host environment.

Once debootstrap can mount devfs it proceeds further with the installation process:

...
I: Extracting tar...
I: Extracting util-linux...
I: Extracting zlib1g...
I: Installing core packages...
I: Unpacking required packages...
I: Unpacking base-files...
I: Unpacking base-passwd...
I: Unpacking bash...
...

and finishes successfully:

...
I: Configuring ubuntu-minimal...
I: Configuring libc-bin...
I: Configuring systemd...
I: Configuring ca-certificates...
I: Base system installed successfully.