Cross compile Rust to FreeBSD using Nix


24 December 2025

Here’s a quick example of a flake.nix that sets up an environment suitable to cross-compile Rust from a Linux host to a FreeBSD target. The example project uses Tokio and around 100 dependencies overall, but it likely requires additional work to support building against and linking to arbitrary C libraries.

This was tested on a x86_64 Linux host. The target triple is x86_64-unknown-freebsd. The flake.nix builds a dev shell with:

  • clang cross compiler
  • FreeBSD 15.0 sysroot

See below for the flake.nix. With this file in place, and a Cargo.toml and some Rust source, you get both a normal dev shell using nix develop for local development, and a dev shell for cross-compiling to FreeBSD using a command like:

For the example, I’m using the static-files example from Poem. With that in place, the following should work:

$ nix develop .#freebsd -c cargo build --target=x86_64-unknown-freebsd --release

$ file target/x86_64-unknown-freebsd/debug/example-static-files
target/x86_64-unknown-freebsd/debug/example-static-files:
  ELF 64-bit LSB pie executable, x86-64, version 1 (FreeBSD),
  dynamically linked, interpreter /libexec/ld-elf.so.1,
  for FreeBSD 15.0 (1500068), FreeBSD-style,
  with debug_info, not stripped

The binary can be copied and launched from an x86_64 FreeBSD system.

See this nixos wiki for additional ideas.

# flake.nix
{
  description = "Dev shells for Rust development on Linux with FreeBSD cross compilation";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs =
    {
      self,
      nixpkgs,
      flake-utils,
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        devShells.default =
          with pkgs;
          mkShell {
            name = "dev-shell";

            packages = [
              stdenv

              # Rust toolchain
              cargo
              rustc
              rust-analyzer
              rustfmt
              clippy
            ];
          };

        # Shell for cross-compiling to FreeBSD from Linux.
        #
        # Uses pkgsCross.cargo to get a cross rustc wrapper, and host clang
        # (via pkgs.llvmPackages.clang-unwrapped) with explicit --target/--sysroot.
        #
        # Usage: nix develop .#freebsd -c cargo build --target=x86_64-unknown-freebsd --release
        devShells.freebsd = let
          pkgsCross = pkgs.pkgsCross.x86_64-freebsd;
          target = "x86_64-unknown-freebsd";
          sysroot = pkgs.fetchzip {
            url = "https://download.freebsd.org/releases/amd64/15.0-RELEASE/base.txz";
            hash = "sha256-OLnHge+hTwjvMNTiChI7YWOhkFUJKj2WQ33SaCa7h4E=";
            stripRoot = false;
          };
        in pkgs.mkShell {
          name = "freebsd-cross";

          nativeBuildInputs = [
            pkgsCross.cargo
            pkgs.llvmPackages.clang-unwrapped
            pkgs.llvmPackages.lld
          ];

          CC_x86_64_unknown_freebsd = "${pkgs.llvmPackages.clang-unwrapped}/bin/clang";
          CXX_x86_64_unknown_freebsd = "${pkgs.llvmPackages.clang-unwrapped}/bin/clang++";

          RUSTFLAGS = [
            "-Clinker=${pkgs.llvmPackages.clang-unwrapped}/bin/clang"
            "-Clink-arg=--target=${target}"
            "-Clink-arg=--sysroot=${sysroot}"
            "-Clink-arg=-fuse-ld=lld"
          ];

          CFLAGS_x86_64_unknown_freebsd = "--target=${target} --sysroot=${sysroot}";
          CXXFLAGS_x86_64_unknown_freebsd = "--target=${target} --sysroot=${sysroot}";

          shellHook = ''
            echo "FreeBSD cross-compilation environment ready. Target: ${target}"
            echo "Run: cargo build --target=${target} --release"
          '';
        };
      }
    );
}