diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts index d2dbf9fa..188e8863 100644 --- a/docs/.vitepress/sidebar.ts +++ b/docs/.vitepress/sidebar.ts @@ -155,6 +155,7 @@ const sidebar: DefaultTheme.SidebarItem[] = [ collapsed: true, items: [ // auto-generated-release-notes-start + { text: '2026-05-20', link: '/release-notes/command-deck/2026-05-20' }, { text: '2026-05-13', link: '/release-notes/command-deck/2026-05-13' }, { text: '2026-04-21', link: '/release-notes/command-deck/2026-04-21' }, { text: '2026-03-27', link: '/release-notes/command-deck/2026-03-27' }, diff --git a/docs/features/apps/install-scripts/advanced/debugging.md b/docs/features/apps/install-scripts/advanced/debugging.md index 812a7ca1..a8e6efd4 100644 --- a/docs/features/apps/install-scripts/advanced/debugging.md +++ b/docs/features/apps/install-scripts/advanced/debugging.md @@ -27,8 +27,8 @@ If an install scripts fails, this will help: #### Permission Errors - **Symptom**: App fails to start, logs show permission denied errors -- **Solution**: Add appropriate entries to `ensure_permissions_exists` in your install script -- **Example**: PostgreSQL requires specific user/group permissions +- **Solution**: Add the `owner` field to the relevant entry in `ensure_directories_exists` with the correct TrueNAS user and group (e.g., `{ "user": "netdata", "group": "docker" }`) +- **Example**: PostgreSQL data directories typically require `"owner": { "user": "netdata", "group": "docker" }` #### Missing Directories - **Symptom**: App fails during installation, "directory not found" errors diff --git a/docs/features/apps/install-scripts/curated/index.md b/docs/features/apps/install-scripts/curated/index.md index cb671c50..cf88607a 100644 --- a/docs/features/apps/install-scripts/curated/index.md +++ b/docs/features/apps/install-scripts/curated/index.md @@ -3,21 +3,27 @@ | App | Download | Size | Last Modified | |---|---|---:|---| -| `bazarr` | [bazarr.json](/install-scripts/bazarr.json) | 1.3 KB | 2026-03-15 | -| `drawio` | [drawio.json](/install-scripts/drawio.json) | 757 B | 2026-03-15 | -| `emby` | [emby.json](/install-scripts/emby.json) | 2.2 KB | 2026-03-15 | -| `handbrake` | [handbrake.json](/install-scripts/handbrake.json) | 1.8 KB | 2026-03-15 | -| `home-assistant` | [home-assistant.json](/install-scripts/home-assistant.json) | 1.6 KB | 2026-03-19 | -| `immich` | [immich.json](/install-scripts/immich.json) | 1.7 KB | 2026-03-15 | -| `jellyfin` | [jellyfin.json](/install-scripts/jellyfin.json) | 2.2 KB | 2026-03-15 | -| `lidarr` | [lidarr.json](/install-scripts/lidarr.json) | 1.4 KB | 2026-03-15 | -| `nextcloud` | [nextcloud.json](/install-scripts/nextcloud.json) | 3.5 KB | 2026-03-15 | -| `peanut` | [peanut.json](/install-scripts/peanut.json) | 834 B | 2026-03-15 | -| `plex` | [plex.json](/install-scripts/plex.json) | 3.4 KB | 2025-12-12 | -| `prowlarr` | [prowlarr.json](/install-scripts/prowlarr.json) | 766 B | 2025-12-04 | -| `qbittorrent` | [qbittorrent.json](/install-scripts/qbittorrent.json) | 1.0 KB | 2025-12-04 | -| `radarr` | [radarr.json](/install-scripts/radarr.json) | 1.2 KB | 2025-12-04 | -| `scrutiny` | [scrutiny.json](/install-scripts/scrutiny.json) | 1.3 KB | 2026-03-15 | -| `sonarr` | [sonarr.json](/install-scripts/sonarr.json) | 1.2 KB | 2025-12-04 | -| `syncthing` | [syncthing.json](/install-scripts/syncthing.json) | 2.5 KB | 2026-05-05 | +| `bazarr` | [bazarr.json](/install-scripts/bazarr.json) | 1.4 KB | 2026-05-15 | +| `blinko` | [blinko.json](/install-scripts/blinko.json) | 1.3 KB | 2026-05-20 | +| `dozzle` | [dozzle.json](/install-scripts/dozzle.json) | 614 B | 2026-05-18 | +| `drawio` | [drawio.json](/install-scripts/drawio.json) | 634 B | 2026-05-15 | +| `emby` | [emby.json](/install-scripts/emby.json) | 2.3 KB | 2026-05-15 | +| `excalidraw` | [excalidraw.json](/install-scripts/excalidraw.json) | 614 B | 2026-05-18 | +| `handbrake` | [handbrake.json](/install-scripts/handbrake.json) | 1.9 KB | 2026-05-15 | +| `home-assistant` | [home-assistant.json](/install-scripts/home-assistant.json) | 1.4 KB | 2026-05-15 | +| `immich` | [immich.json](/install-scripts/immich.json) | 1.6 KB | 2026-05-15 | +| `jellyfin` | [jellyfin.json](/install-scripts/jellyfin.json) | 2.3 KB | 2026-05-15 | +| `lidarr` | [lidarr.json](/install-scripts/lidarr.json) | 1.4 KB | 2026-05-15 | +| `lubelogger` | [lubelogger.json](/install-scripts/lubelogger.json) | 1.5 KB | 2026-05-18 | +| `navidrome` | [navidrome.json](/install-scripts/navidrome.json) | 5.1 KB | 2026-05-18 | +| `nextcloud` | [nextcloud.json](/install-scripts/nextcloud.json) | 3.4 KB | 2026-05-20 | +| `peanut` | [peanut.json](/install-scripts/peanut.json) | 911 B | 2026-05-15 | +| `plex` | [plex.json](/install-scripts/plex.json) | 3.4 KB | 2026-05-15 | +| `portracker` | [portracker.json](/install-scripts/portracker.json) | 894 B | 2026-05-18 | +| `prowlarr` | [prowlarr.json](/install-scripts/prowlarr.json) | 781 B | 2026-05-15 | +| `qbittorrent` | [qbittorrent.json](/install-scripts/qbittorrent.json) | 1.0 KB | 2026-05-15 | +| `radarr` | [radarr.json](/install-scripts/radarr.json) | 1.3 KB | 2026-05-15 | +| `scrutiny` | [scrutiny.json](/install-scripts/scrutiny.json) | 1.4 KB | 2026-05-15 | +| `sonarr` | [sonarr.json](/install-scripts/sonarr.json) | 1.3 KB | 2026-05-15 | +| `syncthing` | [syncthing.json](/install-scripts/syncthing.json) | 2.6 KB | 2026-05-15 | diff --git a/docs/features/apps/install-scripts/overview.md b/docs/features/apps/install-scripts/overview.md index 868655e2..ce75fcbd 100644 --- a/docs/features/apps/install-scripts/overview.md +++ b/docs/features/apps/install-scripts/overview.md @@ -49,10 +49,12 @@ For apps not yet curated or when you need to customize the configuration: ### Best Practices and Common Pitfalls #### Best Practices +- **Use V4 format** — all new install scripts must use `"version": 4` - **Always use `$LOCATION()` macros** for paths instead of hardcoded paths - **Use `$HOST_PATH()` and `$MOUNTED_HOST_PATH()`** for storage configuration instead of manual object creation -- **Include necessary directories** in `ensure_directories_exists` - assume no directories exist -- **Set proper permissions** with `ensure_permissions_exists` for apps that require specific user/group access +- **All directory entries must be objects** — bare strings in `ensure_directories_exists` are no longer supported in V4 +- **Declare ownership** with the `owner` field for apps that require specific user/group ownership (e.g., `"postgres"`, `"apps"`) +- **Add `snapshot` config** on data and config directories to enable automatic pre-update ZFS snapshots - **Use `$MEMORY()` for dynamic memory allocation** to ensure apps work across different system configurations - **Reference TrueNAS app schemas** from the [official apps repository](https://github.com/truenas/apps) for `app_values` structure diff --git a/docs/features/apps/install-scripts/reference/schema.md b/docs/features/apps/install-scripts/reference/schema.md index 73daf237..b7ea6dbd 100644 --- a/docs/features/apps/install-scripts/reference/schema.md +++ b/docs/features/apps/install-scripts/reference/schema.md @@ -4,15 +4,14 @@ Install scripts are JSON objects with the following structure. Scripts can use v ## Root Properties -- **`version`** (required): Schema version. Must be `3` or higher (currently latest supported version). +- **`version`** (required): Schema version. Must be `4` (current required version). Versions 1-3 are deprecated. - **`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](https://www.npmjs.com/package/semver#ranges) including `>=`, `>`, `<`, `<=`, `^`, `~`, and complex ranges like `">=1.0.0 <3.0.0"` - **`changeLog`** (optional): Description of changes in this version of the script - **`requirements`** (required): System requirements that are validated before installation - **`installation_questions`** (optional): Array of questions to ask the user during installation -- **`ensure_directories_exists`** (optional): Array of directories to create before installation -- **`ensure_permissions_exists`** (optional): Array of permission modifications for specific paths +- **`ensure_directories_exists`** (optional): Array of directory entry objects to create before installation, with optional ownership and snapshot declarations - **`app_values`** (required): Configuration object passed directly to TrueNAS API ## Available Macros @@ -83,8 +82,8 @@ Locations are folder paths configured in HexOS Settings → Locations. Each loca "locations": ["ApplicationsPerformance", "Photos", "Media"] }, "ensure_directories_exists": [ - "$LOCATION(ApplicationsPerformance)/immich/config", - "$LOCATION(Photos)/immich" + { "path": "$LOCATION(ApplicationsPerformance)/immich/config", "owner": { "user": "apps" } }, + { "path": "$LOCATION(Photos)/immich", "owner": { "user": "apps" } } ], "app_values": { "storage": { @@ -149,7 +148,7 @@ Network ports that the application will use. HexOS can validate port availabilit ```json { - "version": 2, + "version": 4, "requirements": { "locations": [ "ApplicationsPerformance", @@ -234,19 +233,43 @@ Reference question responses in your `app_values` using the `$QUESTION(key)` syn Question responses can be used in conditional logic with the `$IF` macro. See the [$IF macro documentation](/features/apps/install-scripts/reference/macros#if-condition-truevalue-falsevalue) for examples of using questions in conditional expressions. -## Directory Creation -- **String format**: Simple path string -- **Object format**: - - `path`: Directory path (required) - - `network_share`: Boolean, whether to expose as network share - - `posix`: Boolean, whether to use POSIX permissions - -## Permission Management -Required for apps that need specific user/group permissions (like PostgreSQL). -- `path`: Directory path to modify -- `username`: User to grant access to -- `access`: Access level ("read", "write", etc.) -- `posix`: Object with additional POSIX settings (e.g., `groupname`) +## Directory Creation, Ownership, and Snapshots + +Each entry in `ensure_directories_exists` is an object with the following properties: + +- `path` (required): Directory path, typically using `$LOCATION()` macros +- `network_share` (optional): Boolean, whether to expose as a network share +- `owner` (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 group +- `snapshot` (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:** +```json +{ + "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:** +- HexOS calls `user.get_user_obj` and optionally `group.get_group_obj` on the TrueNAS system to resolve usernames and group names to numeric uid/gid +- After `app.update` completes, HexOS verifies and repairs ownership on declared paths if TrueNAS changed it +- If a path has a POSIX1E ACL (legacy), HexOS automatically migrates it to NFS4 with `aclmode: PASSTHROUGH`, snapshots the dataset first as a rollback point, then applies the canonical ACL with the declared uid/gid +- Only applies to app-specific paths (4+ path segments, e.g., `/mnt/pool/location/app/data`) — location roots are never modified +- Paths without `owner` are created with default permissions and not tracked for repair + +**How `snapshot` works:** +- Before any app update, HexOS snapshots each dataset with a `snapshot` config so support can assist with restoring your application and data if something goes wrong +- Snapshots are named `hexos-app-{appId}-{id}-{timestamp}` and stamped with metadata: `hexos:purpose`, `hexos:app`, `hexos:snapshot_id`, `hexos:path` +- Only the latest 3 snapshots per app per dataset are kept — older snapshots are automatically pruned after each new snapshot +- Only applies to app-specific paths (4+ path segments) — location roots are never snapshotted ## App Values This 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](https://github.com/truenas/apps). For example, you can see Plex's schema for the `storage` property [here](https://github.com/truenas/apps/blob/1d2a6e9811f9af2ceae6529cc094a432a7da4e96/trains/stable/plex/app_versions.json#L422). diff --git a/docs/public/install-scripts/bazarr.json b/docs/public/install-scripts/bazarr.json index bea44573..df200379 100644 --- a/docs/public/install-scripts/bazarr.json +++ b/docs/public/install-scripts/bazarr.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -37,7 +37,10 @@ "path": "$LOCATION(Shows)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/bazarr/config" + { + "path": "$LOCATION(ApplicationsPerformance)/bazarr/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { @@ -61,4 +64,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/blinko.json b/docs/public/install-scripts/blinko.json new file mode 100644 index 00000000..00ca08e7 --- /dev/null +++ b/docs/public/install-scripts/blinko.json @@ -0,0 +1,48 @@ +{ + "version": 4, + "script": { + "version": "1.0.0", + "changeLog": "Initial" + }, + "requirements": { + "locations": ["ApplicationsPerformance", "ApplicationsCapacity"], + "specifications": ["2CORE", "256MB"], + "permissions": ["READ_WRITE_LOCATIONS"], + "ports": [30412] + }, + "ensure_directories_exists": [ + { + "path": "$LOCATION(ApplicationsPerformance)", + "network_share": true + }, + { + "path": "$LOCATION(ApplicationsCapacity)", + "network_share": true + }, + { "path": "$LOCATION(ApplicationsCapacity)/blinko/data", "owner": { "user": "apps" }, "snapshot": { "id": "data" } }, + { "path": "$LOCATION(ApplicationsPerformance)/blinko/postgres_data", "owner": { "user": "netdata", "group": "docker" }, "snapshot": { "id": "db" } } + ], + "app_values": { + "blinko": { + "db_password": "$RANDOM_STRING(7)", + "nextauth_secret": "$RANDOM_STRING(7)", + "additional_envs": [] + }, + "network": { + "web_port": { + "bind_mode": "published", + "port_number": 30412 + } + }, + "storage": { + "data": "$HOST_PATH($LOCATION(ApplicationsCapacity)/blinko/data)", + "postgres_data": "$HOST_PATH($LOCATION(ApplicationsPerformance)/blinko/postgres_data)" + }, + "resources": { + "limits": { + "cpus": 2, + "memory": "$MEMORY(10%, 4096)" + } + } + } +} \ No newline at end of file diff --git a/docs/public/install-scripts/dozzle.json b/docs/public/install-scripts/dozzle.json new file mode 100644 index 00000000..b1dc57b1 --- /dev/null +++ b/docs/public/install-scripts/dozzle.json @@ -0,0 +1,28 @@ +{ + "version": 4, + "script": { + "version": "1.0.0", + "changeLog": "initial" + }, + "requirements": { + "locations": [], + "specifications": ["2CORE", "1024MB"], + "permissions": [], + "ports": [30064] + }, + "ensure_directories_exists": [], + "app_values": { + "network": { + "web_port": { + "bind_mode": "published", + "port_number": 30064 + } + }, + "resources": { + "limits": { + "cpus": 2, + "memory": "$MEMORY(5%, 1024)" + } + } + } +} \ No newline at end of file diff --git a/docs/public/install-scripts/drawio.json b/docs/public/install-scripts/drawio.json index 3e66e472..4cfd27e5 100644 --- a/docs/public/install-scripts/drawio.json +++ b/docs/public/install-scripts/drawio.json @@ -1,32 +1,32 @@ { - "version": 3, - "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": 4, + "script": { + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" + }, + "requirements": { + "locations": [], + "specifications": ["2CORE", "1024MB"], + "permissions": [], + "ports": [30090, 30091] + }, + "ensure_directories_exists": [], + "app_values": { + "network": { + "http_port": { + "bind_mode": "published", + "port_number": 30090 + }, + "https_port": { + "bind_mode": "published", + "port_number": 30091 + } }, - "requirements": { - "locations": [], - "specifications": ["2CORE", "1024MB"], - "permissions": [], - "ports": [30090, 30091] - }, - "ensure_directories_exists": [], - "app_values": { - "network": { - "http_port": { - "bind_mode": "published", - "port_number": 30090 - }, - "https_port": { - "bind_mode": "published", - "port_number": 30091 - } - }, - "resources": { - "limits": { - "cpus": 2, - "memory": "$MEMORY(5%, 1024)" - } - } + "resources": { + "limits": { + "cpus": 2, + "memory": "$MEMORY(5%, 1024)" + } } -} \ No newline at end of file + } +} diff --git a/docs/public/install-scripts/emby.json b/docs/public/install-scripts/emby.json index ae8bb0f3..04e84b7b 100644 --- a/docs/public/install-scripts/emby.json +++ b/docs/public/install-scripts/emby.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.2", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "ApplicationsCapacity", "Media", "Photos", "Music", "Movies", "Shows", "Videos"], @@ -43,9 +43,9 @@ "path": "$LOCATION(Videos)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/emby/config", - "$LOCATION(ApplicationsPerformance)/emby/cache", - "$LOCATION(ApplicationsPerformance)/emby/transcode" + { "path": "$LOCATION(ApplicationsPerformance)/emby/config", "snapshot": { "id": "config" } }, + { "path": "$LOCATION(ApplicationsPerformance)/emby/cache" }, + { "path": "$LOCATION(ApplicationsPerformance)/emby/transcode" } ], "app_values": { "storage": { @@ -79,4 +79,4 @@ "gpus": "$GPU_CONFIG()" } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/excalidraw.json b/docs/public/install-scripts/excalidraw.json new file mode 100644 index 00000000..85abbb10 --- /dev/null +++ b/docs/public/install-scripts/excalidraw.json @@ -0,0 +1,28 @@ +{ + "version": 4, + "script": { + "version": "1.0.0", + "changeLog": "initial" + }, + "requirements": { + "locations": [], + "specifications": ["2CORE", "1024MB"], + "permissions": [], + "ports": [30255] + }, + "ensure_directories_exists": [], + "app_values": { + "network": { + "web_port": { + "bind_mode": "published", + "port_number": 30255 + } + }, + "resources": { + "limits": { + "cpus": 2, + "memory": "$MEMORY(5%, 1024)" + } + } + } +} \ No newline at end of file diff --git a/docs/public/install-scripts/handbrake.json b/docs/public/install-scripts/handbrake.json index c0910391..9913aa4e 100644 --- a/docs/public/install-scripts/handbrake.json +++ b/docs/public/install-scripts/handbrake.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -44,8 +44,13 @@ "path": "$LOCATION(Videos)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/handbrake/config", - "$LOCATION(Media)/HandBrake/Output" + { + "path": "$LOCATION(ApplicationsPerformance)/handbrake/config", + "snapshot": { "id": "config" } + }, + { + "path": "$LOCATION(Media)/HandBrake/Output" + } ], "app_values": { "storage": { @@ -77,4 +82,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/home-assistant.json b/docs/public/install-scripts/home-assistant.json index 478fbfb8..303ff2ad 100644 --- a/docs/public/install-scripts/home-assistant.json +++ b/docs/public/install-scripts/home-assistant.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated dataset locations and set config to posix" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "Media"], @@ -19,25 +19,9 @@ "path": "$LOCATION(Media)", "network_share": true }, - { - "path": "$LOCATION(ApplicationsPerformance)/home-assistant/config", - "posix": true - }, - { - "path": "$LOCATION(ApplicationsPerformance)/home-assistant/postgres_data", - "posix": true - }, - "$LOCATION(Media)/home-assistant" - ], - "ensure_permissions_exists": [ - { - "path": "$LOCATION(ApplicationsPerformance)/home-assistant/postgres_data", - "username": "netdata", - "access": "read", - "posix": { - "groupname": "docker" - } - } + { "path": "$LOCATION(ApplicationsPerformance)/home-assistant/config", "owner": { "user": "apps" }, "snapshot": { "id": "config" } }, + { "path": "$LOCATION(ApplicationsPerformance)/home-assistant/postgres_data", "owner": { "user": "netdata", "group": "docker" }, "snapshot": { "id": "db" } }, + { "path": "$LOCATION(Media)/home-assistant" } ], "app_values": { "home_assistant": { @@ -61,4 +45,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/immich.json b/docs/public/install-scripts/immich.json index 81646b45..6f06878d 100644 --- a/docs/public/install-scripts/immich.json +++ b/docs/public/install-scripts/immich.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "Photos"], @@ -19,21 +19,8 @@ "path": "$LOCATION(Photos)", "network_share": true }, - "$LOCATION(Photos)/immich", - { - "path": "$LOCATION(ApplicationsPerformance)/immich/postgres_data", - "posix": true - } - ], - "ensure_permissions_exists": [ - { - "path": "$LOCATION(ApplicationsPerformance)/immich/postgres_data", - "username": "netdata", - "access": "read", - "posix": { - "groupname": "docker" - } - } + { "path": "$LOCATION(Photos)/immich", "owner": { "user": "apps" }, "snapshot": { "id": "data" } }, + { "path": "$LOCATION(ApplicationsPerformance)/immich/postgres_data", "owner": { "user": "netdata", "group": "docker" }, "snapshot": { "id": "db" } } ], "app_values": { "release_name": "immich", @@ -68,4 +55,4 @@ "gpus": "$GPU_CONFIG()" } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/jellyfin.json b/docs/public/install-scripts/jellyfin.json index 7658b0a2..65f43b00 100644 --- a/docs/public/install-scripts/jellyfin.json +++ b/docs/public/install-scripts/jellyfin.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "ApplicationsCapacity", "Media", "Photos", "Music", "Movies", "Shows", "Videos"], @@ -43,9 +43,9 @@ "path": "$LOCATION(Videos)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/jellyfin/config", - "$LOCATION(ApplicationsCapacity)/jellyfin/cache", - "$LOCATION(ApplicationsPerformance)/jellyfin/transcodes" + { "path": "$LOCATION(ApplicationsPerformance)/jellyfin/config", "snapshot": { "id": "config" } }, + { "path": "$LOCATION(ApplicationsCapacity)/jellyfin/cache" }, + { "path": "$LOCATION(ApplicationsPerformance)/jellyfin/transcodes" } ], "app_values": { "storage": { @@ -79,4 +79,4 @@ "gpus": "$GPU_CONFIG()" } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/lidarr.json b/docs/public/install-scripts/lidarr.json index da1f48ff..7b20e650 100644 --- a/docs/public/install-scripts/lidarr.json +++ b/docs/public/install-scripts/lidarr.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -37,8 +37,13 @@ "path": "$LOCATION(Music)", "network_share": true }, - "$LOCATION(Downloads)/qbittorrent", - "$LOCATION(ApplicationsPerformance)/lidarr/config" + { + "path": "$LOCATION(Downloads)/qbittorrent" + }, + { + "path": "$LOCATION(ApplicationsPerformance)/lidarr/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { @@ -61,4 +66,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/lubelogger.json b/docs/public/install-scripts/lubelogger.json new file mode 100644 index 00000000..74faebf2 --- /dev/null +++ b/docs/public/install-scripts/lubelogger.json @@ -0,0 +1,48 @@ +{ + "version": 4, + "script": { + "version": "1.0.0", + "changeLog": "Initial Script" + }, + "requirements": { + "locations": ["ApplicationsPerformance", "ApplicationsCapacity"], + "specifications": ["2CORE", "256MB"], + "permissions": ["READ_WRITE_LOCATIONS"], + "ports": [30350] + }, + "ensure_directories_exists": [ + { + "path": "$LOCATION(ApplicationsPerformance)", + "network_share": true + }, + { + "path": "$LOCATION(ApplicationsCapacity)", + "network_share": true + }, + { "path": "$LOCATION(ApplicationsPerformance)/lubelogger/postgres", "owner": { "user": "netdata", "group": "docker" }, "snapshot": { "id": "db" } }, + { "path": "$LOCATION(ApplicationsCapacity)/lubelogger/data", "owner": { "user": "apps" }, "snapshot": { "id": "data" } }, + { "path": "$LOCATION(ApplicationsPerformance)/lubelogger/keys", "owner": { "user": "apps" }, "snapshot": { "id": "keys" } } + ], + "app_values": { + "lubelogger": { + "db_password": "$RANDOM_STRING(7)" + }, + "network": { + "web_port": { + "bind_mode": "published", + "port_number": 30350 + } + }, + "storage": { + "data": "$HOST_PATH($LOCATION(ApplicationsCapacity)/lubelogger/data)", + "protection_keys": "$HOST_PATH($LOCATION(ApplicationsPerformance)/lubelogger/keys)", + "postgres_data": "$HOST_PATH($LOCATION(ApplicationsPerformance)/lubelogger/postgres)" + }, + "resources": { + "limits": { + "cpus": 2, + "memory": "$MEMORY(5%, 1024)" + } + } + } +} \ No newline at end of file diff --git a/docs/public/install-scripts/navidrome.json b/docs/public/install-scripts/navidrome.json new file mode 100644 index 00000000..09008fef --- /dev/null +++ b/docs/public/install-scripts/navidrome.json @@ -0,0 +1,228 @@ +{ + "version": 4, + "script": { + "version": "1.0.0", + "changeLog": "Initial Install Script" + }, + "installation_questions": [ + { + "question": "Select a welcome message (Optional)", + "description": "This message is shown at the login screen of Navidrome", + "type": "text", + "key": "welcome_message", + "required": false, + "default": "Welcome to Navidrome" + }, + { + "question": "Select the default language", + "description": "If the wrong language is showing you may have to clear your browser cookies.", + "type": "select", + "key": "default_language", + "required": true, + "options": [ + { + "text": "English", + "value": "en" + }, + { + "text": "Arabic (العربية)", + "value": "ar" + }, + { + "text": "Bulgarian (Български)", + "value": "bg" + }, + { + "text": "Bosnian (Bosanski)", + "value": "bs" + }, + { + "text": "Catalan (Català)", + "value": "ca" + }, + { + "text": "Czech (Čeština)", + "value": "cs" + }, + { + "text": "Danish (Dansk)", + "value": "da" + }, + { + "text": "German (Deutsch)", + "value": "de" + }, + { + "text": "Greek (Ελληνικά)", + "value": "el" + }, + { + "text": "Esperanto", + "value": "eo" + }, + { + "text": "Spanish (Español)", + "value": "es" + }, + { + "text": "Euskara", + "value": "eu" + }, + { + "text": "Persian (فارسی)", + "value": "fa" + }, + { + "text": "Finnish (Suomi)", + "value": "fi" + }, + { + "text": "French (Français)", + "value": "fr" + }, + { + "text": "Galician (Galego)", + "value": "gl" + }, + { + "text": "Hindi (हिंदी)", + "value": "hi" + }, + { + "text": "Hungarian (Magyar)", + "value": "hu" + }, + { + "text": "Indonesian (Bahasa Indonesia)", + "value": "id" + }, + { + "text": "Italian (Italiano)", + "value": "it" + }, + { + "text": "Japanese (日本語)", + "value": "ja" + }, + { + "text": "Korean (한국어)", + "value": "ko" + }, + { + "text": "Nederlands", + "value": "nl" + }, + { + "text": "Norwegian (Norsk)", + "value": "no" + }, + { + "text": "Polish (Polski)", + "value": "pl" + }, + { + "text": "brazilian Portuguese (Português (Brasil))", + "value": "pt-br" + }, + { + "text": "Russian (Русский)", + "value": "ru" + }, + { + "text": "Slovak (Slovenčina)", + "value": "sk" + }, + { + "text": "slovenian (Slovenščina)", + "value": "sl" + }, + { + "text": "Serbian (српски)", + "value": "sr" + }, + { + "text": "Swedish (Svenska)", + "value": "sv" + }, + { + "text": "Thai (ไทย)", + "value": "th" + }, + { + "text": "Turkish (Türkçe)", + "value": "tr" + }, + { + "text": "Ukrainian (Українська)", + "value": "uk" + }, + { + "text": "Simplified Chinese (简体中文)", + "value": "zh-Hans" + }, + { + "text": "Traditional Chinese (繁體中文)", + "value": "zh-Hant" + } + ], + "default": "en" + } + ], + "requirements": { + "locations": [ + "ApplicationsPerformance", + "Media", + "Music" + ], + "specifications": [ + "2CORE", "1024MB"], + "permissions": [ + "READ_WRITE_LOCATIONS" + ], + "ports": [30043] + }, + "ensure_directories_exists": [ + { + "path": "$LOCATION(ApplicationsPerformance)", + "network_share": true + }, + { + "path": "$LOCATION(Media)", + "network_share": true + }, + { + "path": "$LOCATION(Music)", + "network_share": true + }, + { "path": "$LOCATION(ApplicationsPerformance)/navidrome/data", "owner": { "user": "apps" }, "snapshot": { "id": "config" } } + ], + "app_values": { + "navidrome": { + "welcome_message": "$QUESTION(welcome_message)", + "additional_envs": [ + { + "name": "ND_DEFAULTLANGUAGE", + "value": "$QUESTION(default_language)" + } + ] + }, + "storage": { + "data": "$HOST_PATH($LOCATION(ApplicationsPerformance)/navidrome/data)", + "music": "$HOST_PATH($LOCATION(Music))", + "additional_storage": [ + "$MOUNTED_HOST_PATH($LOCATION(Media), /Media)" + ] + }, + "network": { + "web_port": { + "port_number": 30043 + } + }, + "resources": { + "limits": { + "cpus": 4, + "memory": "$MEMORY(10%, 4096)" + } + } + } +} \ No newline at end of file diff --git a/docs/public/install-scripts/nextcloud.json b/docs/public/install-scripts/nextcloud.json index fb80ba89..7c335e7d 100644 --- a/docs/public/install-scripts/nextcloud.json +++ b/docs/public/install-scripts/nextcloud.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -47,26 +47,13 @@ "path": "$LOCATION(ApplicationsPerformance)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/nextcloud/html", - { - "path": "$LOCATION(ApplicationsPerformance)/nextcloud/postgres_data", - "posix": true - }, + { "path": "$LOCATION(ApplicationsPerformance)/nextcloud/html", "owner": { "user": "www-data" }, "snapshot": { "id": "html" } }, + { "path": "$LOCATION(ApplicationsPerformance)/nextcloud/postgres_data", "owner": { "user": "netdata", "group": "docker" }, "snapshot": { "id": "db" } }, { "path": "$LOCATION(ApplicationsCapacity)", "network_share": true }, - "$LOCATION(ApplicationsCapacity)/nextcloud/data" - ], - "ensure_permissions_exists": [ - { - "path": "$LOCATION(ApplicationsPerformance)/nextcloud/postgres_data", - "username": "netdata", - "access": "read", - "posix": { - "groupname": "docker" - } - } + { "path": "$LOCATION(ApplicationsCapacity)/nextcloud/data", "snapshot": { "id": "data" } } ], "app_values": { "nextcloud": { @@ -132,4 +119,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/peanut.json b/docs/public/install-scripts/peanut.json index 4782fadd..e47b917e 100644 --- a/docs/public/install-scripts/peanut.json +++ b/docs/public/install-scripts/peanut.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -22,7 +22,10 @@ "path": "$LOCATION(ApplicationsPerformance)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/peanut/config" + { + "path": "$LOCATION(ApplicationsPerformance)/peanut/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { @@ -41,4 +44,4 @@ "memory": "$MEMORY(5%, 1024)" } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/plex.json b/docs/public/install-scripts/plex.json index de8879d0..3cba5050 100644 --- a/docs/public/install-scripts/plex.json +++ b/docs/public/install-scripts/plex.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.0", - "changeLog": "Upgraded Plex install script to be compatible with latest HexOS script changes" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "installation_questions": [ { @@ -72,13 +72,10 @@ "path": "$LOCATION(Videos)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/plex/data", - { - "path": "$LOCATION(ApplicationsPerformance)/plex/config", - "posix": true - }, - "$LOCATION(ApplicationsCapacity)/plex/logs", - "$LOCATION(ApplicationsPerformance)/plex/transcode" + { "path": "$LOCATION(ApplicationsPerformance)/plex/data", "owner": { "user": "apps" }, "snapshot": { "id": "data" } }, + { "path": "$LOCATION(ApplicationsPerformance)/plex/config", "owner": { "user": "apps" }, "snapshot": { "id": "config" } }, + { "path": "$LOCATION(ApplicationsCapacity)/plex/logs" }, + { "path": "$LOCATION(ApplicationsPerformance)/plex/transcode" } ], "app_values": { "plex": { @@ -117,4 +114,4 @@ "gpus": "$GPU_CONFIG()" } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/portracker.json b/docs/public/install-scripts/portracker.json new file mode 100644 index 00000000..c3ea6673 --- /dev/null +++ b/docs/public/install-scripts/portracker.json @@ -0,0 +1,44 @@ +{ + "version": 4, + "script": { + "version": "1.0.0", + "changeLog": "Initial" + }, + "requirements": { + "locations": [ + "ApplicationsPerformance" + ], + "specifications": [ + "2CORE", + "1024MB" + ], + "permissions": [ + "READ_WRITE_LOCATIONS" + ], + "ports": [30233] + }, + "ensure_directories_exists": [ + { + "path": "$LOCATION(ApplicationsPerformance)", + "network_share": true + }, + { "path": "$LOCATION(ApplicationsPerformance)/portracker/data", "owner": { "user": "apps" }, "snapshot": { "id": "data" } } + ], + "app_values": { + "storage": { + "data": "$HOST_PATH($LOCATION(ApplicationsPerformance)/portracker/data)" + } + }, + "network": { + "web_port": { + "bind_mode": "published", + "port_number": 30233 + } + }, + "resources": { + "limits": { + "cpus": 2, + "memory": "$MEMORY(5%, 1024)" + } + } +} \ No newline at end of file diff --git a/docs/public/install-scripts/prowlarr.json b/docs/public/install-scripts/prowlarr.json index 382c4263..9e7eedf2 100644 --- a/docs/public/install-scripts/prowlarr.json +++ b/docs/public/install-scripts/prowlarr.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.0", - "changeLog": "Upgraded Prowlarr install script to be compatible with latest HexOS script changes" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance"], @@ -15,7 +15,10 @@ "path": "$LOCATION(ApplicationsPerformance)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/prowlarr/config" + { + "path": "$LOCATION(ApplicationsPerformance)/prowlarr/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { @@ -28,4 +31,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/qbittorrent.json b/docs/public/install-scripts/qbittorrent.json index db383af9..b54b1d2c 100644 --- a/docs/public/install-scripts/qbittorrent.json +++ b/docs/public/install-scripts/qbittorrent.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.0", - "changeLog": "Upgraded qBittorrent install script to be compatible with latest HexOS script changes" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "ApplicationsCapacity", "Downloads"], @@ -23,8 +23,13 @@ "path": "$LOCATION(Downloads)", "network_share": true }, - "$LOCATION(Downloads)/qbittorrent", - "$LOCATION(ApplicationsPerformance)/qbittorrent/config" + { + "path": "$LOCATION(Downloads)/qbittorrent" + }, + { + "path": "$LOCATION(ApplicationsPerformance)/qbittorrent/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { diff --git a/docs/public/install-scripts/radarr.json b/docs/public/install-scripts/radarr.json index f0d61b5b..6fc787ca 100644 --- a/docs/public/install-scripts/radarr.json +++ b/docs/public/install-scripts/radarr.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.0", - "changeLog": "Upgraded Radarr install script to be compatible with latest HexOS script changes" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "ApplicationsCapacity", "Downloads", "Movies"], @@ -27,8 +27,13 @@ "path": "$LOCATION(Movies)", "network_share": true }, - "$LOCATION(Downloads)/qbittorrent", - "$LOCATION(ApplicationsPerformance)/radarr/config" + { + "path": "$LOCATION(Downloads)/qbittorrent" + }, + { + "path": "$LOCATION(ApplicationsPerformance)/radarr/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { diff --git a/docs/public/install-scripts/scrutiny.json b/docs/public/install-scripts/scrutiny.json index 6a449e87..24454892 100644 --- a/docs/public/install-scripts/scrutiny.json +++ b/docs/public/install-scripts/scrutiny.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.1", - "changeLog": "Updated requirements" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -22,8 +22,8 @@ "path": "$LOCATION(ApplicationsPerformance)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/scrutiny/config", - "$LOCATION(ApplicationsPerformance)/scrutiny/influxdb" + { "path": "$LOCATION(ApplicationsPerformance)/scrutiny/config", "snapshot": { "id": "config" } }, + { "path": "$LOCATION(ApplicationsPerformance)/scrutiny/influxdb", "snapshot": { "id": "influxdb" } } ], "app_values": { "network": { @@ -60,4 +60,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/public/install-scripts/sonarr.json b/docs/public/install-scripts/sonarr.json index 3d35511f..1d32126a 100644 --- a/docs/public/install-scripts/sonarr.json +++ b/docs/public/install-scripts/sonarr.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.0", - "changeLog": "Upgraded Sonarr install script to be compatible with latest HexOS script changes" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": ["ApplicationsPerformance", "ApplicationsCapacity", "Downloads", "Shows"], @@ -27,8 +27,13 @@ "path": "$LOCATION(Shows)", "network_share": true }, - "$LOCATION(Downloads)/qbittorrent", - "$LOCATION(ApplicationsPerformance)/sonarr/config" + { + "path": "$LOCATION(Downloads)/qbittorrent" + }, + { + "path": "$LOCATION(ApplicationsPerformance)/sonarr/config", + "snapshot": { "id": "config" } + } ], "app_values": { "storage": { diff --git a/docs/public/install-scripts/syncthing.json b/docs/public/install-scripts/syncthing.json index 010a0345..f9560c89 100644 --- a/docs/public/install-scripts/syncthing.json +++ b/docs/public/install-scripts/syncthing.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "script": { - "version": "1.0.2", - "changeLog": "add access to media, movies, shows" + "version": "1.1.0", + "changeLog": "Upgraded to V4 install script format" }, "requirements": { "locations": [ @@ -67,7 +67,7 @@ "path": "$LOCATION(Shows)", "network_share": true }, - "$LOCATION(ApplicationsPerformance)/syncthing/config" + { "path": "$LOCATION(ApplicationsPerformance)/syncthing/config", "snapshot": { "id": "config" } } ], "app_values": { "run_as": { @@ -109,4 +109,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/release-notes/command-deck/2026-05-20.md b/docs/release-notes/command-deck/2026-05-20.md new file mode 100644 index 00000000..e85ac600 --- /dev/null +++ b/docs/release-notes/command-deck/2026-05-20.md @@ -0,0 +1,35 @@ +# May 20, 2026 - App install script v4, New app curations, Curation beta program + +## Introducing our V4 installation scripts. + +This new iteration of app install scripts has a focus of improving application stability. + +- We have updated our curation process to reduce the likelihood of bugs in curated apps. +- We are now automatically creating temporary snapshots prior to updating applications. + - This will help eliminate the chances of apps with breaking updates damaging your data. +- We have also implemented a system that will heal permissions modified in TrueNAS. + - Modified permissions in TrueNAS previously could have unintended consequences such as preventing curated app installation. + + + +## App Curations + +We have committed to doubling our curations over the next few months, starting with these 6 releasing today. + +[LubeLogger](https://lubelogger.com) - LubeLogger assists users in keeping track of their car maintenance records. + +[Navidrome](https://www.navidrome.org) - Navidrome helps facilitate hosting your own music server. + +[Excalidraw](https://github.com/excalidraw/excalidraw) - Excalidraw is a Virtual whiteboard for sketching hand-drawn like diagrams. + +[Blinko](https://github.com/blinkospace/blinko) - Blinko is a lightweight note taking app. + +[Dozzle](https://dozzle.dev) - Dozzle is a realtime log viewer for docker apps running on your server + +[Portracker](https://github.com/mostafa-wahied/portracker) - Portracker is a realtime port monitoring tool. + +## Curation beta test program + +We are now recruiting users to beta test future curations prior to full release. +If you are interested please register using the [signup form](https://forms.gle/EKZenkWFFzFNKKxu5). + diff --git a/docs/release-notes/command-deck/index.md b/docs/release-notes/command-deck/index.md index d7f56104..99656cc5 100644 --- a/docs/release-notes/command-deck/index.md +++ b/docs/release-notes/command-deck/index.md @@ -11,6 +11,7 @@ For users who are actively connected during an update, there may be a brief down ## 2026 Releases +- [**2026-05-20**](./2026-05-20) - App install script v4, New app curations, Curation beta program - [**2026-05-13**](./2026-05-13) - TrueNAS Compatibility, App Install Improvements & Buddy Backup Foundations - [**2026-04-21**](./2026-04-21) - HexOS 1.0 Local Rollout Complete - [**2026-03-27**](./2026-03-27) - Hotfix : Apps Newly Curated diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index bfcd9776..1ccb271f 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -8,7 +8,7 @@ The Command Deck is the HexOS web interface. Updates are automatically deployed **[View Command Deck Release Notes →](/release-notes/command-deck/)** -**Latest:** [2026-05-13 — TrueNAS Compatibility, App Install Improvements & Buddy Backup Foundations](/release-notes/command-deck/2026-05-13) +**Latest:** [2026-05-20 — App install script v4, New app curations, Curation beta program](/release-notes/command-deck/2026-05-20) ## TrueNAS