Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@
# Build: nix build .#lxc-k8s-test-lxc
# Upload: scp result/*.tar.xz root@atlas:/var/lib/vz/template/cache/
lxc-k8s-test-lxc = self.nixosConfigurations.lxc-k8s-test.config.system.build.tarball;
# Firecracker dev VM rootfs (ext4) and kernel (vmlinux).
# Build: nix build .#fc-dev-rootfs / nix build .#fc-dev-kernel
Comment on lines +283 to +284
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fc-dev-kernel is set to config.boot.kernelPackages.kernel, which builds a kernel package output directory (containing multiple artifacts). The comment and PR description imply this output is a single vmlinux file; consider either adjusting the documentation to reflect the directory output, or exposing a flake output that points directly at the intended kernel artifact path (e.g., the vmlinux/bzImage file) to avoid ambiguity for provisioning scripts.

Suggested change
# Firecracker dev VM rootfs (ext4) and kernel (vmlinux).
# Build: nix build .#fc-dev-rootfs / nix build .#fc-dev-kernel
# Firecracker dev VM rootfs (ext4) and kernel package output directory.
# Build: nix build .#fc-dev-rootfs / nix build .#fc-dev-kernel
# Note: fc-dev-kernel is the built kernel package output, not a single vmlinux file;
# consumers should reference the desired kernel artifact path within that output.

Copilot uses AI. Check for mistakes.
fc-dev-rootfs = self.nixosConfigurations.fc-dev.config.system.build.ext4;
fc-dev-kernel = self.nixosConfigurations.fc-dev.config.boot.kernelPackages.kernel;
};

homeConfigurations = {
Expand Down Expand Up @@ -383,6 +387,13 @@
home-manager.nixosModules.home-manager
];
};

# Firecracker dev VM — NixOS rootfs for Bazel development.
# Not a real host — produces ext4 image for Firecracker microVMs.
fc-dev = nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./nix/nixos/hosts/fc_dev ];
};
};
};
}
108 changes: 108 additions & 0 deletions nix/nixos/hosts/fc_dev/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# fc-dev — NixOS configuration for Firecracker dev VMs.
# Not a real host — produces an ext4 rootfs image for Firecracker microVMs.
#
# Build rootfs: nix build .#fc-dev-rootfs
# Build kernel: nix build .#fc-dev-kernel
#
# The VM runs systemd as init, starts sshd on boot, and includes the full
# Bazel development toolchain from bazel-dev.nix.
{
modulesPath,
pkgs,
lib,
...
}:
{
imports = [
../../modules/bazel-dev.nix
(modulesPath + "/profiles/minimal.nix")
./make-rootfs.nix
];

# Firecracker boots the kernel directly — no bootloader.
boot.loader.grub.enable = false;
boot.initrd.enable = false;
boot.isContainer = false;
boot.kernelParams = [
"console=ttyS0"
"reboot=k"
"panic=1"
];

# Minimal kernel modules for virtio (Firecracker's device model).
boot.initrd.availableKernelModules = lib.mkForce [ ];
boot.kernelModules = [
"virtio_blk"
"virtio_net"
"virtio_pci"
"virtio_mmio"
];

networking.hostName = "fc-dev";
# Static network config — set by the VM pod entrypoint via kernel cmdline
# or DHCP. For simplicity, use a static config matching the TAP subnet.
networking.useDHCP = false;
networking.interfaces.eth0 = {
ipv4.addresses = [
{
address = "10.0.0.2";
prefixLength = 30;
}
];
};
networking.defaultGateway = {
address = "10.0.0.1";
interface = "eth0";
};
networking.nameservers = [
"8.8.8.8"
"1.1.1.1"
];

# SSH access — the only way into the VM.
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
};
};
# Placeholder key — real key injected via process_api CreateProcess or
# written to ~/.ssh/authorized_keys via WS command after boot.
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAA-placeholder-replaced-at-runtime"
];
Comment on lines +70 to +74
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The placeholder authorized_keys entry is not a valid public key and will be written into the image. Even if it’s intended to be replaced at runtime, leaving an invalid key can cause confusing auth failures and log noise. Prefer leaving the list empty by default (and rely entirely on runtime injection) or using a clearly-valid test key gated behind a dev-only flag.

Suggested change
# Placeholder key — real key injected via process_api CreateProcess or
# written to ~/.ssh/authorized_keys via WS command after boot.
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAA-placeholder-replaced-at-runtime"
];
# No baked-in SSH key — real key is injected via process_api CreateProcess
# or written to ~/.ssh/authorized_keys via WS command after boot.
users.users.root.openssh.authorizedKeys.keys = [ ];

Copilot uses AI. Check for mistakes.

# Dev tools beyond bazel-dev.nix
environment.systemPackages = with pkgs; [
bazelisk
python313
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bazel-dev.nix already adds python3 to environment.systemPackages; adding python313 here will pull two Python versions into the rootfs and can make python3 resolution ambiguous (and increases image size). Consider choosing a single Python (e.g., override python3 in bazel-dev.nix via an option/overlay, or remove one of the entries here).

Suggested change
python313

Copilot uses AI. Check for mistakes.
clang
openssh
curl
wget
jq
htop
# Dev headers for pip wheel builds
pkg-config
openssl.dev
cairo.dev
dbus.dev
];

# systemd: fast boot, no unnecessary services.
services.getty.autologinUser = "root";
systemd.services."serial-getty@ttyS0".enable = true;

# Firecracker VMs have limited resources — keep it lean.
documentation.enable = false;
programs.command-not-found.enable = false;

# Nix itself (for nix-shell, nix build inside the VM).
nix.settings.experimental-features = [
"nix-command"
"flakes"
];

system.stateVersion = "25.11";
}
38 changes: 38 additions & 0 deletions nix/nixos/hosts/fc_dev/make-rootfs.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Produces system.build.ext4 — an ext4 filesystem image containing the
# full NixOS system closure, suitable for Firecracker's virtio-blk rootfs.
{
config,
pkgs,
lib,
modulesPath,
...
}:
let
make-ext4-fs = import (modulesPath + "/../lib/make-ext4-fs.nix");
in
{
system.build.ext4 = make-ext4-fs {
inherit pkgs lib;
inherit (pkgs)
e2fsprogs
libfaketime
perl
fakeroot
zstd
;
storePaths = [ config.system.build.toplevel ];
compressImage = false;
volumeLabel = "fc-dev-rootfs";
populateImageCommands = ''
# System profile symlink — the only way to find the system closure.
# process_api spawns /nix/var/nix/profiles/system/init to start systemd,
# which then runs NixOS activation (creates /etc, /tmp, etc.).
mkdir -p ./files/nix/var/nix/profiles
ln -s ${config.system.build.toplevel} ./files/nix/var/nix/profiles/system

# NixOS activation checks for this marker.
mkdir -p ./files/etc
touch ./files/etc/NIXOS
'';
};
}
Loading