+
+The agent requires a hardware TPM 2.0 on NixOS.
+The Debian and RPM packages fall back to a software TPM on hosts without one,
+but the helper scripts that set that up are not part of the nixpkgs package,
+so a host with no /dev/tpmrm0 cannot enroll yet.
+
+
+
+1. If you track a stable NixOS channel, add an overlay so that `pkgs.step-agent` resolves.
+ Skip this step on `nixos-unstable`.
+
+ ```nix
+ nixpkgs.overlays = [
+ (final: prev: {
+ inherit
+ (import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz") {
+ system = prev.stdenv.hostPlatform.system;
+ config.allowUnfreePredicate = pkg: prev.lib.getName pkg == "step-agent";
+ })
+ step-agent
+ ;
+ })
+ ];
+ ```
+
+2. Save the following as `step-agent.nix` alongside your `configuration.nix`,
+ and add `./step-agent.nix` to its `imports` list:
+
+ ```nix
+ { config, lib, pkgs, ... }:
+
+ {
+ # The step-agent package is unfree.
+ nixpkgs.config.allowUnfreePredicate = pkg: lib.getName pkg == "step-agent";
+
+ environment.systemPackages = [ pkgs.step-agent ];
+
+ # The agent talks to the TPM from user space, so it needs read/write access
+ # to the TPM resource manager. This creates the tss group and the udev rules
+ # that grant that group /dev/tpmrm0.
+ security.tpm2.enable = true;
+
+ users.groups.step-agent = { };
+ users.users.step-agent = {
+ isSystemUser = true;
+ group = "step-agent";
+ home = "/var/lib/step-agent";
+ extraGroups = [ "tss" ];
+ };
+
+ # /run/step-agent holds the PKCS#11 and SSH sockets that other users connect
+ # to. Create it with tmpfiles rather than systemd's RuntimeDirectory=, which
+ # deletes the directory every time the service stops.
+ systemd.tmpfiles.rules = [
+ "d /run/step-agent 0755 step-agent step-agent - -"
+ ];
+
+ systemd.services.step-agent = {
+ description = "Smallstep Agent";
+ documentation = [ "https://u.step.sm/docs/agent" ];
+ after = [ "network-online.target" ];
+ requires = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ # The agent starts once the device is registered and agent.yaml exists.
+ unitConfig.ConditionPathIsReadWrite = "/etc/step-agent/agent.yaml";
+
+ environment = {
+ HOME = "/var/lib/step-agent";
+ RUNTIME_DIRECTORY = "/run/step-agent";
+ };
+
+ serviceConfig = {
+ Type = "notify";
+ WatchdogSec = "60s";
+ ExecStart = "${lib.getExe pkgs.step-agent} start";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ User = "step-agent";
+ Group = "step-agent";
+ ConfigurationDirectory = "step-agent";
+ StateDirectory = "step-agent";
+ Restart = "always";
+ RestartSec = 10;
+
+ ProtectSystem = true;
+ ProtectHome = "read-only";
+ PrivateTmp = true;
+ SecureBits = "keep-caps";
+ AmbientCapabilities = [ "CAP_IPC_LOCK" "CAP_CHOWN" "CAP_DAC_OVERRIDE" "CAP_FOWNER" ];
+ CapabilityBoundingSet = [ "CAP_SYSLOG" "CAP_IPC_LOCK" "CAP_CHOWN" "CAP_DAC_OVERRIDE" "CAP_FOWNER" ];
+ DeviceAllow = [ "/dev/tpmrm0 rw" ];
+ ReadWritePaths = [ "-/dev/tpmrm0" ];
+
+ LimitNOFILE = 65536;
+ LimitMEMLOCK = "infinity";
+ };
+ };
+
+ # Restart the agent when its configuration changes, such as after registering.
+ systemd.paths.step-agent-restart = {
+ wantedBy = [ "multi-user.target" ];
+ pathConfig.PathChanged = "/etc/step-agent/agent.yaml";
+ };
+
+ systemd.services.step-agent-restart.serviceConfig = {
+ Type = "oneshot";
+ ExecStart = "${config.systemd.package}/bin/systemctl restart step-agent.service";
+ };
+
+ # Let the agent restart units and manage NetworkManager connections.
+ security.polkit.enable = true;
+ security.polkit.extraConfig = ''
+ polkit.addRule(function(action, subject) {
+ if (subject.user == "step-agent") {
+ if (action.id == "org.freedesktop.systemd1.manage-units") {
+ return polkit.Result.YES;
+ }
+ if (action.id.indexOf("org.freedesktop.NetworkManager.") == 0) {
+ return polkit.Result.YES;
+ }
+ }
+ });
+ '';
+
+ # Publish the agent's PKCS#11 server to p11-kit clients. The module path has
+ # to be explicit: the agent searches FHS locations that do not exist on NixOS.
+ environment.etc."pkcs11/modules/step-agent.module".text = ''
+ module: ${pkgs.p11-kit}/lib/pkcs11/p11-kit-client.so
+ server-address: unix:path=/run/step-agent/step-agent-pkcs11.sock
+ '';
+ }
+ ```
+
+3. Rebuild your system:
+
+ ```bash
+ sudo nixos-rebuild switch
+ ```
+
+4. Register the device with your team:
+
+ ```bash
+ sudo step-agent register [team name]
+ ```
+
+ Registration writes `agent.yaml` into `/etc/step-agent`,
+ which systemd creates and keeps writable through `ConfigurationDirectory=`.
+ Do not manage `agent.yaml` with `environment.etc`:
+ that produces a read-only symlink into the Nix store, and the service refuses to start.
+
+5. Check that it was installed correctly:
+
+ ```bash
+ step-agent version
+ ```
+
+ Output:
+
+ ```bash
+ step-agent/0.67.3 (linux/amd64)
+ Release Date: 2026-05-19 15:50 UTC
+ ```
+
+
## Registering and approving endpoints
### Self-registration
@@ -327,6 +499,8 @@ sudo systemctl enable --now step-agent
sudo systemctl enable --now step-agent-restart.path
```
+On NixOS, `nixos-rebuild switch` enables and starts both units, so skip this step.
+
If you get any errors, check the agent’s status:
```bash
@@ -360,6 +534,9 @@ modutil -dbdir ~/.pki/nssdb -add step-agent \
Export `P11_KIT_SERVER_ADDRESS` and point it at the running agent's socket **before** you run `modutil`. `modutil` loads and initializes the module as it adds it, so running it first fails with a load error and can leave a broken module registered in the NSS database. If you hit this, see [Recovering from a failed `modutil` add](./troubleshooting-agent.mdx#recovering-from-a-failed-modutil-add).
+On NixOS, `p11-kit-client.so` lives in the Nix store rather than under `/usr/lib`.
+Point `modutil` at it with `-libfile $(nix eval --raw nixpkgs#p11-kit)/lib/pkcs11/p11-kit-client.so`.
+
Next, start Chrome from the command line.
In Chrome, you should now have access to certificates managed by Smallstep.
@@ -384,6 +561,8 @@ To uninstall the Smallstep Agent from a Linux system:
sudo apt-get remove step-agent
```
+ **For NixOS:** remove `./step-agent.nix` from your `imports` list, then run `sudo nixos-rebuild switch`.
+
2. Optionally, remove configuration and certificate files:
```bash
diff --git a/platform/troubleshooting-agent.mdx b/platform/troubleshooting-agent.mdx
index 6c3db595..4ea7ff27 100644
--- a/platform/troubleshooting-agent.mdx
+++ b/platform/troubleshooting-agent.mdx
@@ -1,5 +1,5 @@
---
-updated_at: February 03, 2026
+updated_at: July 27, 2026
title: Troubleshooting Guide
html_title: Smallstep Troubleshooting Guide
description: Troubleshoot Smallstep Device Identity issues. Diagnose platform, MDM, and endpoint problems with step-by-step guidance.
@@ -593,6 +593,11 @@ Quick reference for platform-specific commands and file locations.
| Collect logs | `step-agent logs collect --log-dir /var/lib/step-agent/logs` |
| Agent configuration | `/etc/step-agent/agent.yaml` |
+On NixOS these paths are the same, with two differences.
+There is no `/usr/bin/step-agent`: the binary is a Nix store path on your `PATH`.
+And `step-agent doctor` reports p11-kit as missing unless the client module is registered by its store path,
+as shown in [the NixOS install instructions](./smallstep-agent.mdx#nixos).
+