Jetson Headless Mode
NVIDIAOfficialCreated by Jetson TeamDistributed by NVIDIA
- Skill
- Workflow
- Cloud and DevOps
- Apache-2.0
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured, reversible workflow to transition NVIDIA Jetson devices into a headless state. By analyzing current system state via memory audit data, it identifies and disables non-essential daemons and the graphical desktop environment. The process emphasizes safety through dry-run defaults, explicit user approval, and documented reversal commands, ensuring that critical services like camera pipelines and power management remain intact.
What it does
- Generates a memory reclamation plan based on live system audit data.
- Switches the systemd default target from graphical to multi-user.
- Disables non-essential background services like Bluetooth, printing, and crash reporting.
- Provides a dry-run mechanism to preview changes before application.
- Includes built-in reversal commands for every applied change.
- Calculates estimated memory savings based on the specific device configuration.
When to use it
- When preparing a Jetson device for deployment as an edge inference appliance.
- When you need to maximize available system memory by removing GUI overhead.
- After running a memory audit that confirms the device is running unnecessary display managers.
- When you have confirmed the user does not require local display output or a desktop environment.
Advantages
- High-yield memory reclamation with minimal risk.
- Fully reversible changes ensure system state can be restored.
- Uses live device data for accurate, context-aware savings estimates.
- Prevents accidental disabling of critical system services like nvargus-daemon.
- Safe-by-default execution model requires explicit user confirmation for mutations.
Usage examples
- “How much memory can I save by putting my Jetson in headless mode?”
- The agent runs the planning script against your current audit data and reports the estimated total savings in MB, along with a list of recommended services to disable.
- “Apply headless mode to my Jetson device.”
- The agent performs a dry run of the headless configuration, displays the proposed changes for your approval, and then executes the changes if authorized.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/nvidia/jetson-headless-mode/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip jetson-headless-mode.zip -d .agents/skills
Documentation by Jetson Team
Jetson Headless Mode
Plan-then-apply for safe, reversible user-space memory reclamation: switch the default systemd target away from graphical.target and disable a curated set of non-essential daemons. This is the highest-yield, lowest-risk memory win on Jetson.
Purpose
Build a user-approved headless-mode plan from live audit data, then apply only safe, reversible user-space changes that reduce desktop and daemon memory use on Jetson.
When to use
"Free as much memory as possible — I don't need the GUI."
"I'm shipping this Jetson as an inference appliance / edge node."
After
jetson-memory-auditshowsdefault_systemd_target=graphical.targetor showsgdm3/lightdm/sddmactive on a system the user describes as headless.
When NOT to use
The user needs the local desktop, display output, kiosk UI, or any X/Wayland session. In that case, do not recommend disabling the graphical target or display manager; use
jetson-memory-auditfor a read-only view and suggest non-GUI memory options instead.You do not have current audit data. Run
jetson-memory-auditfirst, or ask the user for its output, before proposing changes or estimating savings.
Use live device data as the source of truth. Jetson family, SKU/variant, memory totals, active display services, and savings estimates must come from jetson-diagnostic/scripts/detect_jetson.sh, audit.json, or a fresh jetson-memory-audit run. If a value is not available, say it is unknown instead of guessing. The savings numbers below are upper bounds; the real delta is whatever a before/after audit reports.
Prerequisites
Start from a current
jetson-memory-auditJSON snapshot.Confirm the user does not need the local desktop, display output, kiosk UI, or X/Wayland session.
Mutating changes require
sudoand explicit user approval; dry-run first unless approval was already given in the same prompt.Run on the Jetson host or in a host-visible sandbox with access to systemd state.
Available Scripts
| Script | Purpose | Arguments |
|---|---|---|
scripts/plan.sh | Reads a memory audit JSON and emits a plan containing safe, reversible recommendations. | --audit PATH or --audit -, plus --human. |
scripts/apply.sh | Prints or applies the safe commands from a plan JSON. Dry-run by default. | --plan PATH or --plan -, --apply, --reboot, --drop-caches. |
If your agent runtime supports run_script, use it to run scripts/plan.sh and scripts/apply.sh and summarize the returned output. Otherwise run the scripts with bash from the repository root.
Instructions
Run
scripts/plan.shto readaudit.json(fromjetson-memory-audit) and emit a plan with onlysafety: safeknobs (target switch, display managers, audio, print, modem, etc.).Show the plan to the user and confirm.
Run
scripts/apply.sh --plan plan.jsonfor a dry run. Re-run with--applyto execute. Add--drop-cachesto flush the page cache afterward, or--rebootto take effect immediately.Re-run
jetson-memory-audit/scripts/audit.shto verify the actual delta.
Expected workflow
Use the scripts for estimates and application so recommendations are based on the current device state rather than the static upper-bound table alone.
For "what would headless save", "estimate", "plan", or production planning prompts, run
scripts/plan.sh --audit <audit.json>and reportestimated_total_savings_mb, the toprecommendations[*].knob, and whether any display manager orgraphical.targetis active. Do not runapply.sh.For prompts where the user explicitly says to apply headless mode now, run
scripts/apply.sh --plan <plan.json>once as a dry run first. If the user has already approved mutation in the same prompt, re-run the same command with--applyand mention the reversible command(s).If direct execution fails in an agent runtime, invoke scripts with
bash {baseDir}/scripts/<script-name> .... Do not try tochmodinstalled skill files.
Plan / apply contract
plan.shemits the same JSON shape asjetson-inference-mem-tune/scripts/recommend.py: an array ofrecommendationswith{layer, knob, estimated_savings_mb, safety, command, reversible_command, rationale}.apply.shfilters entries tosafety == "safe"with a non-emptycommand, then re-checks the filtered safety marker in the shell loop before execution. Anything else, such as kernel command-line changes, device-tree changes, or accuracy tradeoffs, is out of scope for this skill.Default mode is dry-run.
--applyis required to mutate the system.
Knobs covered
| Knob | Action | Estimated savings | Reversible? |
|---|---|---|---|
disable-graphical-target | systemctl set-default multi-user.target | up to 865 MB | yes |
stop-gdm3 / gdm / lightdm / sddm / display-manager | systemctl disable --now <svc> | ~200 MB / svc | yes |
stop-pulseaudio | disable audio daemon | ~8 MB | yes |
stop-bluetooth | disable Bluetooth stack | ~6 MB | yes |
stop-ModemManager | disable WWAN manager | ~4 MB | yes |
stop-cups / stop-cups-browsed | disable print stack | ~5 / ~3 MB | yes |
stop-snapd | disable Snap daemon | ~30 MB | yes |
stop-whoopsie / kerneloops | disable crash reporters | ~4 / ~2 MB | yes |
stop-avahi-daemon | disable mDNS | ~3 MB | yes |
stop-unattended-upgrades / packagekit | disable background package work | ~6 / ~8 MB | yes |
Do NOT disable these services
nvargus-daemon— required for any libargus camera pipeline.nvgetty.service— serial console; disabling can lock you out of recovery.nvpmodel— power-mode service; required for clock/power tuning.containerd/docker— leave on if you run containers (most inference workloads do).nvfb/nvdisplay-related kernel services — tied to boot-time display configuration, so this skill does not change them.
Safety
Does not edit
/boot/extlinux/extlinux.conf, the device tree, or boot-time memory reservations.Does not disable services it does not have an explicit entry for (no blanket "disable everything not whitelisted").
Every applied change has a documented
reversible_command. Re-running the plan with the reverts is sufficient to restore.Dry-run by default.
--applyis the only way to mutate.Report only device facts and savings figures that came from live detection or audit output.
Cross-platform behavior
The same set of knobs applies to every Jetson family in the matrix above. The script reads JETSON_GENERATION / JETSON_PRODUCT_LINE / JETSON_VARIANT from jetson-diagnostic/scripts/detect_jetson.sh (and still exports legacy JETSON_SKU) so the agent can attribute the savings correctly in its summary, but it does not branch on product line.
Files
- ATTRIBUTION.mdAdded by AutoGineer923 B
- BENCHMARK.md3.2 KiB
- LICENSELicence file added by AutoGineer11.2 KiB
- SKILL.md7.5 KiB
- evals/evals.json6.4 KiB
- scripts/apply.shScript5.7 KiB
- scripts/plan.shScript5.1 KiB
- skill-card.md3.6 KiB
- skill.oms.sig4.9 KiB
scripts/plan.sh
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# plan.sh
#
# Build a "headless mode" plan from a jetson-memory-audit JSON. Emits the same
# JSON shape as recommend.py: an array of {layer, knob, estimated_savings_mb,
# safety, command, reversible_command, rationale, reference, notes}.
#
# Only safety="safe" entries are produced; apply.sh refuses to run anything else.
#
# Usage:
# plan.sh --audit PATH # PATH to audit JSON, or '-' for stdin
# plan.sh --audit - --human # pretty-print
#
# Exit codes:
# 0 ok
# 2 audit JSON missing or malformed
set -uo pipefail
AUDIT=""
HUMAN=0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILLS_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# shellcheck source=../../jetson-diagnostic/scripts/common.sh
# shellcheck disable=SC1091
. "$SKILLS_ROOT/jetson-diagnostic/scripts/common.sh"
while [ $# -gt 0 ]; do
case "$1" in
--audit) need_value "$@"; AUDIT="$2"; shift 2 ;;
--human) HUMAN=1; shift ;;
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) echo "unknown flag: $1" >&2; exit 64 ;;
esac
done
if [ -z "$AUDIT" ]; then
echo "ERROR: --audit is required (path to audit JSON, or '-')" >&2
exit 64
fi
# shellcheck source=../../jetson-diagnostic/scripts/detect_jetson.sh
# shellcheck disable=SC1091
. "$SKILLS_ROOT/jetson-diagnostic/scripts/detect_jetson.sh"
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
if [ "$AUDIT" = "-" ]; then
cat > "$TMP" || { echo "ERROR: failed to read audit JSON from stdin" >&2; exit 2; }
else
[ -r "$AUDIT" ] || { echo "ERROR: cannot read audit file: $AUDIT" >&2; exit 2; }
cp "$AUDIT" "$TMP" || { echo "ERROR: failed to read audit file: $AUDIT" >&2; exit 2; }
fi
PRETTY="$HUMAN" AUDIT_PATH="$TMP" python3 <<'PY'
import json, os, sys
try:
with open(os.environ["AUDIT_PATH"], "r", encoding="utf-8") as f:
audit = json.load(f)
except (OSError, json.JSONDecodeError) as e:
print(f"ERROR: failed to read audit JSON: {e}", file=sys.stderr)
sys.exit(2)
target = audit.get("default_systemd_target", "unknown")
services = audit.get("candidate_services", {})
def is_active(svc):
return services.get(svc, {}).get("active") == "active"
REF = "SKILL.md"
recs = []
if target == "graphical.target":
recs.append({
"layer": 1,
"knob": "disable-graphical-target",
"estimated_savings_mb": 865,
"safety": "safe",
"rationale": "Default boot target is graphical.target. Switching to multi-user.target stops the display manager and Xorg/Wayland stack on next boot.",
"reference": REF,
"command": "sudo systemctl set-default multi-user.target",
"reversible_command": "sudo systemctl set-default graphical.target",
"notes": ["Reboot required to fully reclaim memory."],
})
for svc in ("gdm3", "gdm", "lightdm", "sddm", "display-manager"):
if is_active(svc):
recs.append({
"layer": 1,
"knob": f"stop-{svc}",
"estimated_savings_mb": 200,
"safety": "safe",
"rationale": f"Display manager {svc} is active; not needed in headless deployments.",
"reference": REF,
"command": f"sudo systemctl disable --now {svc}",
"reversible_command": f"sudo systemctl enable --now {svc}",
"notes": [],
})
aux = {
"pulseaudio": ("Audio daemon; disable if no audio I/O is needed.", 8),
"bluetooth": ("Bluetooth stack; disable on wired-only deployments.", 6),
"ModemManager": ("Cellular modem manager; disable if no WWAN.", 4),
"cups": ("Print server; almost never needed on edge.", 5),
"cups-browsed": ("CUPS browse helper; same as above.", 3),
"snapd": ("Snap package daemon; disable if you don't ship snaps.", 30),
"whoopsie": ("Ubuntu crash reporter; disable on production.", 4),
"kerneloops": ("Kernel oops reporter; disable on production.", 2),
"avahi-daemon": ("mDNS responder; disable if not used.", 3),
"unattended-upgrades": ("Auto-update daemon; disable on locked production builds.", 6),
"packagekit": ("Background package indexer; disable on production.", 8),
}
for svc, (reason, mb) in aux.items():
if is_active(svc):
recs.append({
"layer": 2,
"knob": f"stop-{svc}",
"estimated_savings_mb": mb,
"safety": "safe",
"rationale": reason,
"reference": REF,
"command": f"sudo systemctl disable --now {svc}",
"reversible_command": f"sudo systemctl enable --now {svc}",
"notes": [],
})
out = {
"sku": audit.get("sku", "unknown"),
"variant": audit.get("variant", "unknown"),
"mem_total_gb": audit.get("mem_total_gb", 0),
"l4t_version": audit.get("l4t_version", "unknown"),
"product_model": audit.get("product_model", ""),
"use_case": "headless",
"estimated_total_savings_mb": sum(max(r["estimated_savings_mb"], 0) for r in recs),
"recommendations": recs,
}
indent = 2 if os.environ.get("PRETTY") == "1" else None
json.dump(out, sys.stdout, indent=indent)
sys.stdout.write("\n")
PY
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Jetson Team
- Publisher
- NVIDIA (Official)
- Distributed by
- NVIDIA
- Source
- NVIDIA/skills · skills/jetson-headless-mode · commit fce3cca
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE-APACHE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Jetson Team or NVIDIA.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download jetson-headless-mode.zip