diff --git a/.agents/skills/codebase-context/references/common.md b/.agents/skills/codebase-context/references/common.md
index f454752bd..e0b18ebb2 100644
--- a/.agents/skills/codebase-context/references/common.md
+++ b/.agents/skills/codebase-context/references/common.md
@@ -32,24 +32,23 @@ Import only from an exported package entrypoint. Do not deep-import `@homarr/com
## Root exports
-| Area | Public exports | Purpose |
-| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
-| Objects | `objectKeys`, `objectEntries`, `hashObjectBase64` | Typed key/entry iteration and stable query-key hashing |
-| Strings | `capitalize`, `isNullOrWhitespace`, `bestMatch`, `normalizeImageName`, `getImageMatchRank` | Text normalization and matching |
-| Cookies | `parseCookies`, `setClientCookie` | Cookie parsing and browser cookie serialization |
-| Arrays | `splitToNChunks`, `splitToChunksWithNItems` | Split by number of groups or maximum group size |
-| Dates | `formatDuration`, `toValidDate`, `isDateWithin` | Duration formatting, safe date coercion, relative-window checks |
-| Timing | `Stopwatch` | Elapsed-time measurement and formatting |
-| React hooks | `useTimeAgo`, `useIntegrationConnected` | Relative time and integration freshness state |
-| URLs | `removeTrailingSlash`, `extractBaseUrlFromHeaders`, `getPortFromUrl`, `isAbsoluteUrl`, `SAFE_NEW_TAB_REL`, `getSafeApplicationUrl`, `getSafeAppHref` | URL normalization, safe navigation, ports, and new-tab policy |
-| Numbers and bytes | `formatNumber`, `randomInt`, `formatBytes`, `formatBytesPair`, `formatByteRate`, `metricToImperial`, `imperialToMetric` | Compact numbers, byte displays, rates, and unit conversion |
-| Legacy bytes | `humanFileSize` | Deprecated; use the `formatBytes*` helpers in new code |
-| Byte types | `ByteUnitSystem`, `FormatBytesOptions` | Options for byte formatting |
-| Errors | `extractErrorMessage`, `FlattenError` | Unknown-error messages and flattenable domain errors |
-| Theme | `getMantineColor` | Resolve a Mantine color and shade |
-| Functions | `isFunction` | Unknown-value function guard |
-| Grid | `GridAlgorithmItem`, `generateResponsiveGridFor` | Responsive nested-section grid placement |
-| IDs | `createId` | CUID2 ID creation |
+| Area | Public exports | Purpose |
+| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
+| Objects | `objectKeys`, `objectEntries`, `isRecord`, `hashObjectBase64` | Typed key/entry iteration, record guards, and stable query-key hashing |
+| Strings | `capitalize`, `isNullOrWhitespace`, `bestMatch`, `normalizeImageName`, `getImageMatchRank` | Text normalization and matching |
+| Cookies | `parseCookies`, `setClientCookie` | Cookie parsing and browser cookie serialization |
+| Arrays | `splitToNChunks`, `splitToChunksWithNItems` | Split by number of groups or maximum group size |
+| Dates | `formatDuration`, `toValidDate`, `isDateWithin` | Duration formatting, safe date coercion, relative-window checks |
+| Timing | `Stopwatch` | Elapsed-time measurement and formatting |
+| React hooks | `useTimeAgo`, `useIntegrationConnected` | Relative time and integration freshness state |
+| URLs | `removeTrailingSlash`, `extractBaseUrlFromHeaders`, `getPortFromUrl`, `isAbsoluteUrl`, `SAFE_NEW_TAB_REL`, `getSafeApplicationUrl`, `getSafeAppHref` | URL normalization, safe navigation, ports, and new-tab policy |
+| Numbers and bytes | `formatNumber`, `formatBytes`, `formatBytesPair`, `formatByteRate`, `formatBitRate`, `defaultByteUnitSystem`, `metricToImperial` | Compact numbers, byte displays, byte/bit rates, and unit conversion |
+| Byte types | `ByteUnitSystem`, `FormatBytesOptions`, `FormatBitRateOptions` | Options for byte and bit-rate formatting |
+| Errors | `extractErrorMessage`, `FlattenError` | Unknown-error messages and flattenable domain errors |
+| Theme | `getMantineColor` | Resolve a Mantine color and shade |
+| Functions | `isFunction` | Unknown-value function guard |
+| Grid | `GridAlgorithmItem`, `generateResponsiveGridFor` | Responsive nested-section grid placement |
+| IDs | `createId` | CUID2 ID creation |
The root deliberately does not re-export `types`, `server`, `client`, or `env`; use their named entrypoints.
diff --git a/.github/workflows/docs-update-openapi.yml b/.github/workflows/docs-update-openapi.yml
index a2f1f7c30..4c81d4146 100644
--- a/.github/workflows/docs-update-openapi.yml
+++ b/.github/workflows/docs-update-openapi.yml
@@ -26,36 +26,8 @@ jobs:
- name: Copy env
run: cp .env.example .env
- - name: Start dev server
- run: pnpm --filter @homarr/nextjs dev &
- env:
- TURBO_TELEMETRY_DISABLED: 1
- DISABLE_TELEMETRY: 1
-
- - name: Wait for server and fetch spec
- run: |
- set -euo pipefail
- for i in $(seq 1 30); do
- if curl -fsS http://localhost:3000/api/openapi -o /tmp/openapi.json 2>/dev/null; then
- echo "Server is up, spec fetched"
- break
- fi
- echo "Waiting for server... ($i/30)"
- sleep 3
- done
-
- if [ ! -f /tmp/openapi.json ]; then
- echo "::error::Server failed to start within timeout"
- exit 1
- fi
-
- jq -e '.openapi and .paths' /tmp/openapi.json > /dev/null || {
- echo "::error::Fetched file is not a valid OpenAPI spec"
- exit 1
- }
-
- cp /tmp/openapi.json apps/docs/static/api/open-api-schema.json
- echo "OpenAPI spec updated ($(jq '.paths | length' /tmp/openapi.json) paths)"
+ - name: Generate and validate OpenAPI spec
+ run: pnpm scripts:update-openapi
- name: Commit updated spec
uses: EndBug/add-and-commit@v10
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
index 38113dc9d..375da03e8 100644
--- a/.oxfmtrc.json
+++ b/.oxfmtrc.json
@@ -15,6 +15,7 @@
"apps/docs/blog/2024/**",
"apps/docs/blog/2025/**",
"apps/docs/blog/authors.yml",
+ "apps/docs/static/api/open-api-schema.json",
"packages/custom-widgets/src/core/component-catalog.generated.json",
"apps/docs/static/custom-widgets/component-catalog-v1.json"
]
diff --git a/apps/docs/blog/2026/09-03-homarr-2.0/img/drag-and-drop-still.webp b/apps/docs/blog/2026/09-03-homarr-2.0/img/drag-and-drop-still.webp
new file mode 100644
index 000000000..50c21f40d
Binary files /dev/null and b/apps/docs/blog/2026/09-03-homarr-2.0/img/drag-and-drop-still.webp differ
diff --git a/apps/docs/blog/2026/09-03-homarr-2.0/img/drag-and-drop.gif b/apps/docs/blog/2026/09-03-homarr-2.0/img/drag-and-drop.gif
deleted file mode 100644
index 15871579f..000000000
Binary files a/apps/docs/blog/2026/09-03-homarr-2.0/img/drag-and-drop.gif and /dev/null differ
diff --git a/apps/docs/blog/2026/09-03-homarr-2.0/index.mdx b/apps/docs/blog/2026/09-03-homarr-2.0/index.mdx
index 54399ba6a..13ea745d9 100644
--- a/apps/docs/blog/2026/09-03-homarr-2.0/index.mdx
+++ b/apps/docs/blog/2026/09-03-homarr-2.0/index.mdx
@@ -2,6 +2,8 @@
authors:
- ajnart
tags: [homarr, update, version, assistant, custom widgets, dashboards, docker]
+description: Homarr 2.0 introduces Custom Widgets and Workshop, rebuilt board editing, Assistant tooling, responsive layouts, onboarding, and more.
+image: ./img/homarr-v2-dashboard.webp
draft: false
---
@@ -21,7 +23,9 @@ Homarr 2.0 is an upgrade, not a brand-new app wearing the same logo. Here is wha
:::warning Back up before upgrading
-Create a [Homarr backup](/docs/management/backup) first. Do not test a prerelease image on your only production volume. Backups are boring. Restores are worse.
+Follow the [v1 to v2 upgrade guide](/docs/getting-started/upgrade-v2) and create a
+[Homarr backup](/docs/management/backup) first. Do not test a prerelease image on your only production volume.
+Backups are boring. Restores are worse.
:::
@@ -61,7 +65,7 @@ We have called this the **"ultimate Homarr update"**. Not because work stops, bu
We threw out the old drag-and-drop system and rebuilt it on [`dnd-kit`](https://dndkit.com/). Moves and eight-direction resizes are transactional: Homarr previews the result, checks collisions and only saves a valid layout. Bad moves roll back instead of wrecking the board. In short: less fighting with your own dashboard.
-
+
### Goodbye Groups and sections, bonjour Containers
@@ -157,6 +161,7 @@ Board startup work runs in parallel, heavy screens load when opened, and identic
## Links
- Try the [Homarr 2.0 preview](https://app-v2.preview.homarr.dev).
+- Follow the [v1 to v2 upgrade guide](/docs/getting-started/upgrade-v2).
- Follow the [v2 release pull request](https://github.com/homarr-labs/homarr/pull/6545) and [public beta thread](https://github.com/homarr-labs/homarr/issues/6600).
- Compare [v1.76.2 with the release/v2 branch](https://github.com/homarr-labs/homarr/compare/v1.76.2...release/v2).
- Read the guides for [Boards](/docs/management/boards), [Custom Widgets](/docs/management/custom-widgets), [Assistant](/docs/management/assistant), [MCP](/docs/management/mcp) and [Docker labels](/docs/integrations/docker-labels).
diff --git a/apps/docs/docs/advanced/command-line/development/index.mdx b/apps/docs/docs/advanced/command-line/development/index.mdx
index 6f4701af8..c2e7edb1b 100644
--- a/apps/docs/docs/advanced/command-line/development/index.mdx
+++ b/apps/docs/docs/advanced/command-line/development/index.mdx
@@ -148,13 +148,18 @@ homarr images rm feature
# List data volumes with owner PR/tag and in-use status
homarr data
-# Delete data volume (safely stops and removes owning container first)
+# Preview deleting a data volume and its owning container
homarr data rm homarr_feature_data
+# Permanently delete the data after reviewing the preview
+homarr data rm homarr_feature_data --force
+
# Remove stopped containers (prompts without --force)
homarr prune --force
```
+Forced data deletion removes the derived owner container only when it mounts the exact selected volume. A same-named container that does not own that volume stops the operation.
+
### Instance Operations & Diagnostics
```bash
diff --git a/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx b/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx
index bf8808118..23f6a65af 100644
--- a/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx
+++ b/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx
@@ -14,4 +14,4 @@ homarr fix-usernames
This will convert all credential usernames to lowercase and update the database accordingly.
-
+
diff --git a/apps/docs/docs/advanced/command-line/index.mdx b/apps/docs/docs/advanced/command-line/index.mdx
index 851291924..e3fd6db6e 100644
--- a/apps/docs/docs/advanced/command-line/index.mdx
+++ b/apps/docs/docs/advanced/command-line/index.mdx
@@ -4,6 +4,7 @@ The Homarr image includes a recovery CLI for operations that cannot be completed
application API; use the [API](/docs/management/api) or [MCP endpoint](/docs/management/mcp) for automation.
Back up the database and stop Homarr before running a command that changes data.
+Failed commands print an error and return a non-zero exit code, so shell scripts can stop safely.
## Run the recovery CLI
diff --git a/apps/docs/docs/advanced/command-line/password-recovery/index.mdx b/apps/docs/docs/advanced/command-line/password-recovery/index.mdx
index 844b44140..68e79ac92 100644
--- a/apps/docs/docs/advanced/command-line/password-recovery/index.mdx
+++ b/apps/docs/docs/advanced/command-line/password-recovery/index.mdx
@@ -10,10 +10,10 @@ Using this command, you can forcefully reset the password of a user.
It will be set to a random value and the hash for the user will be updated.
All sessions for that user will be terminated and the user must re-login.
-
+
Using the `--username` parameter, you can specify who you want to reset the password for.
Executing the command will create a new random password, update the database and display the new password.
All sessions for this user will be terminated and they must re-login.
-
+
diff --git a/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx b/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx
index 4c3bca3fe..d2f5482ba 100644
--- a/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx
+++ b/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx
@@ -53,20 +53,6 @@ Commands at a grid boundary or at the tile's minimum size are ignored and announ
On touch screens, press and hold a tile before moving it; a quick tap does not start a drag. Resize handles accept direct touch input. During a pointer or touch move or resize, `Esc` cancels the interaction and restores the layout from before it started.
-## Board editing
-
-When a tile is focused in edit mode:
-
-| Shortcut | Description |
-| -------------------- | --------------------------------------------------------------------------- |
-| `Enter` or `Space` | Starts or stops keyboard editing |
-| Arrow keys | Moves the focused tile within its current canvas, rail, or Container |
-| `Shift` + Arrow keys | Resizes from the tile's lower-right corner: Left/Up shrink; Right/Down grow |
-| `Esc` | Stops keyboard editing without undoing its changes |
-
-Keyboard moves and resizes apply immediately. To move an item between a canvas, rail, or Container, drag it there or use **Move / resize item** for precise numeric placement.
-Commands at a grid boundary or at the tile's minimum size are ignored and announced instead of moving or shrinking the tile.
-
:::tip
Here `MOD` refers to the modifier key, which is `Ctrl` on Windows and `Cmd` on macOS.
diff --git a/apps/docs/docs/community/faq.mdx b/apps/docs/docs/community/faq.mdx
index 2beeaae7e..9944ae809 100644
--- a/apps/docs/docs/community/faq.mdx
+++ b/apps/docs/docs/community/faq.mdx
@@ -1,31 +1,32 @@
---
title: Frequently Asked Questions
+description: Answers to common Homarr questions about installation, customization, connectivity, support, and commercial use.
tags:
- Community
- Help
---
-### Can Homarr run on a Raspberry Pi?
+## Can Homarr run on a Raspberry Pi?
Yes, when the device uses a supported `linux/arm64` operating system. `linux/arm/v7` is not supported. See
[Prerequisites](/docs/getting-started).
-### Can I use custom app icons?
+## Can I use custom app icons?
Yes. Use a direct image URL, one of Homarr's icon sources, or upload an image. See [Icons](/docs/advanced/icons).
-### Can I create widgets?
+## Can I create widgets?
Yes. [Custom Widgets](/docs/management/custom-widgets) use server-side API requests and safe JSX. They can be written in
the workbench or created with an MCP-connected agent.
-### Can Homarr be exposed to the internet?
+## Can Homarr be exposed to the internet?
Homarr supports authenticated and public deployments. Terminate HTTPS at a reverse proxy, keep Homarr updated, and grant
public board access only when anonymous viewing is intended. See [Board access control](/docs/management/boards#access-control)
and [Single sign-on](/docs/advanced/single-sign-on).
-### Where are the logs?
+## Where are the logs?
Use **Management → Tools → Logs** for retained and live application logs. Container startup failures and older events are
available through the deployment platform, for example `docker container logs homarr`.
@@ -33,18 +34,18 @@ available through the deployment platform, for example `docker container logs ho
Use the browser developer console only for client-side failures. Review logs before sharing them because URLs and other
deployment details may be present.
-### Why can Homarr not reach a service behind Gluetun?
+## Why can Homarr not reach a service behind Gluetun?
Gluetun's firewall blocks traffic that is not explicitly allowed. Configure its
[`FIREWALL_OUTBOUND_SUBNETS`](https://github.com/qdm12/gluetun-wiki/blob/main/setup/options/firewall.md) for the relevant
LAN or Docker subnet, and use an address reachable from the Homarr container.
-### Where do I report a bug or request a feature?
+## Where do I report a bug or request a feature?
Use the [GitHub issue templates](https://github.com/homarr-labs/homarr/issues/new/choose). For community support, see
[Get in touch](/docs/community/get-in-touch).
-### Can Homarr be used commercially?
+## Can Homarr be used commercially?
Yes. Homarr is licensed under the [Apache License 2.0](/docs/community/license). Commercial users can support development
through [OpenCollective](/docs/community/donate).
diff --git a/apps/docs/docs/getting-started/after-the-installation.mdx b/apps/docs/docs/getting-started/after-the-installation.mdx
index 7e5712c83..8922bed6c 100644
--- a/apps/docs/docs/getting-started/after-the-installation.mdx
+++ b/apps/docs/docs/getting-started/after-the-installation.mdx
@@ -19,7 +19,7 @@ If onboarding returns after a restart, check that `/appdata` is writable and per
A **board** is a dashboard page containing apps, widgets, and Containers. Boards can be public or restricted to users
and groups. Each user can choose a home board.
-
+
See [Boards](/docs/management/boards) for responsive layouts, access control, appearance, and home-board settings.
@@ -28,7 +28,7 @@ See [Boards](/docs/management/boards) for responsive layouts, access control, ap
An **app** is a saved shortcut: a name, URL, icon, and optional status check. Add an App tile or a Bookmarks widget to
show apps on a board.
-
+
See [Apps](/docs/management/apps) or [Icons](/docs/advanced/icons).
@@ -37,7 +37,7 @@ See [Apps](/docs/management/apps) or [Icons](/docs/advanced/icons).
An **integration** is a server-side connection from Homarr to a supported service such as Sonarr, Home Assistant, or
Docker. Integrations provide data and actions to widgets. They can also be linked to apps.
-
+
See [Managing integrations](/docs/management/integrations) and the
[integration catalog](/docs/category/integrations).
@@ -47,7 +47,7 @@ See [Managing integrations](/docs/management/integrations) and the
A **widget** is a board tile. Some widgets work on their own; others require one or more integrations. Configuration,
supported integrations, and non-obvious limitations are documented on each widget page.
-
+
See the [widget catalog](/docs/category/widgets).
@@ -56,7 +56,7 @@ See the [widget catalog](/docs/category/widgets).
A **Container** groups board items and can be nested. A **rail** reserves a fixed area at the left or right of a
responsive layout. Both are configured in board edit mode.
-
+
See [Boards](/docs/management/boards#containers-and-rails).
@@ -65,7 +65,7 @@ See [Boards](/docs/management/boards#containers-and-rails).
Users inherit permissions and default boards from their groups. Resource-specific access can further restrict boards
and integrations.
-
+
See [Users and groups](/docs/management/users) and [single sign-on](/docs/advanced/single-sign-on).
diff --git a/apps/docs/docs/getting-started/img/after-installation-apps.png b/apps/docs/docs/getting-started/img/after-installation-apps.png
deleted file mode 100644
index 96270fe56..000000000
Binary files a/apps/docs/docs/getting-started/img/after-installation-apps.png and /dev/null differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-apps.webp b/apps/docs/docs/getting-started/img/after-installation-apps.webp
new file mode 100644
index 000000000..e882b8c8c
Binary files /dev/null and b/apps/docs/docs/getting-started/img/after-installation-apps.webp differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-boards.png b/apps/docs/docs/getting-started/img/after-installation-boards.png
deleted file mode 100644
index 0a83d3eda..000000000
Binary files a/apps/docs/docs/getting-started/img/after-installation-boards.png and /dev/null differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-boards.webp b/apps/docs/docs/getting-started/img/after-installation-boards.webp
new file mode 100644
index 000000000..73091b10f
Binary files /dev/null and b/apps/docs/docs/getting-started/img/after-installation-boards.webp differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-containers-rails.png b/apps/docs/docs/getting-started/img/after-installation-containers-rails.png
deleted file mode 100644
index 645c76b83..000000000
Binary files a/apps/docs/docs/getting-started/img/after-installation-containers-rails.png and /dev/null differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-containers-rails.webp b/apps/docs/docs/getting-started/img/after-installation-containers-rails.webp
new file mode 100644
index 000000000..d90adef01
Binary files /dev/null and b/apps/docs/docs/getting-started/img/after-installation-containers-rails.webp differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-integrations.png b/apps/docs/docs/getting-started/img/after-installation-integrations.png
deleted file mode 100644
index 3754ee2c2..000000000
Binary files a/apps/docs/docs/getting-started/img/after-installation-integrations.png and /dev/null differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-integrations.webp b/apps/docs/docs/getting-started/img/after-installation-integrations.webp
new file mode 100644
index 000000000..947e458b9
Binary files /dev/null and b/apps/docs/docs/getting-started/img/after-installation-integrations.webp differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-users-permissions.png b/apps/docs/docs/getting-started/img/after-installation-users-permissions.png
deleted file mode 100644
index 078a6ad42..000000000
Binary files a/apps/docs/docs/getting-started/img/after-installation-users-permissions.png and /dev/null differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-users-permissions.webp b/apps/docs/docs/getting-started/img/after-installation-users-permissions.webp
new file mode 100644
index 000000000..83ea47271
Binary files /dev/null and b/apps/docs/docs/getting-started/img/after-installation-users-permissions.webp differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-widgets.png b/apps/docs/docs/getting-started/img/after-installation-widgets.png
deleted file mode 100644
index 9edc4f7b1..000000000
Binary files a/apps/docs/docs/getting-started/img/after-installation-widgets.png and /dev/null differ
diff --git a/apps/docs/docs/getting-started/img/after-installation-widgets.webp b/apps/docs/docs/getting-started/img/after-installation-widgets.webp
new file mode 100644
index 000000000..5afb76119
Binary files /dev/null and b/apps/docs/docs/getting-started/img/after-installation-widgets.webp differ
diff --git a/apps/docs/docs/getting-started/installation/pika-pods.mdx b/apps/docs/docs/getting-started/installation/pika-pods.mdx
index 37fcf9b44..7b7790584 100644
--- a/apps/docs/docs/getting-started/installation/pika-pods.mdx
+++ b/apps/docs/docs/getting-started/installation/pika-pods.mdx
@@ -2,4 +2,4 @@
Pika Pods is a hosting platform for open source apps. You can deploy Homarr on Pika Pods with just a few clicks:
-[__](https://www.pikapods.com/pods?run=homarr-v1)
+[__](https://www.pikapods.com/pods?run=homarr)
diff --git a/apps/docs/docs/getting-started/installation/unraid.mdx b/apps/docs/docs/getting-started/installation/unraid.mdx
index 18588c5d4..e768cf296 100644
--- a/apps/docs/docs/getting-started/installation/unraid.mdx
+++ b/apps/docs/docs/getting-started/installation/unraid.mdx
@@ -30,7 +30,7 @@ You can install Homarr directly from your Unraid Dashboard, no terminal required
Don't know whether you have the plugin installed or not? Search for this tab in the navigation:
-
+
:::
#### Installing
@@ -41,7 +41,7 @@ After you've installed the Community Apps Plugin, you can install Homarr from th
2. Search for "Homarr" and click on the result.
3. Click on "Install" and adjust the settings to your liking.
-
+
After you've installed Homarr, you can find it under the tab "Docker".
diff --git a/apps/docs/docs/integrations/_category_.json b/apps/docs/docs/integrations/_category_.json
index 896594e32..426dfea6f 100644
--- a/apps/docs/docs/integrations/_category_.json
+++ b/apps/docs/docs/integrations/_category_.json
@@ -5,6 +5,6 @@
"link": {
"type": "generated-index",
"title": "Integrations",
- "description": "Integration are a principal part of homarr, it's where you can connect your other apps to interact with them with widgets or other capabilities, Homarr currently integrates with the following applications:"
+ "description": "Connect Homarr to supported services so widgets can display data and run actions. Choose an integration to see its setup requirements and compatible widgets."
}
}
diff --git a/apps/docs/docs/management/api/index.mdx b/apps/docs/docs/management/api/index.mdx
index 7da2c975d..c975f9a9f 100644
--- a/apps/docs/docs/management/api/index.mdx
+++ b/apps/docs/docs/management/api/index.mdx
@@ -18,7 +18,7 @@ Create an API key from the **Authentication** tab. Its value has the form `.
Send it in the `ApiKey` header:
```sh
-curl -H 'ApiKey: .' https://homarr.example.com/api/health/live
+curl -H 'ApiKey: .' https://homarr.example.com/api/info
```
The same header authenticates tRPC requests under `/api/trpc`. Without a key, browser requests can use the Homarr
@@ -27,6 +27,21 @@ session cookie.
API keys do not expire automatically and act as the user that created them. Every request uses that user's group and
resource permissions.
+## Health probes
+
+The liveness endpoint does not require authentication:
+
+```sh
+curl https://homarr.example.com/api/health/live
+```
+
+It returns JSON with overall and per-component status and latency. HTTP `200` means every dependency is healthy; HTTP
+`500` identifies an unhealthy component without exposing its internal error. Health responses are not cached.
+
+## Pagination
+
+General paginated endpoints accept at most 100 records per page, and shared search endpoints accept a limit of at most 100. Request subsequent pages instead of using a larger value.
+
## Permission behavior
Unauthorized operations return `FORBIDDEN`. List endpoints filter records on the server when resource-specific access
diff --git a/apps/docs/docs/management/boards/index.mdx b/apps/docs/docs/management/boards/index.mdx
index 762563acd..5b51ef96e 100644
--- a/apps/docs/docs/management/boards/index.mdx
+++ b/apps/docs/docs/management/boards/index.mdx
@@ -51,6 +51,8 @@ layout.
- Press Escape during a pointer or touch interaction to restore the item to its previous position.
The add-content menu can create an app, integration, widget, or Container without leaving the board.
+When adding apps, click one app to add it immediately, or choose **Select multiple** to add several at once. `Ctrl`/`Cmd`
+click remains available as a shortcut.
## Board settings
diff --git a/apps/docs/docs/management/certificates/index.mdx b/apps/docs/docs/management/certificates/index.mdx
index 46f0a6cc7..d8efdabc1 100644
--- a/apps/docs/docs/management/certificates/index.mdx
+++ b/apps/docs/docs/management/certificates/index.mdx
@@ -17,7 +17,7 @@ The color of the icon indicates how long it is valid.

-
+
| Color | Meaning |
| --------- | --------------------------- |
@@ -34,7 +34,7 @@ Navigate to `Management` > `Tools` > `Certificates`.
To add a certificate click on the "Add certificate" button. The upload form expands directly on the certificates page.
-
+
Then select the certificate file you want to add and click the "Add" button. You can cancel to collapse the form without
leaving the page.
@@ -43,7 +43,7 @@ leaving the page.
To delete a certificate you can click on the trash icon in the certificate card.
-
+
The first click arms the action and the second click confirms the deletion in place; no confirmation dialog is opened.
diff --git a/apps/docs/docs/management/custom-widgets/index.mdx b/apps/docs/docs/management/custom-widgets/index.mdx
index aa8886119..1e07f0611 100644
--- a/apps/docs/docs/management/custom-widgets/index.mdx
+++ b/apps/docs/docs/management/custom-widgets/index.mdx
@@ -29,6 +29,8 @@ encrypted separately and are not included in exports, prompts, browser data, or
Administrators can create, import, duplicate, enable, export, publish, and delete definitions under
**Management → Custom Widgets**. Adding or configuring one on a board also requires permission to modify that board.
+Use **Actions → Configure sources** to update deployment-specific URLs, network access, and encrypted credentials
+without reopening the authoring workbench.
Once placed, normal board permissions control rendering and requests. Queries can run for anonymous viewers on public
boards; actions require authentication and the permission declared by the request.
diff --git a/apps/docs/docs/management/mcp.mdx b/apps/docs/docs/management/mcp.mdx
index 36683a39f..39f7f2229 100644
--- a/apps/docs/docs/management/mcp.mdx
+++ b/apps/docs/docs/management/mcp.mdx
@@ -47,7 +47,8 @@ Clients without Streamable HTTP support can use `mcp-remote` over STDIO:
}
```
-The **AI / MCP** tab on Homarr's API page shows the current endpoint, configuration, and generated tool catalog.
+The **AI / MCP** tab on Homarr's API page shows the current endpoint, configuration, and generated tool catalog. Filter
+the catalog by namespace, tool name, or description; matching categories expand automatically.
## Authentication and permissions
diff --git a/apps/docs/docs/management/media/index.mdx b/apps/docs/docs/management/media/index.mdx
index 2872dbe6e..1cae4f268 100644
--- a/apps/docs/docs/management/media/index.mdx
+++ b/apps/docs/docs/management/media/index.mdx
@@ -11,6 +11,6 @@ tags:
Using the media feature, you can upload images for custom icons to Homarr.
Images will be stored in the database. They can be selected in the [icon picker](/docs/advanced/icons).
-
+

diff --git a/apps/docs/docs/management/search-engines/index.mdx b/apps/docs/docs/management/search-engines/index.mdx
index 0d1ecf7c6..8fa2e9c96 100644
--- a/apps/docs/docs/management/search-engines/index.mdx
+++ b/apps/docs/docs/management/search-engines/index.mdx
@@ -22,7 +22,7 @@ You can add any search engine that has a search URL that accepts a search term a
To create a custom search engine, you can click the button at the top of the page.
-
+
### Name
diff --git a/apps/docs/docs/management/users/index.mdx b/apps/docs/docs/management/users/index.mdx
index 00fc8ac67..baa462508 100644
--- a/apps/docs/docs/management/users/index.mdx
+++ b/apps/docs/docs/management/users/index.mdx
@@ -13,8 +13,10 @@ A **user** is a Homarr identity with preferences, sessions, group memberships, a
use local credentials, LDAP, or OIDC; see [Single sign-on](/docs/advanced/single-sign-on).
Manage users under **Management → Users → Manage**. Administrators can change a user's profile, language, home boards,
-search preferences, week start, accessibility preference, and local password. Deleting a user does not delete boards
-they created.
+search preferences, data-size units (binary KiB/MiB or decimal KB/MB), week start, accessibility preference, and local
+password. Decimal units are the default. Change them from the user form or **Command menu (Ctrl/Cmd + K) →
+Preferences → Data size units**. Byte rates follow the same preference; bit rates stay decimal and use labels such as
+Mb/s. Deleting a user does not delete boards they created.

diff --git a/apps/docs/docs/widgets/_category_.json b/apps/docs/docs/widgets/_category_.json
index df8d80354..6af2e65ce 100644
--- a/apps/docs/docs/widgets/_category_.json
+++ b/apps/docs/docs/widgets/_category_.json
@@ -5,7 +5,7 @@
"link": {
"type": "generated-index",
"title": "Widgets",
- "description": "Widgets allow you to show data on your board or control applications. Click on one of the widgets below for further information:",
- "image": "/img/docusaurus.png"
+ "description": "Widgets display data or provide actions on a board. Choose a widget to see its configuration, supported integrations, and limitations.",
+ "image": "/img/pictures/widgets/widget-dark.jpg"
}
}
diff --git a/apps/docs/docs/widgets/downloads/index.ts b/apps/docs/docs/widgets/downloads/index.ts
index f4e1945b1..7d5b10b34 100644
--- a/apps/docs/docs/widgets/downloads/index.ts
+++ b/apps/docs/docs/widgets/downloads/index.ts
@@ -66,8 +66,9 @@ export const downloadsWidget: WidgetDefinition = {
defaultValue: "yes",
},
{
- name: "Hide completed torrent under this threshold (in kiB/s)",
- description: "This will hide completed torrent entries whose upload speed is below the specified threshold.",
+ name: "Hide completed torrent under this threshold",
+ description:
+ "This hides completed torrents whose upload speed is below the threshold. The input uses KiB/s with binary units and KB/s with decimal units.",
values: "Any number above 0, 0 to disable",
defaultValue: "0",
},
diff --git a/apps/docs/docs/widgets/system-disks/index.ts b/apps/docs/docs/widgets/system-disks/index.ts
index ade53efc0..993b3c67f 100644
--- a/apps/docs/docs/widgets/system-disks/index.ts
+++ b/apps/docs/docs/widgets/system-disks/index.ts
@@ -19,11 +19,7 @@ export const systemDisksWidget: WidgetDefinition = {
description: "How disk usage values are shown",
values: {
type: "select",
- options: [
- "Percentage (e.g., 76%)",
- "Absolute values (e.g., 800GB / 1TB)",
- "Free space percentage (e.g., 24% free)",
- ],
+ options: ["Percentage (e.g., 76%)", "Absolute values", "Free space percentage (e.g., 24% free)"],
},
defaultValue: "Percentage (e.g., 76%)",
},
diff --git a/apps/docs/package.json b/apps/docs/package.json
index ce8dd21ca..716b13761 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -34,17 +34,13 @@
"@mdx-js/react": "^3.1.1",
"@nivo/line": "^0.99.0",
"@scalar/api-reference-react": "catalog:",
- "@shadcn/react": "catalog:",
"@tabler/icons-react": "catalog:",
"@tailwindcss/postcss": "^4.3.2",
"class-variance-authority": "^0.7.1",
"clsx": "catalog:",
- "cmdk": "catalog:",
"date-fns": "catalog:",
"dayjs": "catalog:",
"docusaurus-plugin-image-zoom": "^3.0.1",
- "embla-carousel-react": "catalog:",
- "input-otp": "catalog:",
"leader-line-new": "^1.1.9",
"next-themes": "catalog:",
"pocketbase": "catalog:",
@@ -52,10 +48,7 @@
"posthog-docusaurus": "^2.0.5",
"prism-react-renderer": "^2.4.1",
"react": "catalog:",
- "react-day-picker": "catalog:",
"react-dom": "catalog:",
- "react-resizable-panels": "catalog:",
- "recharts": "3.8.0",
"sonner": "catalog:",
"swiper": "^14.0.5",
"tailwind-merge": "^3.6.0",
diff --git a/apps/docs/src/components/carbon.tsx b/apps/docs/src/components/carbon.tsx
index 68e2708e1..11aabc025 100644
--- a/apps/docs/src/components/carbon.tsx
+++ b/apps/docs/src/components/carbon.tsx
@@ -24,147 +24,3 @@ export function Carbon() {
>
);
}
-
-// Variant 1: Strict re-initialization – remove prior scripts and injected nodes, then load cover
-export function CarbonCoverStrict() {
- const ref = React.useRef(null!);
- const location = useLocation();
- if (process.env.NODE_ENV === "development") {
- return null;
- }
-
- useEffect(() => {
- const serve = "CWBDTKQM";
- const placement = "homarrdev";
-
- // Clear our local container
- ref.current.innerHTML = "";
-
- // Remove any globally injected Carbon ad containers
- Array.from(document.querySelectorAll('[id^="carbonads"], .carbonads')).forEach(
- (el) => el.parentElement && el.parentElement.removeChild(el),
- );
-
- // Remove existing script if present
- const existing = document.getElementById("_carbonads_js");
- if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
-
- // Reset possible global handle
- try {
- delete (window as any)._carbonads;
- } catch {}
-
- // Inject fresh cover script into our container
- const s = document.createElement("script");
- s.id = "_carbonads_js";
- s.src = `https://cdn.carbonads.com/carbon.js?serve=${serve}&placement=${placement}&format=cover`;
- ref.current.appendChild(s);
- }, [location]);
-
- return (
- <>
-
-
- >
- );
-}
-
-// Variant 2: Observer – load cover then keep only the latest injected ad node
-export function CarbonCoverObserver() {
- const ref = React.useRef(null!);
- const location = useLocation();
- if (process.env.NODE_ENV === "development") {
- return null;
- }
-
- useEffect(() => {
- const serve = "CWBDTKQM";
- const placement = "homarrdev";
-
- ref.current.innerHTML = "";
-
- const s = document.createElement("script");
- s.id = "_carbonads_js";
- s.src = `https://cdn.carbonads.com/carbon.js?serve=${serve}&placement=${placement}&format=cover`;
- ref.current.appendChild(s);
-
- // Observe body and ensure we keep only the most recent carbon container
- const trim = () => {
- const nodes = Array.from(document.querySelectorAll('[id^="carbonads"]')) as HTMLElement[];
- if (nodes.length > 1) {
- // Keep the last (most recently inserted); remove the rest
- nodes.slice(0, -1).forEach((n) => n.parentElement && n.parentElement.removeChild(n));
- }
- };
-
- const observer = new MutationObserver(() => trim());
- observer.observe(document.body, { childList: true, subtree: true });
-
- // Initial pass
- trim();
-
- return () => observer.disconnect();
- }, [location]);
-
- return (
- <>
-
-
- >
- );
-}
-
-// Variant 3: CSS-only – hide the first instance so only the second (cover) remains visible
-export function CarbonCoverCssOnlySecond() {
- const ref = React.useRef(null!);
- const location = useLocation();
- if (process.env.NODE_ENV === "development") {
- return null;
- }
-
- useEffect(() => {
- const serve = "CWBDTKQM";
- const placement = "homarrdev";
- ref.current.innerHTML = "";
- const s = document.createElement("script");
- s.id = "_carbonads_js";
- s.src = `https://cdn.carbonads.com/carbon.js?serve=${serve}&placement=${placement}&format=cover`;
- ref.current.appendChild(s);
- }, [location]);
-
- return (
- <>
-
-
- >
- );
-}
diff --git a/apps/docs/src/components/pages/home/drag-and-drop/drag-and-drop-showcase.tsx b/apps/docs/src/components/pages/home/drag-and-drop/drag-and-drop-showcase.tsx
index b79c8de01..a4859091b 100644
--- a/apps/docs/src/components/pages/home/drag-and-drop/drag-and-drop-showcase.tsx
+++ b/apps/docs/src/components/pages/home/drag-and-drop/drag-and-drop-showcase.tsx
@@ -18,7 +18,16 @@ export const DragAndDropShowcase = () => {
className={"rounded-3xl overflow-hidden border-8 border-red-500 border-solid"}
style={{ aspectRatio: "17.6/9" }}
>
-
+
diff --git a/apps/docs/src/components/pages/home/drag-and-drop/showcase-dark.mp4 b/apps/docs/src/components/pages/home/drag-and-drop/showcase-dark.mp4
index eba8f7321..dd166e03c 100644
Binary files a/apps/docs/src/components/pages/home/drag-and-drop/showcase-dark.mp4 and b/apps/docs/src/components/pages/home/drag-and-drop/showcase-dark.mp4 differ
diff --git a/apps/docs/src/components/pages/home/drag-and-drop/showcase-light.mp4 b/apps/docs/src/components/pages/home/drag-and-drop/showcase-light.mp4
index 359fccc3e..e136faabe 100644
Binary files a/apps/docs/src/components/pages/home/drag-and-drop/showcase-light.mp4 and b/apps/docs/src/components/pages/home/drag-and-drop/showcase-light.mp4 differ
diff --git a/apps/docs/src/components/pages/home/features/features.tsx b/apps/docs/src/components/pages/home/features/features.tsx
index aaa77aaf6..327432d71 100644
--- a/apps/docs/src/components/pages/home/features/features.tsx
+++ b/apps/docs/src/components/pages/home/features/features.tsx
@@ -59,7 +59,7 @@ function FeatureComponent(props: Feature) {
-
{props.title}
+
{props.title}
{props.content}
diff --git a/apps/docs/src/components/pages/home/hero/hero.tsx b/apps/docs/src/components/pages/home/hero/hero.tsx
index 61128c23f..0f457b7a5 100644
--- a/apps/docs/src/components/pages/home/hero/hero.tsx
+++ b/apps/docs/src/components/pages/home/hero/hero.tsx
@@ -49,11 +49,11 @@ export default function HomeHero() {
- Host from $2.3/month
+ Host with PikaPods
diff --git a/apps/docs/src/components/pages/home/integrations/available-integrations.tsx b/apps/docs/src/components/pages/home/integrations/available-integrations.tsx
index d00feeb00..36254a47b 100644
--- a/apps/docs/src/components/pages/home/integrations/available-integrations.tsx
+++ b/apps/docs/src/components/pages/home/integrations/available-integrations.tsx
@@ -24,7 +24,7 @@ export const AvailableIntegrations = () => {
with tons of users, making Homarr reliable in big scale deployments too.