Skip to content

Deploying Packages

Deploy plain Nix packages to machines as user-level tools with the packages installable type.

The packages type deploys via nix profile add (nix profile install when Lix is detected), with no rollback and no activation script. It is ideal for standalone tools and editor configurations on machines that already have a system configuration; attrpath resolution and the activation/rollback skip are defined by the preset, see Output Types.

Packages are always user-level: set user to install into a specific user’s profile (activation runs via su -l <user> -c); when user is empty, the package installs as the SSH user. See Configuration Model for the canonical user field semantics.

Export derivations under packages.<system>.<name>:

{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs }:
let
systems = [ "x86_64-linux" "aarch64-linux" ];
forAllSystems = nixpkgs.lib.genAttrs systems;
in {
packages = forAllSystems (system:
let pkgs = nixpkgs.legacyPackages.${system}; in {
default = pkgs.hello;
my-tool = pkgs.callPackage ./my-tool.nix { };
}
);
};
}

The attribute name in your panix YAML must match packages.<system>.<name> in the flake.

fleet:
flakes:
my-tools:
url: path:../my-tools-flake
installables:
packages:
my-tool: # Must match packages.<system>.my-tool
user: alice # Install into alice's nix profile
machines:
workstation:
ssh:
hostname: ws.lan
username: root # SSH as root, activate as alice

A common pattern is deploying the system config as root and user tools as the target user, by defining both installable types under one flake. See Configuration Model for that layout.

Packages-specific nix flags (profile_add_default_flags, profile_add_env) follow the same inheritance and override semantics as other nix flags.

nixvim configures Neovim declaratively and produces a wrapped derivation. It does not export a pre-built package, so evaluate your config and export the result from your flake’s packages output:

packages.${system}.default = nixvim.lib.nixvim.modules.buildNixvimWith {
inherit system;
modules = [{ plugins.treesitter.enable = true; }];
};

The panix YAML is the same as any packages installable. For module-based integration with generation rollback and XDG config management, deploy nixvim through a homeConfigurations installable using nixvim.homeModules.nixvim.

nvf is the successor to neovim-flake. Evaluate configs via nvf.lib.neovimConfiguration and use .neovim as the package:

packages.${system}.default =
(nvf.lib.neovimConfiguration {
inherit pkgs;
modules = [ ./nvf-config.nix ];
}).neovim;

The panix YAML is identical to the nixvim case.

nix-maid manages declarative dotfiles and systemd user units through a bundle activated via bin/activate (systemd-tmpfiles + sd-switch), rather than nix profile add. Build a bundle under a maidConfigurations output using its callable flake API:

maidConfigurations.my-config = nix-maid pkgs {
file.home.".gitconfig".text = "[user]\n name = Example";
packages = [ pkgs.hello ];
};
fleet:
flakes:
my-infra:
installables:
maidConfigurations:
my-config:
user: myuser
machines:
workstation:
  • No rollback for packages and maidConfigurations: Panix rollback is skipped (no profile path). For packages, use nix profile history and nix profile rollback on the target machine directly.
  • No activation modes: both types activate immediately, no switch, boot, test, or dry-activate modes.
  • Profile conflicts (packages only): nix profile add uses the user’s default profile. Use profile_add_default_flags: ["--priority", "N"] to control priority.