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/apply.sh
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# apply.sh
#
# Apply safe, reversible user-space memory-reclamation knobs from a plan JSON.
# Defaults to dry-run; --apply is required to mutate the system.
#
# Usage:
# apply.sh --plan plan.json [--apply] [--reboot] [--drop-caches]
#
# --plan PATH JSON output from plan.sh (or '-' for stdin).
# --apply Actually run the commands. Without this flag, only print them.
# --reboot After applying, reboot the system. Off by default.
# --drop-caches After applying (and only if not rebooting), flush the kernel
# page cache so freed memory shows up in `free -h` immediately.
# Off by default.
#
# This script is limited to safe user-space changes and does not:
# - Touch /boot/extlinux/extlinux.conf
# - Modify the device tree or carveouts
# - Trigger a re-flash
# - Apply recommendations not filtered as "safety": "safe"
#
# Anything in those classes is out of scope for this device-runtime script.
#
# Exit codes:
# 0 ok
# 2 not on a Jetson, or plan JSON malformed
set -uo pipefail
PLAN=""
APPLY=0
DO_REBOOT=0
DROP_CACHES=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
--plan) need_value "$@"; PLAN="$2"; shift 2 ;;
--apply) APPLY=1; shift ;;
--reboot) DO_REBOOT=1; shift ;;
--drop-caches) DROP_CACHES=1; shift ;;
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) echo "unknown flag: $1" >&2; exit 64 ;;
esac
done
if [ -z "$PLAN" ]; then
echo "ERROR: --plan is required (path to plan 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"
if ! command -v python3 >/dev/null 2>&1; then
echo "ERROR: python3 is required to parse the plan." >&2
exit 2
fi
if [ "$PLAN" = "-" ]; then
PLAN_JSON=$(cat) || { echo "ERROR: failed to read plan JSON from stdin" >&2; exit 2; }
else
[ -r "$PLAN" ] || { echo "ERROR: cannot read plan file: $PLAN" >&2; exit 64; }
PLAN_JSON=$(cat "$PLAN") || { echo "ERROR: failed to read plan file: $PLAN" >&2; exit 2; }
fi
SAFE_LINES=$(printf '%s' "$PLAN_JSON" | python3 -c '
import json, sys
try:
plan = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"ERROR: malformed plan JSON: {e}", file=sys.stderr)
sys.exit(2)
if not isinstance(plan, dict):
print("ERROR: malformed plan JSON: top-level value must be an object", file=sys.stderr)
sys.exit(2)
recommendations = plan.get("recommendations", [])
if not isinstance(recommendations, list):
print("ERROR: malformed plan JSON: recommendations must be an array", file=sys.stderr)
sys.exit(2)
for r in recommendations:
if not isinstance(r, dict): continue
if r.get("safety") != "safe": continue
cmd = r.get("command")
if not cmd: continue
knob = r.get("knob", "?")
savings = r.get("estimated_savings_mb", 0)
print(f"{cmd}\t{knob}\t{savings}\tsafe")
') || exit 2
run_allowed_cmd() {
local arg
case "$1" in
sudo\ systemctl\ set-default\ *)
arg="${1#sudo systemctl set-default }"
case "$arg" in ''|*[!A-Za-z0-9_.@-]*) return 64 ;; esac
sudo systemctl set-default "$arg"
;;
sudo\ systemctl\ disable\ --now\ *)
arg="${1#sudo systemctl disable --now }"
case "$arg" in ''|*[!A-Za-z0-9_.@-]*) return 64 ;; esac
sudo systemctl disable --now "$arg"
;;
sudo\ systemctl\ enable\ --now\ *)
arg="${1#sudo systemctl enable --now }"
case "$arg" in ''|*[!A-Za-z0-9_.@-]*) return 64 ;; esac
sudo systemctl enable --now "$arg"
;;
*)
return 64
;;
esac
}
if [ -z "$SAFE_LINES" ]; then
echo "Nothing to apply: plan contains no knobs with safety=safe."
exit 0
fi
echo "Plan contains the following safe knobs:"
echo "$SAFE_LINES" | awk -F'\t' '{ printf " - %-32s ~%s MB %s\n", $2, $3, $1 }'
echo
if [ "$APPLY" -ne 1 ]; then
echo "DRY RUN. Re-run with --apply to execute the commands above."
exit 0
fi
echo "About to apply $(echo "$SAFE_LINES" | wc -l) commands as root."
echo "Press Ctrl-C within 5 seconds to abort."
sleep 5
echo "$SAFE_LINES" | while IFS=$'\t' read -r cmd knob savings safety; do
if [ "$safety" != "safe" ]; then
echo "WARN: skipping recommendation without safety=safe for knob '$knob'." >&2
continue
fi
echo "+ $cmd # $knob (~${savings} MB)"
if run_allowed_cmd "$cmd"; then
continue
else
rc=$?
fi
if [ "$rc" -eq 64 ]; then
echo "WARN: skipping unsupported command for knob '$knob': $cmd" >&2
else
echo "WARN: command failed for knob '$knob'. Continuing." >&2
fi
done
echo
if [ "$DO_REBOOT" -eq 1 ]; then
echo "Rebooting in 5 seconds..."
sleep 5
sudo systemctl reboot
else
echo "Done. Some changes (e.g. default systemd target) only take effect after reboot."
if [ "$DROP_CACHES" -eq 1 ]; then
echo
echo "Flushing page cache so freed memory shows up immediately..."
sudo sync && sudo sysctl -w vm.drop_caches=3 >/dev/null
echo "Done. Run jetson-memory-audit/scripts/audit.sh to verify the delta."
else
echo "Tip: pass --drop-caches to flush reclaimable page cache so 'free -h' reflects the new baseline."
fi
fi
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