Install scripts are JSON objects with the following structure. Scripts can use various macros (template variables) that are dynamically replaced during processing.
::: tip Source of Truth
Install scripts live in the hexos-app-catalog repository. See Contributing for how to submit new scripts.
:::
version (required): Schema version. Must be 4 or 5. Versions 1-3 are deprecated. Use 5 if your script includes lifecycle hooks; otherwise 4 is fine.custom (optional): Set to true for community/custom apps that aren't in the default TrueNAS cataloginternal (optional): Set to true for dev/test apps — hidden in productionmetadata (required if custom: true): Custom app metadata (see Custom App Metadata)script (required): Metadata about the install script itself
version (required): Semantic version of this install script (e.g., "1.0.0", "2.1.3")updateCompatibility (optional): Semver range expression defining which script versions can update to this version (e.g., ">=1.0.0" allows updates from any version 1.0.0 or higher, "^2.0.0" allows updates from 2.x.x versions). Supports all semver range syntax including >=, >, <, <=, ^, ~, and complex ranges like ">=1.0.0 <3.0.0"changeLog (optional): Description of changes in this version of the scriptrequirements (required): System requirements that are validated before installationinstallation_questions (optional): Array of questions to ask the user during installationensure_directories_exists (optional): Array of directory entry objects to create before installation, with optional ownership and snapshot declarationsapp_values (required): Configuration object passed directly to TrueNAS APIhooks (optional, V5 only): Array of lifecycle hook declarations. See Hooks ReferenceInstall scripts support various macros that are replaced dynamically during script processing:
$SERVER_LAN_IP: Server's LAN IP address$SERVER_HOST_ID: Server's unique host ID$LOCATION(locationId): Resolves to configured location path$RANDOM_STRING(length): Generates random alphanumeric string$MEMORY(percentage, minimum_mb): Calculates memory allocation$HOST_PATH(path): Creates host path configuration object$MOUNTED_HOST_PATH(path, mount_point): Creates mounted host path configuration$APP_INSTALLED(appName): Returns "true" or "false" if app is installed$QUESTION(key): References user's response to installation question$IF(condition, trueValue, [falseValue]): Conditional logic with support for:
true, false!condition$APP_INSTALLED(appName)$QUESTION(key)value1 == value2value1 != value2$IF(["condition1", "condition2"], trueValue, falseValue, "AND")$IF(["condition1", "condition2"], trueValue, falseValue, "OR")For detailed macro documentation and examples, see the Macros Reference.
When custom: true, the metadata object is required:
| Property | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Display name in the HexOS app store |
description |
string | Yes | Brief description of what the app does |
icon |
string | Yes | URL to the app's icon (SVG or PNG) |
version |
string | Yes | Semantic version of the custom app |
For complete, working examples of install scripts, browse the hexos-app-catalog repository. These production-ready scripts demonstrate best practices and real-world usage patterns for popular applications like Plex, Jellyfin, Immich, and more.
The requirements object defines system requirements that HexOS validates before allowing app installation. This ensures users have properly configured their system with the necessary locations, resources, and permissions.
Requirements Object Properties:
locations (array): Required folder locations that must be configured in HexOS Settingsspecifications (array): Minimum hardware/resource specifications neededpermissions (array): Special permissions the app needsports (array): Network ports that must be availableLocations are folder paths configured in HexOS Settings → Locations. Each location maps to a specific use case:
Available Locations:
ApplicationsPerformance: High-performance storage for app data (typically SSD)ApplicationsCapacity: High-capacity storage for app data (typically HDD)Media: General media filesPhotos: Photo library storageMusic: Music library storageMovies: Movie library storageShows: TV show library storageVideos: Video library storageDownloads: Download directoryDocuments: Document storageBackups: Backup storageImportant: Every $LOCATION() macro used anywhere in your install script (in ensure_directories_exists, app_values, etc.) must be listed in the locations requirements array.
Example:
{
"requirements": {
"locations": ["ApplicationsPerformance", "Photos", "Media"]
},
"ensure_directories_exists": [
{ "path": "$LOCATION(ApplicationsPerformance)/immich/config", "owner": { "user": "apps" } },
{ "path": "$LOCATION(Photos)/immich", "owner": { "user": "apps" } }
],
"app_values": {
"storage": {
"config": "$HOST_PATH($LOCATION(ApplicationsPerformance)/immich/config)",
"photos": "$HOST_PATH($LOCATION(Photos)/immich)",
"additional_storage": [
"$MOUNTED_HOST_PATH($LOCATION(Media), /media)"
]
}
}
}
Hardware and resource specifications ensure the system meets minimum requirements:
Available Specifications:
100MB, 200MB, 500MB, 1GB, 2GB: Minimum storage space needed1CORE, 2CORE, 4CORE, 8CORE: Minimum CPU cores requiredGPU: Requires GPU hardware (for transcoding, ML, etc.)Example:
{
"requirements": {
"specifications": ["2CORE", "200MB", "GPU"]
}
}
Special permissions that the app requires to function:
Available Permissions:
READ_WRITE_LOCATIONS: App needs read/write access to configured locationsExample:
{
"requirements": {
"permissions": ["READ_WRITE_LOCATIONS"]
}
}
Network ports that the application will use. HexOS can validate port availability before installation.
Example:
{
"requirements": {
"ports": [8080, 8443]
}
}
{
"version": 4,
"requirements": {
"locations": [
"ApplicationsPerformance",
"ApplicationsCapacity",
"Media",
"Photos",
"Music",
"Movies",
"Shows",
"Videos"
],
"specifications": ["2CORE", "200MB", "GPU"],
"permissions": ["READ_WRITE_LOCATIONS"],
"ports": [32400]
}
}
When users attempt to install an app, HexOS performs the following checks:
Location Validation: Verifies that all required locations are configured in Settings → Locations
ensure_directories_exists will create subdirectories within configured locationsSpecifications Check: Validates system meets minimum hardware requirements (coming soon)
Permissions Check: Confirms the app has necessary permissions (coming soon)
Port Availability: Validates required ports are available (coming soon)
Important: The ensure_directories_exists section of your install script will only create subdirectories and files. It does not create the base location paths themselves. Users must configure these locations in HexOS Settings first, and your requirements validation ensures this happens before installation begins.
Installation questions allow you to prompt users for configuration values during app installation. Question responses can be referenced in app_values using the $QUESTION(key) syntax.

Example of installation questions displayed during app installation
Question Object Properties:
question (required): The question text shown to the userdescription (optional): Additional help text explaining the questionplaceholder (optional): Placeholder text for input fieldstype (required): Question type - one of:
"text": Text input field"number": Numeric input field"select": Dropdown/selection with predefined options"boolean": True/false togglekey (required): Unique identifier used to reference the answer with $QUESTION(key)options (required for select type): Array of option objects with text and value propertiesrequired (optional): Whether the question must be answered (default: false)default (optional): Default value or special syntax like $RANDOM_STRING(16)Using Question Responses:
Reference question responses in your app_values using the $QUESTION(key) syntax:
{
"installation_questions": [
{
"question": "Web Port",
"type": "number",
"key": "web_port",
"default": 8080
}
],
"app_values": {
"network": {
"web_port": {
"port_number": "$QUESTION(web_port)"
}
}
}
}
Using Questions in Conditionals:
Question responses can be used in conditional logic with the $IF macro. See the $IF macro documentation for examples of using questions in conditional expressions.
Each entry in ensure_directories_exists is an object with the following properties:
path (required): Directory path, typically using $LOCATION() macrosnetwork_share (optional): Boolean, whether to expose as a network shareowner (optional): Object specifying the TrueNAS user and group that should own this directory
user (required): TrueNAS username (e.g., "apps", "netdata")group (optional): TrueNAS group name (e.g., "docker"). If omitted, uses the user's default groupsnapshot (optional): Object with an id field. When present, HexOS snapshots this dataset before app updates so support can assist with restoring your application and data if something goes wrong
id (required): Identifier included in the snapshot name and metadata (e.g., "db", "config")Example:
{
"ensure_directories_exists": [
{ "path": "$LOCATION(Photos)", "network_share": true },
{ "path": "$LOCATION(ApplicationsPerformance)", "network_share": true },
{ "path": "$LOCATION(Photos)/immich", "owner": { "user": "apps" }, "snapshot": { "id": "data" } },
{ "path": "$LOCATION(ApplicationsPerformance)/immich/postgres_data", "owner": { "user": "netdata", "group": "docker" }, "snapshot": { "id": "db" } },
{ "path": "$LOCATION(ApplicationsPerformance)/immich/config", "owner": { "user": "apps" }, "snapshot": { "id": "config" } }
]
}
How owner works:
user.get_user_obj and optionally group.get_group_obj on the TrueNAS system to resolve usernames and group names to numeric uid/gidapp.update completes, HexOS verifies and repairs ownership on declared paths if TrueNAS changed itaclmode: PASSTHROUGH, snapshots the dataset first as a rollback point, then applies the canonical ACL with the declared uid/gid/mnt/pool/location/app/data) — location roots are never modifiedowner are created with default permissions and not tracked for repairHow snapshot works:
snapshot config so support can assist with restoring your application and data if something goes wronghexos-app-{appId}-{id}-{timestamp} and stamped with metadata: hexos:purpose, hexos:app, hexos:snapshot_id, hexos:pathThis object is passed directly to TrueNAS's app installation API. The structure varies by application and corresponds to the app's configuration schema in the TrueNAS apps repository. For example, you can see Plex's schema for the storage property here.
Install scripts support conditional logic to customize app configuration based on:
$APP_INSTALLED(appName)$QUESTION(key)$IF macro