Skip to content

Fleet Device Management

Fleet is an open-source device management platform that uses osquery to provide visibility and control over your device fleet. In Ghaf, Fleet Orbit is integrated to enable centralized device management, monitoring, and remote script execution.

The Fleet integration in Ghaf provides:

  • Device Visibility: Query device state, installed software, and system configuration
  • Remote Management: Execute scripts and commands on managed devices
  • Policy Enforcement: Define and monitor compliance policies
  • Fleet Desktop: User-facing tray application for device status

Fleet Orbit runs in the GUI VM and reports to a central Fleet server using a dynamic hostname derived from the device hardware, ensuring consistent identification across reboots.

┌─────────────────────────────────────────────────────────────┐
│ Ghaf Host │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ghaf-dynamic-hostname service │ │
│ │ └─► /persist/common/ghaf/hostname │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ (virtiofs) │
└───────────────────────────┼─────────────────────────────────┘
┌───────────────────────────┼─────────────────────────────────┐
│ GUI VM │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /etc/common/ghaf/hostname │ │
│ └──────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼──────────────────────────────┐ │
│ │ orbit.service │ │
│ │ └─► ORBIT_HOSTNAME_FILE=/etc/common/ghaf/hostname │ │
│ │ └─► Reports to Fleet Server │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ fleet-desktop.service (user) │ │
│ │ └─► Tray icon for device status │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Fleet is configured in the GUI VM via services.orbit. The following example shows the default configuration:

services.orbit = {
enable = true;
fleetUrl = "https://your-fleet.example.com";
enrollSecret = "your-enroll-secret"; # Use enrollSecretPath for production
hostnameFile = "/etc/common/ghaf/hostname"; # Dynamic hostname from host
enableScripts = true; # Allow remote script execution
};
Option Environment Variable Description
enable Enable Fleet Orbit systemd service
fleetUrl ORBIT_FLEET_URL Base URL of the Fleet server
enrollSecret ORBIT_ENROLL_SECRET Enroll secret for Fleet server
enrollSecretPath ORBIT_ENROLL_SECRET_PATH Path to enroll secret file (recommended for production)
fleetCertificate ORBIT_FLEET_CERTIFICATE Path to Fleet server certificate chain
hostnameFile ORBIT_HOSTNAME_FILE Path to file containing hostname (Ghaf-specific)
hostIdentifier ORBIT_HOST_IDENTIFIER Host identifier mode: uuid or instance
enableScripts ORBIT_ENABLE_SCRIPTS Enable remote script execution
debug ORBIT_DEBUG Enable debug logging
insecure ORBIT_INSECURE Disable TLS certificate verification

For production deployments, use enrollSecretPath with a secrets management solution like sops-nix:

services.orbit = {
enable = true;
fleetUrl = "https://fleet.example.com";
enrollSecretPath = config.sops.secrets.fleet-enroll-secret.path;
hostnameFile = "/etc/common/ghaf/hostname";
};
sops.secrets.fleet-enroll-secret = {
sopsFile = ./secrets.yaml;
};
For CI/dev images, keep secrets out of the image and inject the enroll secret at
runtime. Ghaf uses a shared host path for dynamic hostname; you can reuse the
same mechanism for the enroll secret (for example, `/etc/common/ghaf/fleet/enroll`
inside the guest).

Ghaf uses hardware-derived hostnames for device identification. The ghaf-dynamic-hostname service on the host generates a unique hostname based on hardware identifiers (DMI serial, disk UUID, or MAC address) and writes it to /persist/common/ghaf/hostname.

This file is shared with VMs via virtiofs and mounted at /etc/common/ghaf/hostname. The Fleet module is patched to read the hostname from this file via the hostnameFile option, ensuring devices are consistently identified on the Fleet server.

The orbit service includes a ConditionPathExists directive to wait for the hostname file and, when enrollSecretPath is used, to enroll the secret file before starting.

Two systemd services are created:

  • orbit.service (system): Runs the Orbit agent, connecting to the Fleet server
  • fleet-desktop.service (user): Runs Fleet Desktop tray application in graphical sessions

Orbit logs are written to:

  • /var/log/orbit/orbit.log — Main Orbit log
  • /var/log/orbit/osquery/ — osquery result and status logs
  • journalctl -u orbit — Systemd journal

The Ghaf Fleet integration includes patches for NixOS compatibility:

  • Disabled auto-updates: NixOS manages packages declaratively; Orbit’s auto-update is disabled
  • Disabled keystore: Secrets are not stored in OS-specific keystores
  • Fixed paths: osqueryd binary path is set from Nix store
  • Shebang patching: Remote scripts have shebangs patched to use NixOS paths

Check if the hostname file exists:

Terminal window
ls -la /etc/common/ghaf/hostname

If you use enrollSecretPath, also verify the secret file is present:

Terminal window
ls -la /etc/common/ghaf/fleet/enroll

Check service status:

Terminal window
systemctl status orbit
journalctl -u orbit -f

Enable debug logging:

services.orbit = {
debug = true;
# For testing only:
# insecure = true;
};

Verify scripts are enabled and check logs:

Terminal window
cat /var/log/orbit/orbit.log | grep -i script