Compare commits

...
Author SHA1 Message Date
ajnart 6969e3fb27 Support Markdown exports for getting-started docs 2026-09-11 11:20:02 +02:00
ajnart 16ba3c6977 docs: improve Fumadocs search and AI integration 2026-09-10 20:51:08 +02:00
ajnart 6704ef2c13 docs: migrate documentation to Fumadocs 2026-09-10 20:39:26 +02:00
leafyelin 6cca757493 feat(plugins): add llama.cpp server integration and widget (#6765) 2026-09-10 20:06:13 +02:00
Thomas 1102ad8342 fix: protect demo users from deletion and password changes (#6819) 2026-09-10 19:52:47 +02:00
Thomas 7c00c18a42 docs(mcp): restore custom widget authoring guidance (#6817) 2026-09-10 19:24:55 +02:00
Thomas ea3dac752f fix(mcp): harden tool schemas and dispatch (#6815) 2026-09-10 19:19:47 +02:00
Thomas f994d665d2 ci: remove automatic test gates and trim test suites (#6816)
* ci: remove automatic test gates and trim test suites

* docs: remove CI audit report
2026-09-10 19:14:31 +02:00
Thomas 3358b6337f fix(mcp): use JSON-compatible invitation dates (#6814) 2026-09-10 17:53:38 +02:00
Thomas 61d3fd5f08 fix(board): restore container grid geometry (#6808) 2026-09-09 15:04:27 +02:00
Thomas 3aac106156 docs: correct v2 beta installation guidance (#6809) 2026-09-09 12:09:14 +02:00
Thomas 9d857377c1 fix: refine release v2 interactions (#6805) 2026-09-08 11:36:19 +02:00
Thomas 923905c529 fix: keep bookmark density consistent (#6804)
* fix: keep bookmark density consistent

* fix: prioritize bookmark titles in compact rows

* fix: preserve compact bookmark text fallback
2026-09-08 10:39:41 +02:00
Thomas a4a903d7c3 feat: polish release v2 experience (#6800) 2026-09-07 19:17:37 +02:00
Thomas 925b0fb801 fix: improve bookmark widget density (#6802)
* fix: improve bookmark widget density

* fix: render dense bookmark grids as icons

* fix: honor bookmark column density
2026-09-07 13:47:11 +02:00
ajnart 6db26a0d99 docs: restore release article hierarchy 2026-09-06 20:41:55 +02:00
ajnart bbcbd031fe docs: clarify Homarr provider terms 2026-09-06 20:37:52 +02:00
ajnart 7024a57f56 docs: tighten Homarr 2.0 release article 2026-09-06 19:57:44 +02:00
Thomas 5121356113 Merge pull request #6794 from homarr-labs/feat/refocus-homarr-v2-article
docs: refocus Homarr 2.0 article
2026-09-06 19:20:05 +02:00
ajnart 4f2ab59345 docs: refocus Homarr 2.0 article 2026-09-06 19:19:23 +02:00
Thomas 73bf68c9a8 Merge pull request #6792 from homarr-labs/feat/v2-release-client-ids
fix(ci): use GitHub App client IDs
2026-09-06 18:05:28 +02:00
ajnart dea1de5fee fix(ci): use GitHub App client IDs 2026-09-06 17:57:35 +02:00
Thomas 2bd538e685 Merge pull request #6791 from homarr-labs/feat/publish-homarr-v2-blog
docs: publish Homarr 2.0 article
2026-09-06 17:52:05 +02:00
ajnart f0c39ea0d2 docs: publish Homarr 2.0 article 2026-09-06 17:51:36 +02:00
Thomas 8a0cf15bf7 docs: finalize Homarr 2.0 release article (#6790) 2026-09-06 17:48:08 +02:00
Thomas f1c898efe2 docs: add Homarr 2.0 release article (#6789) 2026-09-06 15:39:54 +02:00
Thomas 724dee55c5 fix(weather): handle disabled external connections (#6780) 2026-09-06 15:35:06 +02:00
Thomas 50a3bcce69 fix(ci): restore release automation (#6770)
* fix(ci): restore release automation

* ci: remove obsolete documentation release dispatch

* ci: report production deployment failures
2026-09-06 15:32:52 +02:00
Thomasandajnart ca7be8f3ad fix(integrations): support UniFi controller default ports (#6787)
* fix(integrations): use Unifi default port 8443 when no port is given

The node-unifi library hardcodes https:// in its base URL and the
Unifi controller API only speaks HTTPS. When a user adds the
integration with a bare http:// or https:// URL, getPortFromUrl
returned 80 or 443, and the library attempted a TLS handshake on
the wrong port. The server's plain HTTP response caused
'EPROTO ... wrong version number'.

Derive the port from the user's explicit port, or fall back to 8443
(the integration's defaultPort). Closes #6207.

* style: auto-fix lint and format

* fix(integrations): support UniFi controller default ports

* docs(unifi): resolve review leftovers

---------

Co-authored-by: ajnart <ajnart@users.noreply.github.com>
2026-09-06 15:32:45 +02:00
134ef48848 fix(integrations): preserve Nextcloud subpaths (#6786)
* fix(integrations): preserve Nextcloud subpaths

* Keep existing Nextcloud DAV endpoints intact

---------

Co-authored-by: Artur <145776561+arturict@users.noreply.github.com>
Co-authored-by: arturict <arturict@users.noreply.github.com>
2026-09-06 15:32:37 +02:00
98aa6f01f2 feat(auth): support nested OIDC claim paths (#6785)
* feat(auth): support nested OIDC claim paths

* test(auth): cover OIDC profile name fallbacks

* test(auth): cover missing OIDC username fallback

* test(auth): preserve OIDC provider guard

---------

Co-authored-by: Artur <145776561+arturict@users.noreply.github.com>
Co-authored-by: arturict <arturict@users.noreply.github.com>
2026-09-06 15:32:31 +02:00
Thomas 4c88030b0f fix(downloads): preserve name column width (#6784) 2026-09-06 15:32:25 +02:00
Thomas 0c11c2785e docs(helm): sync current chart metadata (#6783) 2026-09-06 15:32:20 +02:00
Thomas 96a6926b2b fix(docker): normalize container CPU usage (#6782) 2026-09-06 15:32:15 +02:00
Thomas f89487231f fix(unraid): use byte-based memory metrics (#6781) 2026-09-06 15:32:09 +02:00
Thomas a62d11b5cd fix(mcp): honor public OAuth origin (#6779) 2026-09-06 15:31:57 +02:00
Thomas 28e242417d fix(backup): prompt re-login after restore (#6778) 2026-09-06 15:31:51 +02:00
Thomas 0ffa95c505 docs: make environment examples safe and current (#6777)
* docs: make SQLite environment example runnable

* docs: remove unsafe source secret examples

* docs(auth): remove unsafe SSO environment values

* docs(auth): clarify OIDC credential placeholders
2026-09-06 15:31:40 +02:00
Thomas b708f687fa docs: add v1 to v2 upgrade guide (#6774)
* docs: add v1 to v2 upgrade guide

* docs: simplify v2 backup guidance
2026-09-06 15:31:33 +02:00
Thomas 4fd9b04c61 fix(deps): patch high production advisories (#6788) 2026-09-06 15:27:14 +02:00
Thomas 378332c8da docs: update contribution checklist for v2 (#6773) 2026-09-06 14:31:57 +02:00
Thomas 3d9b8e8ae6 fix(docs): generate valid integration links (#6772) 2026-09-06 14:31:48 +02:00
Thomas f7b194fc9f fix(auth): patch critical Auth.js vulnerabilities (#6775) 2026-09-06 14:25:09 +02:00
Thomas eb20b97d5f fix(deps): patch critical protobufjs vulnerability (#6776) 2026-09-06 14:25:00 +02:00
657 changed files with 21879 additions and 20204 deletions

No files matched your search

@@ -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.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: documentation-sync
description: Keep Homarr's Docusaurus documentation aligned with user-facing code. Use when adding or changing integrations, widgets, APIs, environment variables, CLI commands, authentication, permissions, cron jobs, settings, or UI behavior that users must understand.
description: Keep Homarr's Fumadocs documentation aligned with user-facing code. Use when adding or changing integrations, widgets, APIs, environment variables, CLI commands, authentication, permissions, cron jobs, settings, or UI behavior that users must understand.
---
# Documentation Sync
@@ -48,4 +48,4 @@ For a widget:
2. Keep names, defaults, paths, screenshots, links, and prerequisites consistent with code.
3. Update every affected page and remove superseded guidance.
4. Run the narrowest useful docs validation. Use `pnpm turbo build --filter=@homarr/docs` when links, MDX, generated definitions, or navigation can fail; otherwise run the docs package formatter on touched files.
5. Treat broken links and anchors as failures; Docusaurus checks them strictly.
5. Treat broken links and anchors as failures; run `pnpm --filter @homarr/docs validate:links` after the static build.
+11 -7
View File
@@ -1,22 +1,24 @@
# Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo.
# Keep this file up-to-date when you add new variables to \`.env\`.
# Keep this file up-to-date when you add new variables to `.env`.
# This file will be committed to version control, so make sure not to have any secrets in it.
# If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets.
# The below secret is not used anywhere but required for Auth.js (Would encrypt JWTs and Mail hashes, both not used)
AUTH_SECRET="supersecret"
# Generate two different 64-character hex values with `openssl rand -hex 32` before starting Homarr.
# Never reuse these values or commit the populated `.env` file.
AUTH_SECRET=
# The below secret is used to encrypt integration secrets in the database.
# It should be a 32-byte string, generated by running `openssl rand -hex 32` on Unix
# or starting the project without any (which will show a randomly generated one).
SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
# Homarr will reject an empty or invalid value and print a randomly generated candidate.
SECRET_ENCRYPTION_KEY=
LOG_LEVEL='info'
# This is how you can use the sqlite driver:
DB_DRIVER='better-sqlite3'
DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE'
# Safe local source-development path. SQLite files are ignored by Git.
# Container images use /appdata/db/db.sqlite by default instead.
DB_URL='./db.sqlite'
# Those are the two ways to use the mysql2 driver:
# 1. Using the URL format:
@@ -63,6 +65,8 @@ UNSAFE_ENABLE_MOCK_INTEGRATION=true
# Public URL contract. Values must be absolute HTTP(S) URLs without credentials,
# query strings, or fragments. Trailing slashes are normalized at startup.
# Public Homarr origin used by MCP OAuth metadata and redirects behind proxies.
# BASE_URL=https://homarr.example.com
HOMARR_WEBSITE_URL=https://homarr.dev
# PocketBase API used by Homarr and by the standalone documentation site.
# Defaults to HOMARR_WEBSITE_URL.
+6 -6
View File
@@ -4,14 +4,14 @@
<h3>Homarr</h3>
</div>
**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**
**Thank you for your contribution. Please ensure that your pull request meets the following requirements:**
- [ ] Builds without warnings or errors (`pnpm build`, autofix with `pnpm format:fix`)
- [ ] Pull request targets `dev` branch
- [ ] Relevant checks pass without warnings or errors
- [ ] Pull request targets the intended base branch (`dev` by default; `release/v2` for v2 release work)
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
- [ ] No shorthand variable names are used (eg. `x`, `y`, `i` or any abbrevation)
- [ ] Documentation is up to date. Create a pull request [here](https://github.com/homarr-labs/documentation/).
- [ ] When using AI; No temp files are checked in, the code style follows the rest of the project
- [ ] No shorthand variable names are used (for example, `x`, `y`, `i`, or abbreviations)
- [ ] User-facing changes include matching documentation updates under `apps/docs`
- [ ] When using AI, no temporary files are checked in and the code style follows the rest of the project
**When contributing a new integration or widget:**
@@ -20,7 +20,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
permission-pull-requests: write # required to enable auto-merge
- name: Install GitHub CLI
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
permission-pull-requests: write # required to approve a pull request and enable auto-merge
- name: Install GitHub CLI
+10 -57
View File
@@ -97,17 +97,16 @@ jobs:
- name: Validate Custom Widget bundle budget
run: |
pnpm --filter nextjs build
pnpm turbo run build --filter=@homarr/nextjs
pnpm check:custom-widget-bundle
- name: Run unit tests
run: pnpm test
container:
name: Container and E2E
name: Container build
# Same-repository previews already build amd64; only build a fallback when no digest is available.
needs: [fast-gate, preview-amd64]
if: |
!cancelled() && needs.fast-gate.result == 'success' &&
needs.preview-amd64.result != 'failure' && needs.preview-amd64.outputs.digest == '' &&
(
github.event_name == 'merge_group' ||
(github.event_name == 'push' && github.ref == 'refs/heads/release/v2') ||
@@ -115,10 +114,9 @@ jobs:
(github.event_name == 'workflow_dispatch' && (inputs.operation == 'validate' || inputs.operation == 'publish'))
)
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 20
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@v7
if: "!env.ACT"
@@ -126,23 +124,10 @@ jobs:
persist-credentials: false
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
- name: Setup
uses: ./tooling/github/setup
- name: Set up Docker Buildx
if: "!env.ACT"
if: needs.preview-amd64.outputs.digest == '' && !env.ACT
uses: docker/setup-buildx-action@v4
- name: Reuse amd64 preview image
if: needs.preview-amd64.outputs.digest != '' && !env.ACT
env:
DIGEST: ${{ needs.preview-amd64.outputs.digest }}
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
docker pull "${PREVIEW_IMAGE}@${DIGEST}"
docker tag "${PREVIEW_IMAGE}@${DIGEST}" homarr-ci
docker tag "${PREVIEW_IMAGE}@${DIGEST}" homarr-e2e
- name: Build container once
if: needs.preview-amd64.outputs.digest == '' && !env.ACT
uses: docker/build-push-action@v7
@@ -152,9 +137,7 @@ jobs:
platforms: linux/amd64
load: true
push: false
tags: |
homarr-ci
homarr-e2e
tags: homarr-ci
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
@@ -182,37 +165,7 @@ jobs:
fi
done
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 \
--tag homarr-ci --tag homarr-e2e "${secret_args[@]}" .
- name: Cache Playwright browsers
if: "!env.ACT"
id: playwright-cache
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true' || env.ACT
run: pnpm exec playwright install --with-deps chromium
- name: Install Playwright system deps
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium
- name: Run E2E tests
env:
NODE_ENV: development
CI: "true"
run: pnpm exec vitest e2e --retry 1 --maxWorkers 4
- name: Upload E2E artifacts
if: failure() && !env.ACT
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: e2e/test-results/
retention-days: 7
--tag homarr-ci "${secret_args[@]}" .
preview-amd64:
name: Build amd64 preview image
@@ -320,10 +273,10 @@ jobs:
publish-preview:
name: Publish multi-platform preview image
needs: [container, preview-amd64, preview-arm64]
needs: [fast-gate, preview-amd64, preview-arm64]
if: |
!cancelled() &&
needs.container.result == 'success' &&
needs.fast-gate.result == 'success' &&
needs.preview-amd64.result == 'success' &&
needs.preview-arm64.result == 'success' &&
(
@@ -22,7 +22,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
client-id: ${{ secrets.CROWDIN_APP_ID }}
client-id: ${{ vars.CROWDIN_CLIENT_ID }}
permission-contents: write # required to commit to crowdin branch
permission-pull-requests: write # required to create pull request
@@ -52,7 +52,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
permission-pull-requests: write
- name: Approve PR
env:
@@ -64,7 +64,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
permission-contents: write # write to beta branch (due to merge)
permission-pull-requests: write # merge pull request
- id: automerge
+26 -3
View File
@@ -40,6 +40,7 @@ jobs:
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
git_ref: ${{ steps.read-git-ref.outputs.ref || steps.read-semver.outputs.version || github.ref }}
skipped: ${{ env.SKIP_RELEASE }}
upstream_sync: ${{ steps.update-upstream.outcome }}
steps:
- run: echo "Manual dispatch redeploys the latest existing release without creating a new one"
if: env.SKIP_RELEASE == 'true'
@@ -54,7 +55,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
permission-contents: write # required to commit package.json & changelog changes, merge them to dev / next and publish the release
- uses: actions/checkout@v7
@@ -115,8 +116,10 @@ jobs:
run: |
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Update dev / next branch
id: update-upstream
if: env.SKIP_RELEASE == 'false'
continue-on-error: true # Prevent pipeline from failing when merge fails
# Finish publishing the already-created release, then fail in the publish job if synchronization did not work.
continue-on-error: true
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
UPSTREAM_BRANCH: ${{ github.ref_name == 'main' && 'dev' || 'next' }}
@@ -274,7 +277,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
permission-contents: write # required to publish the release
- name: Log in to the Container registry
@@ -311,6 +314,12 @@ jobs:
run: |
gh release edit --repo ${{ github.repository }} ${{ needs.release.outputs.version }} --draft=false
- name: Verify dev / next synchronization
if: needs.release.outputs.skipped == 'false' && needs.release.outputs.upstream_sync != 'success'
run: |
echo "::error::The released commit was not synchronized back to the upstream development branch."
exit 1
- name: Discord notification
if: github.event_name != 'workflow_dispatch' || inputs['send-notifications']
env:
@@ -318,3 +327,17 @@ jobs:
uses: Ilshidur/action-discord@0.4.0
with:
args: "Successfully deployed images for branch **${{ github.ref_name }}**. Tagged as **${{env.NEXT_VERSION}}**."
notify-failure:
name: Notify deployment failure
if: ${{ always() && (failure() || cancelled()) && (github.event_name != 'workflow_dispatch' || inputs['send-notifications']) }}
needs: [release, build-amd64, build-arm64, extract-asset-amd64, extract-asset-arm64, publish]
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Discord failure notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.4.0
with:
args: "The deployment for branch **${{ github.ref_name }}** failed or was cancelled: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
+5 -1
View File
@@ -37,12 +37,16 @@ jobs:
run: pnpm turbo build --filter=@homarr/docs
env:
CI: "true"
- name: Validate exported links and search
run: |
pnpm --filter @homarr/docs validate:links
pnpm --filter @homarr/docs verify:search
- name: Setup Pages
uses: actions/configure-pages@v6
- name: Upload pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: "apps/docs/build"
path: "apps/docs/out"
deploy:
runs-on: ubuntu-latest
+31 -11
View File
@@ -19,10 +19,10 @@ permissions:
jobs:
create-and-merge-pr:
runs-on: ubuntu-latest
timeout-minutes: 2
timeout-minutes: 10
steps:
- name: Discord notification
if: ${{ github.event.inputs.send-notifications }}
if: ${{ github.event_name == 'schedule' || inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.4.0
@@ -38,13 +38,30 @@ jobs:
with:
token: ${{ github.token }}
branch: dev
noNewCommitBehavior: silent
noVersionBumpBehavior: silent
- name: No release needed
if: ${{ steps.semver.outputs.bump == '' || steps.semver.outputs.bump == 'none' }}
run: echo "No release-worthy commits were found since ${{ steps.semver.outputs.current }}." >> "$GITHUB_STEP_SUMMARY"
- name: Create pull request
id: create-pull-request
run: 'gh pr create --title "chore(release): automatic release ${{ steps.semver.outputs.next }}" --body "**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``" --base main --head dev --label automerge'
if: ${{ steps.semver.outputs.bump != '' && steps.semver.outputs.bump != 'none' }}
run: |
pr_url="$(gh pr list --base main --head dev --state open --json url --jq '.[0].url // empty')"
if [[ -z "$pr_url" ]]; then
pr_url="$(gh pr create \
--title "chore(release): automatic release ${{ steps.semver.outputs.next }}" \
--body "**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``" \
--base main \
--head dev \
--label automerge)"
fi
echo "url=$pr_url" >> "$GITHUB_OUTPUT"
echo "pr_number=${pr_url##*/}" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Discord notification
if: ${{ github.event.inputs.send-notifications }}
if: ${{ steps.create-pull-request.outcome == 'success' && (github.event_name == 'schedule' || inputs.send-notifications) }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.4.0
@@ -52,26 +69,29 @@ jobs:
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
- name: Obtain token
id: obtainApprovalToken
if: ${{ steps.create-pull-request.outcome == 'success' }}
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
permission-pull-requests: write
- name: Approve PR
if: ${{ steps.create-pull-request.outcome == 'success' }}
env:
GITHUB_TOKEN: ${{ steps.obtainApprovalToken.outputs.token }}
run: |
gh pr review --approve --body "Automatically approved by GitHub Action"
gh pr review "${{ steps.create-pull-request.outputs.url }}" --approve --body "Automatically approved by GitHub Action"
- name: Obtain token
id: obtainMergeToken
if: ${{ steps.create-pull-request.outcome == 'success' }}
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
permission-contents: write # write to main branch (due to merge)
permission-pull-requests: write # merge pull request
- id: automerge
if: ${{ steps.semver.outputs.bump != 'major' }}
if: ${{ steps.create-pull-request.outcome == 'success' && steps.semver.outputs.bump != 'major' }}
name: automerge
uses: "pascalgn/automerge-action@v0.16.4"
env:
@@ -85,21 +105,21 @@ jobs:
MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
- name: Merged Discord notification
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.event.inputs.send-notifications }}
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && (github.event_name == 'schedule' || inputs.send-notifications) }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.4.0
with:
args: "Merged PR ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
- name: Major Bump Discord notification
if: ${{ steps.semver.outputs.bump == 'major' && github.event.inputs.send-notifications }}
if: ${{ steps.semver.outputs.bump == 'major' && (github.event_name == 'schedule' || inputs.send-notifications) }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.4.0
with:
args: "The release PR must be manually merged because the next version is a major version: ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
- name: Discord Fail Notification
if: failure() && github.event.inputs.send-notifications
if: ${{ failure() && (github.event_name == 'schedule' || inputs.send-notifications) }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.4.0
@@ -1,58 +0,0 @@
name: "[Docs] Algolia Recrawl"
on:
workflow_run:
workflows: ["[Deployment] Documentation"]
types: [completed]
branches: [dev]
workflow_dispatch:
permissions:
contents: read
jobs:
recrawl:
if: >-
${{
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'pull_request')
|| github.event_name == 'workflow_dispatch'
}}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- name: Setup
uses: ./tooling/github/setup
- name: Check Algolia crawler secrets
run: |
missing=0
for secret in ALGOLIA_CRAWLER_ID ALGOLIA_CRAWLER_USER_ID ALGOLIA_CRAWLER_API_KEY; do
if [ -z "${!secret}" ]; then
echo "::warning::Missing repository secret: $secret"
missing=1
fi
done
if [ "$missing" -eq 1 ]; then
echo "Add crawler secrets to enable automatic recrawls."
exit 1
fi
env:
ALGOLIA_CRAWLER_ID: ${{ secrets.ALGOLIA_CRAWLER_ID }}
ALGOLIA_CRAWLER_USER_ID: ${{ secrets.ALGOLIA_CRAWLER_USER_ID }}
ALGOLIA_CRAWLER_API_KEY: ${{ secrets.ALGOLIA_CRAWLER_API_KEY }}
- name: Wait for GitHub Pages to serve the new deployment
run: sleep 60
- name: Trigger Algolia DocSearch recrawl
uses: cssnr/algolia-crawler-action@v2
with:
crawler_id: ${{ secrets.ALGOLIA_CRAWLER_ID }}
crawler_user_id: ${{ secrets.ALGOLIA_CRAWLER_USER_ID }}
crawler_api_key: ${{ secrets.ALGOLIA_CRAWLER_API_KEY }}
- name: Verify search index
working-directory: apps/docs
run: pnpm verify:search
@@ -33,7 +33,7 @@ jobs:
- name: Commit updated contributors
uses: EndBug/add-and-commit@v10
with:
add: "apps/docs/static"
add: "apps/docs/public"
default_author: github_actor
message: "chore(docs): update contributors"
push: true
+3 -31
View File
@@ -26,41 +26,13 @@ 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
with:
add: "apps/docs/static/api/"
add: "apps/docs/public/api/"
default_author: github_actor
message: "chore(docs): update OpenAPI spec"
push: true
+1 -34
View File
@@ -5,39 +5,6 @@ on:
types: [released]
jobs:
trigger-docs-release:
name: Trigger Documentation Release
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
client-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
owner: homarr-labs
repositories: |
documentation
permission-contents: write # required to dispatch repository workflow
- name: Trigger documentation release
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
SOURCE_TAG: ${{ github.event.release.tag_name }}
run: |
curl -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/homarr-labs/documentation/dispatches \
-d @- <<EOF
{
"event_type": "trigger-release",
"client_payload": {
"tag": "${SOURCE_TAG}"
}
}
EOF
update-bug-report-template:
name: Update Bug Report Template
runs-on: ubuntu-latest
@@ -48,7 +15,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
permission-contents: write # required to commit to branch
permission-pull-requests: write # required to create pr & enable automerge
- name: Checkout code
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_CLIENT_ID }}
permission-contents: write # required to commit to branch
permission-pull-requests: write # required to create pr & enable automerge
@@ -24,7 +24,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_CLIENT_ID }}
permission-contents: write # required to commit to branch
permission-pull-requests: write # required to create pr & enable automerge
- name: Checkout code
+29 -12
View File
@@ -2,8 +2,36 @@ name: Workshop
on:
pull_request:
paths:
- ".github/workflows/workshop.yml"
- "apps/workshop/**"
- "apps/docs/**"
- "packages/custom-widgets/**"
- "packages/definitions/**"
- "packages/workshop/**"
- "tooling/typescript/**"
- "patches/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "turbo.json"
- ".npmrc"
push:
branches: [dev, v2, release/v2, main]
paths:
- ".github/workflows/workshop.yml"
- "apps/workshop/**"
- "apps/docs/**"
- "packages/custom-widgets/**"
- "packages/definitions/**"
- "packages/workshop/**"
- "tooling/typescript/**"
- "patches/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "turbo.json"
- ".npmrc"
permissions:
contents: read
@@ -15,21 +43,10 @@ concurrency:
jobs:
validate:
runs-on: ubuntu-24.04
timeout-minutes: 30
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 24.18.0
- uses: pnpm/action-setup@v6
with:
run_install: false
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @homarr/workshop typecheck
- run: pnpm --filter @homarr/docs typecheck
- run: docker compose --env-file apps/workshop/.env.staging.example -f apps/workshop/docker-compose.staging.yml config --quiet
- run: pnpm test:workshop
- run: pnpm test:workshop-image
publish-amd64:
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
+6 -1
View File
@@ -10,7 +10,12 @@
"CHANGELOG.md",
"apps/docs/.docusaurus/**",
"apps/docs/build/**",
"apps/docs/blog/**",
"apps/docs/blog/2022/**",
"apps/docs/blog/2023/**",
"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"
]
+3 -3
View File
@@ -6,7 +6,7 @@
homarr/
├── apps/
│ ├── nextjs/ # Main Next.js application (port 3000)
│ ├── docs/ # Docusaurus 3 documentation site (@homarr/docs)
│ ├── docs/ # Next.js + Fumadocs documentation site (@homarr/docs)
│ ├── tasks/ # Cron-job initialization and scheduling runtime
│ ├── websocket/ # Standalone tRPC WebSocket server (port 3001)
│ └── workshop/ # Go/PocketBase Workshop service
@@ -57,7 +57,7 @@ homarr/
## Documentation Sync
The documentation site lives at `apps/docs/` (Docusaurus 3, `@homarr/docs`).
The documentation site lives at `apps/docs/` (Next.js + Fumadocs, `@homarr/docs`).
When modifying user-facing code, you MUST also update the corresponding documentation:
@@ -75,7 +75,7 @@ When modifying user-facing code, you MUST also update the corresponding document
- `pnpm dev:cli -- dev` — run the developer CLI without installing a global binary
- `pnpm db:seed` — seed default database data explicitly
- `pnpm docker:dev:up` — start the Redis development service in the background
- `pnpm dev:docs` — Docusaurus docs site only
- `pnpm dev:docs` — Fumadocs site only
- `pnpm turbo build` — build all packages
- `pnpm turbo build --filter=@homarr/docs` — build docs only
- `pnpm turbo typecheck` — typecheck all packages
+2 -1
View File
@@ -13,6 +13,7 @@
# next.js
/.next/
/out/
/.source/
# production
/build
@@ -55,4 +56,4 @@ static/data/configs
# CI Stuff
screenshots
test-results
test-results
+18
View File
@@ -0,0 +1,18 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
<!-- END:nextjs-agent-rules -->
## Fumadocs workflow
- Start with `README.md`, `src/lib/source.ts`, `source.config.ts`, and the matching route under `src/app/`.
- Read current [Fumadocs documentation](https://fumadocs.dev/docs) with Context7 before changing framework APIs. Check the installed version and types; latest examples may use a different search adapter.
- Preserve static export: search uses `staticGET` with the matching static client. Do not introduce a request-time search server into this app.
- Keep integration/widget metadata in the existing typed definitions. Check both rendered HTML and processed Markdown when changing MDX components; JSX alone does not guarantee readable search or AI output.
- Run the README validation commands after content or navigation changes. Search results and table-of-contents links must resolve to rendered anchors.
- Kapa is an independent hosted assistant. Keep its keyboard shortcut disabled so it does not override docs search; follow the README for its build configuration and crawler setup.
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+37
View File
@@ -0,0 +1,37 @@
# Homarr Documentation
## Register
product
## Users
People who run Homarr, administrators configuring self-hosted infrastructure, and contributors extending Homarr. They arrive with a concrete task such as installing Homarr, connecting a service, configuring a widget, managing users, or building a custom widget, and need an answer they can scan and apply quickly.
## Product Purpose
The documentation is the complete, authoritative operating guide for Homarr. It keeps setup steps, configuration contracts, screenshots, generated integration and widget metadata, API references, and Workshop tooling next to the application code. Success means a reader can find the right page quickly, understand the next action, copy reliable instructions, and verify the result without searching elsewhere.
## Brand Personality
Focused, dependable, and approachable. The writing is direct and practical. The interface should feel calm and familiar during long technical sessions while retaining Homarr's red identity.
## Anti-references
- Generic marketing layouts that replace Homarr's product identity with unrelated slogans or decorative conversion copy.
- Decorative dashboard aesthetics that compete with reading, including glass effects, large radii, card grids, and gratuitous motion.
- Generic generated-docs styling that obscures Homarr's identity or makes navigation feel detached from the main product.
- Dense reference portals that expose structure without giving readers a clear path through a task.
## Design Principles
1. Put the reader's task first: search, navigation, page outline, and the answer must remain easy to reach.
2. Preserve one source of truth: typed Homarr metadata and existing custom components stay connected to the monorepo.
3. Prefer familiar documentation patterns: the interface should disappear while a reader scans, copies, and follows instructions.
4. Make complete content feel manageable through strong hierarchy, progressive disclosure, and precise cross-links.
5. Treat machine readers as users through canonical Markdown, structured metadata, and standard LLM discovery endpoints.
6. Keep the root landing page recognizably Homarr: preserve its self-hosted dashboard promise and real product evidence while documentation routes remain task-first.
## Accessibility & Inclusion
Meet WCAG 2.2 AA for color contrast, keyboard operation, focus visibility, landmarks, headings, and interactive controls. Respect reduced-motion and system color-scheme preferences. Diagrams, screenshots, icons, and emoji must have meaningful text alternatives when they carry information.
+55 -83
View File
@@ -1,112 +1,84 @@
# Homarr Documentation (`@homarr/docs`)
# Homarr documentation (`@homarr/docs`)
The official [homarr.dev](https://homarr.dev) documentation site, built with [Docusaurus 3](https://docusaurus.io/) and living inside the Homarr monorepo.
The source for [homarr.dev](https://homarr.dev), built with Next.js and Fumadocs UI inside the Homarr monorepo.
## Why it's in the monorepo
## Develop
Having documentation alongside the application code enables:
- **Two-way linking** — the docs app imports from `@homarr/definitions` for type-safe integration/widget metadata, and the main app references doc paths via generated sitemap types
- **Atomic PRs** — code changes and their documentation updates ship in the same pull request
- **Shared tooling** — same Node version, pnpm catalog, oxlint/oxfmt, CI pipeline
- **Build-time validation** — Docusaurus strict mode catches broken links and references at build time
## Development
From the **monorepo root**:
From the repository root:
```bash
# Start the docs dev server (port 3003)
pnpm dev:docs
```
# Or from within this directory
pnpm dev
The site runs on `http://127.0.0.1:3003`. To use a local Workshop API:
# Point the docs at a local PocketBase instance
```bash
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
```
The standalone docs use `WORKSHOP_API_URL` for PocketBase and `HOMARR_WEBSITE_URL` for the public site origin. The old
`WORKSHOP_URL` name is a deprecated one-release alias.
## Build
## Validate
```bash
# Build docs only
pnpm turbo build --filter=@homarr/docs
# Build everything (docs + main app + tasks + websocket)
pnpm build
pnpm --filter @homarr/docs typecheck
pnpm --filter @homarr/docs build
pnpm --filter @homarr/docs validate:links
pnpm --filter @homarr/docs verify:search
```
## Lint & Format
The static export is written to `out/`. The build validates that every registered integration and widget has a docs
page. The link check validates internal Markdown and MDX links and rendered anchors against that export.
The search check exercises the exported index with title, heading, and body queries and verifies result destinations.
Uses oxlint and oxfmt (same as the rest of the monorepo):
## Write content
```bash
pnpm lint # oxlint
pnpm format # oxfmt (check)
pnpm format:fix # oxfmt (fix)
```
Documentation lives in `docs/`; project posts live in `blog/`. Navigation is controlled by the nearest `meta.json`.
Integration and widget folders pair typed `index.ts` metadata with `index.mdx` content.
## Typecheck
Use standard Markdown and MDX. Shared components live in `src/components/`. Mermaid fences, tabs, callouts, zoomable
images, and syntax-highlighted code are supported by the Fumadocs pipeline.
```bash
pnpm typecheck
```
When a user-facing change affects setup or behavior, update the matching page in the same pull request. See the
[documentation-sync skill](../../.agents/skills/documentation-sync/SKILL.md) for the code-to-content map.
## Content Structure
## Published formats
```
docs/
├── getting-started/ # Installation guides, glossary, prerequisites
├── management/ # Boards, apps, users, integrations, settings, tasks, API
├── integrations/ # 56 integration guides (one folder each)
├── widgets/ # 43 widget guides (one folder each)
├── advanced/ # SSO, env vars, proxy, styling, CLI, development
└── community/ # FAQ, donate, license, translations, get-in-touch
```
- `/docs` — searchable documentation with typed navigation
- `/api-reference` — interactive reference generated from the OpenAPI schema
- `/llms.txt` — compact documentation and blog index for agents
- `/llms-full.txt` — complete processed Markdown corpus
- `/llms.mdx/docs/.../content.md` — raw Markdown for each docs page
- `/llms.mdx/blog/.../content.md` — raw Markdown for each blog post
- `/blog/rss.xml` — RSS feed for project posts
Each integration/widget doc follows a consistent pattern:
Every docs page and blog post includes copy-Markdown and view-source actions. Search is generated at build time and
runs locally in the browser; it does not depend on an external crawler. Markdown exports and search resolve
integration credentials and widget defaults from the same typed metadata used by the pages.
The getting-started overview and complete Custom JSX component catalog are also included, with property types,
binding contracts, and blocked capabilities taken from the published catalog JSON.
- `index.ts` — typed metadata (`IntegrationDefinition` / `WidgetDefinition`)
- `index.mdx` — content using shared React components (`IntegrationHeader`, `WidgetHeader`, etc.)
Published Docusaurus `/docs/category/...` addresses remain available as static compatibility pages. They forward to
the corresponding overview or developer setup guide, preserving query strings and anchors, and provide a direct
link when JavaScript is disabled. Their canonical metadata points to the destination; compatibility pages are
excluded from search and the sitemap.
## Importing from homarr packages
## Kapa AI
The docs app has `@homarr/definitions` as a workspace dependency. You can import integration kinds, widget kinds, and other definitions directly:
The **Ask AI** launcher uses Homarr's existing public Kapa Website ID. The root layout loads the widget once across
client-side navigation. **Search** and `Ctrl+K` / `Cmd+K` always use Fumadocs search, including when Kapa is unavailable.
```typescript
import { IntegrationKind } from "@homarr/definitions";
```
Set `KAPA_WEBSITE_ID` at build time to use a different Kapa Website Widget integration. An explicitly empty value
disables the widget, for example `KAPA_WEBSITE_ID= pnpm --filter @homarr/docs build`. This is a public integration ID
included in exported pages, not a Kapa API key. Rebuild the static export after changing it.
This enables type-safe references to homarr domain concepts within documentation code.
In the Kapa dashboard, confirm the integration is live and enable the production and preview domains. Restoring the
script does not verify that the historical integration is still active. Follow the
[Kapa widget setup](https://docs.kapa.ai/integrations/website-widget/quickstart) for domain restrictions and CSP settings.
## Search (Algolia DocSearch)
After changing the documentation structure, review the Website Crawl source in Kapa: use the deployed `/docs` URL as
the start URL and preview `main` as the content selector. Replace any old Docusaurus-specific selector and check that
titles, headings, code samples, and integration details appear in the extracted Markdown. Docs are exported as HTML;
the site also publishes `/sitemap.xml` and permits crawling in `/robots.txt`. The `/llms.txt` exports do not configure
Kapa ingestion automatically. See [Kapa source setup](https://docs.kapa.ai/getting-started/index-your-first-source).
Search uses the `Docusaurus` Algolia index (crawler `cd77a285-2756-4557-bf21-ee703748df15`). A separate `markdown` index is built for LLM markdown indexing.
Crawler reference config: [`docsearch.config.js`](docsearch.config.js).
**Verify search health:**
```bash
pnpm verify:search
```
## Tech Stack
| Layer | Technology |
| -------------- | ------------------------------------------- |
| Site generator | Docusaurus 3 (`@docusaurus/preset-classic`) |
| Styling | Tailwind CSS 4 + Docusaurus Infima |
| Icons | `@tabler/icons-react` |
| Diagrams | Mermaid (`@docusaurus/theme-mermaid`) |
| Search | Algolia DocSearch |
| Analytics | PostHog |
| Charts | @nivo/line |
## Contributing
When a Homarr change affects user-facing behavior, update the corresponding documentation in this app. See the
[documentation-sync skill](../../.agents/skills/documentation-sync/SKILL.md) for the code-to-documentation mapping.
Before publishing, check that Ask AI opens after navigating between pages, ask a documentation question, and verify
its citations point to the current docs. In a separate check, block `widget.kapa.ai` and confirm search still works.
@@ -1,6 +1,7 @@
---
slug: documentation-migration
title: Migration of Documentation
date: 2022-06-22
authors:
- ajnart
- manuel-rw
@@ -12,7 +13,7 @@ The documentation has been re-written partly and includes now many animations, g
We are still figuring things out and highly depend on your feedback.
<!-- truncate -->
{/* Historical excerpt boundary. */}
## Why we migrated
@@ -32,4 +33,4 @@ After we've reviewed your changes, we'll merge your PR and this documentation wi
## TL;DR
The documentation on GitHub Wiki will soon disappear. It will be replaced with this Docusaurus-powered website. You'll find much more information here with higher detail and searchability.
The documentation on GitHub Wiki will soon disappear. It will be replaced with this Docusaurus-powered website. You'll find much more information here with higher detail and searchability.
@@ -1,6 +1,7 @@
---
slug: translations
title: Help us make Homarr multilingual
date: 2022-08-28
authors:
- ajnart
- manuel-rw
@@ -13,8 +14,8 @@ We've been working hard to offer Homarr in many languages. The next update of Ho
![](img/homarr-we-need-you.webp) <br/>
Yes, we need **YOU**! We want to make Homarr accessible to as many people as possible. Thus, we need to translate all content into many different languages. We need your help for that! [You can help us now to translate Homarr into as many languages as possible](/docs/community/translations).
<!-- truncate -->
{/* Historical excerpt boundary. */}
### About global translations / translations per user
We are aware that many people would like to configure translations globally or configure language per user. Currently, translations are stored in the Cookies, which will expire after some time (currently, it's set to 30 days). We will implement more granular language settings in future versions of Homarr, since it requires major changes to Homarr. Stay tuned! 😎
We are aware that many people would like to configure translations globally or configure language per user. Currently, translations are stored in the Cookies, which will expire after some time (currently, it's set to 30 days). We will implement more granular language settings in future versions of Homarr, since it requires major changes to Homarr. Stay tuned! 😎
@@ -1,5 +1,6 @@
---
title: "We present: Homarr 0.11 - the most complex Homarr version yet!"
date: 2023-01-11
authors:
- ajnart
- manuel-rw
@@ -20,7 +21,7 @@ It contains many breaking changes and huge improvements to your favourite dashbo
- Huge design and usability improvements to older components to Homarr
- Better handeling of exceptions and unexpected issues
<!-- truncate -->
{/* Historical excerpt boundary. */}
## Complete implementation of highly customizable drag and drop library "Gridstack"
@@ -1,5 +1,6 @@
---
title: "We present: Homarr 0.12 - The fastest homarr version ever!"
date: 2023-04-16
authors:
- ajnart
- manuel-rw
@@ -15,14 +16,12 @@ This applies to all widgets, but in the case of the IFrame widget, it enables yo
- Integrate monitoring tools like Grafana, Splunk or Elasticsearch
- Integrate your services (for example Sonarr, Radarr and Overseerr) directly into Homarr
![](https://media.discordapp.net/attachments/988335098493542410/1094665745226539168/update-teaser.png?width=1046&height=666)
## Edit mode password
We've added a setting to disable editing completely. This will allow you to restrict edit access without password-protection.
use the `EDIT_MODE_PASSWORD` ENV variable to set a password for edit mode.
turn on `DISABLE_EDIT_MODE=TRUE` to disable edit mode on startup.
<!-- truncate -->
{/* Historical excerpt boundary. */}
## 😎 Blazingly fast
We've implemented a new caching system, that will cache all data in your browser, meaning that Homarr will load within milliseconds, as if it was *powered by rust and blazingly fast* !!!
@@ -72,4 +71,4 @@ Now, you can use the following environment variables to configure this:
- ``DOCKER_HOST``
- ``DOCKER_PORT``
**Full Changelog**: https://github.com/ajnart/homarr/compare/v0.11.5...v0.12.0
**Full Changelog**: https://github.com/ajnart/homarr/compare/v0.11.5...v0.12.0
@@ -1,5 +1,6 @@
---
title: "Authentication in Homarr - 0.14 is here!"
date: 2023-11-10
authors:
- ajnart
- manuel-rw
@@ -23,7 +24,7 @@ provide you a viable solution today.
- 📝 Overhauled notepad widget with a complex graphical editor that enables easy editing right from your browser.
- :globe_with_meridians: Live Translation on Crowdin that enable you to directly translate using images to see what exactly you're translating
<!-- truncate -->
{/* Historical excerpt boundary. */}
## Breaking changes
@@ -83,4 +84,4 @@ We are aware that this update has many breaking changes and may confuse some use
For that reason, we updated our documentation and will enhance it further within the next few weeks.
We've also added this small button at the bottom of the page were you can ask **your questions** to an AI assistant:
![image](https://github.com/ajnart/homarr/assets/30572287/a56f721a-d022-42ad-813a-d85c89462a8c)
![image](https://github.com/ajnart/homarr/assets/30572287/a56f721a-d022-42ad-813a-d85c89462a8c)
@@ -1,5 +1,6 @@
---
title: "Updated documentation with better organization & design"
date: 2023-12-22
authors:
- manuel-rw
tags: [homarr, documentation, design, docs]
@@ -8,7 +9,7 @@ tags: [homarr, documentation, design, docs]
We'd like to let you know that we've updated our documentation and reorganized the pages.
This means, that some links may no longer be valid - we apologise if this causes any inconvenience for you.
<!-- truncate -->
{/* Historical excerpt boundary. */}
A few highlights include:
@@ -18,4 +19,4 @@ A few highlights include:
- Bug fixes for image zoom
- Added documentation for management pages and [new CLI tool](/docs/advanced/command-line/password-recovery/)
Please [let us know](/docs/community/get-in-touch) is something is not working as expected.
Please [let us know](/docs/community/get-in-touch) is something is not working as expected.
@@ -1,9 +1,10 @@
---
title: Homarr 1.0 - complete rewrite of all systems
date: 2024-09-23
authors:
- manuel-rw
---
# Homarr 1.0 - complete rewrite of all systems
We proudly present to you - Homarr ``1.0``.
We have started work on this new version back in 2023 and have worked tirelessly for months on this brand-new dashboard.
@@ -28,7 +29,7 @@ reliability and scalability compared to < ``1.0``.
- **Improved performance**: We have upgraded the underlying framework of Homarr and take advantage
of an in-memory message bus and websockets to deliver data near realtime to you.
<!-- truncate -->
{/* Historical excerpt boundary. */}
## Breaking changes
:::warning
@@ -1,16 +1,16 @@
---
title: Open Beta 1.0
date: 2024-12-17
authors:
- meierschlumpf
---
# Open Beta 1.0
We are proud to announce the open beta of Homarr 1.0. This is a major milestone for us and we are excited to share it with you.
The open beta allows us to polish the last rough edges and gather feedback from you.
We are looking forward to your feedback and hope you enjoy the new features.
Let us know throgh discord or a [github issues](https://github.com/homarr-labs/homarr/issues/new/choose) if you encounter any issues.
<!-- truncate -->
{/* Historical excerpt boundary. */}
## How to use the beta with docker
@@ -36,4 +36,4 @@ services:
Then, run `docker compose up -d` in the same directory. This will start the Homarr container in the background.
Please note that the mounts have changed and it's not backwards compatible with versions before 1.0.0-beta.1.
You can find a list of all breaking changes [here](/blog/2024/09/23/version-1.0)
You can find a list of all breaking changes [here](/blog/2024/09/23/version-1.0)
@@ -1,14 +1,14 @@
---
title: Migrate from 0.15.10+ to 1.0.0
date: 2025-01-19
authors:
- meierschlumpf
---
# Migrate from 0.15.10+ to 1.0.0
In this guide, we will show you how to migrate from Homarr 0.15.10+ to 1.0.0.
If you are still on a version before 0.15.10, please update to 0.15.10 where we introduced the migration export page.
<!-- truncate -->
{/* Historical excerpt boundary. */}
## Preserve your old installation
@@ -22,7 +22,7 @@ Plan to recreate your apps, integrations, and boards manually in the new install
We highly recommend to create a parallel installation of Homarr 1.0.0.
So if something with for example the import does not quite work, the data in the old installation is still available.
To do this you can follow the appropriate [installation guide](https://homarr.dev/docs/category/installation-1) for your system.
To do this you can follow the appropriate [installation guide](/docs/getting-started) for your system.
Please note that if you are using docker you need to specify a diffrent external port for the new installation as the old one had the same port.
So in your compose file the ports would be `7576:7575` instead of `7575:7575`.
@@ -1,14 +1,14 @@
---
title: How to use Argus with Homarr
date: 2025-08-02
authors:
- meierschlumpf
---
# How to use Argus with Homarr
In `v1.32.0` we added the endpoint `/api/info` which returns the current version of Homarr and can therefore be used in Argus to watch for updates.
This guide will show you how to set up a user with apiKey to access the endpoint without providing admin access through it.
<!-- truncate -->
{/* Historical excerpt boundary. */}
![Argus](./img/argus.png)
@@ -64,4 +64,4 @@ Now you can configure Argus to use the user and apiKey we created.
![Argus configuration](./img/argus-configuration.png)
Replace the `<secret>` with the apiKey you created and the deployed version url with the url of your Homarr instance.
Replace the `<secret>` with the apiKey you created and the deployed version url with the url of your Homarr instance.
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,169 @@
---
title: Homarr 2.0.0 - Workshop, Custom Widgets and a rebuilt board
date: 2026-09-03
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
---
# Homarr 2.0.0 - Workshop, Custom Widgets and a rebuilt board
First, thank you.
Many of you used the v2 demo when it was still rough, found bugs and came back to test the next build. Others translated Homarr, answered questions, contributed code or simply kept using it. We would not be shipping this without you.
We are also planning a small batch of **free, limited-edition Homarr swag** for Workshop contributors and a few randomly picked community members. It is not for sale and it is not for profit. No Homarr merch empire, sorry.
Homarr 2.0 is an upgrade, not a brand-new app wearing the same logo. Here is what actually changed.
![A populated Homarr 2.0 dashboard with compact widgets, an Assistant, and an application rail](./img/homarr-v2-dashboard.webp)
{/* Historical excerpt boundary. */}
:::warning Back up before upgrading
Database migrations run automatically when Homarr starts. 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.
:::
## Custom Widgets v2 and Workshop
### Build almost any widget
**This is the big one.** We think it is the most important Homarr update we have made.
Custom Widgets v2 lets you build widgets from JSX, API requests, actions and typed settings. Write one yourself or describe it to Assistant, then test it in the workbench. We think this is enough to make almost any dashboard widget—even that oddly specific component only three people need. Those three people no longer have to wait for us.
This was partly inspired by [Google's upcoming Create My Widget feature](https://blog.google/products-and-platforms/platforms/android/gemini-intelligence/): describe a widget in normal language and let AI make it. Coding knowledge is optional. Knowing what the API does still helps; AI has not fixed bad documentation. Yet.
![The Custom Widget v2 workbench after a live exchange-rate request successfully renders its preview](./img/custom-widget-workbench.webp)
A `homarr-custom-widget-v2` definition can use several fixed-origin API sources. Credentials are encrypted separately and stay out of exports and Workshop submissions. Actions support `GET`, `POST`, `PUT`, `PATCH` and `DELETE`; requests have host checks, timeouts, size limits, permissions and rate limits. Custom Widgets remain **Beta** in 2.0.
### Share widgets and Custom CSS
**Workshop is the other half.** Publish and install Custom Widgets, then update, vote on, comment on and report submissions. Workshop is not only for widgets: you can share your **Custom CSS** there too. Deployment URLs and credentials stay on your Homarr instance.
![The Community Workshop catalog inside Custom Widget management](./img/community-workshop.webp)
:::warning Workshop is moderated
AI makes widgets easy to produce. It does not make the first thing it coughs up worth publishing. **Do not dump untested slop into Workshop.** Test it, add a useful screenshot and explain its setup. Workshop is moderated.
:::
### The ultimate Homarr update
We have called this the **"ultimate Homarr update"**. Not because work stops, but because every new service or widget idea no longer needs a full Homarr release; the community can build and share it instead.
## Board editing, rebuilt
### Drag and drop
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.
![A Calendar tile moving across the rebuilt Homarr 2.0 board grid](./img/drag-and-drop.gif)
### Goodbye Groups and sections, bonjour Containers
Groups and dynamic sections did nearly the same job, so **Containers replace both**. They hold apps, widgets and other Containers. Hold <kbd>Ctrl</kbd> or <kbd>Command</kbd> to move several items at once.
### One board, several layouts
Boards also have Base and Mobile layouts, plus optional breakpoints. **Reset from Base** creates a Mobile starting point without duplicating the board.
## Homarr Assistant
### Work with live Homarr data
Assistant reads live Homarr data and has tools for boards, apps, integrations, widgets, Docker and supported services. It can answer questions, make approved changes and build Custom Widgets. It follows your permissions: reads can run automatically, while changes require approval by default.
![The Homarr Assistant expanded over a live dashboard with health, exploration, media, and board actions](./img/assistant-control-surface.webp)
Open it with <kbd>Shift</kbd> + <kbd>A</kbd>. <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>/</kbd> searches it.
### The Homarr provider
:::note
Assistant ships with a **"Homarr provider"**. It is free, will never offer paid plans, and is paid for by [Ajnart](https://github.com/ajnart) using the ad revenue from this website.
It only uses zero-data-retention (ZDR) providers. [The provider is open source](https://github.com/homarr-labs/homarr/blob/release/v2/apps/workshop/homarr_provider.go), and neither prompts nor responses are logged. We are not selling your data. Pinky promise.
:::
### External assistants through MCP
External assistants can use the same permission-checked tools through `/api/mcp`, with an API key or OAuth 2.1 with PKCE.
## The rest (there is a lot)
### Brand-new onboarding
A six-stage setup studio handles the first admin, core settings, Docker discovery, apps, integrations and the first board.
![The Homarr 2.0 setup studio on its Essentials step, with all six setup stages shown](./img/onboarding-studio.webp)
### Board switcher
Boards now live in a searchable gallery with keyboard navigation. Open it from the header or press <kbd>Shift</kbd> + <kbd>C</kbd>.
![A visual board card inside the searchable Homarr board switcher](./img/board-switcher.webp)
### Command menu and keybinds
<kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>K</kbd> finds resources, pages, settings, users, groups and commands. More
keybinds cover Assistant, editing, selection and advanced widget views.
### Customizable header
Pick which buttons appear, put them on the left, center or right, and change their order.
![The Header studio showing left, center, and right zones plus available controls](./img/header-studio.webp)
### Instance branding
Change the name, logo, favicon, colors, corner styles and login page. Global CSS applies instance-wide; board CSS can override it. Only give that power to admins you trust. CSS remains CSS.
![Instance branding controls for application identity, palette, corner style, and global CSS](./img/instance-branding.webp)
### Widgets
Add content without leaving the board. Settings have live previews, and supported widgets have advanced views. Weather and Clock were redesigned; Air Quality, Countdown and Timer are new.
### Docker and Podman
`DOCKER_ENDPOINTS` supports several hosts. Assisted setup and container labels can create apps, integrations, Containers, widgets and health checks.
![Assisted Docker setup listing discovered services that can become apps or integrations](./img/docker-assisted-setup.webp)
Homepage-compatible labels work too.
### Permissions
Groups use a new Viewer, Editor and Admin matrix. The API, Assistant and MCP follow the same rules. LDAP can create users on first sign-in; OIDC can link verified accounts and synchronize groups.
### Performance
Board startup work runs in parallel, heavy screens load when opened, and identical integration requests share work instead of doing it twice.
## Before upgrading
- Keep a backup and use the final release image, not a prerelease tag.
- Legacy Custom JSX definitions enter a migration state. Homarr keeps encrypted credentials and an archived copy until the v2 definition validates.
- Mobile layouts place rail content in the main flow instead of displaying rails.
- Custom Widgets remain Beta.
- Assistant and MCP do not bypass permissions.
## Links
- Try the [Homarr 2.0 preview](https://app-v2.preview.homarr.dev).
- Follow the [v2 beta setup 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).
+1 -1
View File
@@ -5,7 +5,7 @@
"tsx": true,
"tailwind": {
"config": "",
"css": "src/css/custom.css",
"css": "src/app/global.css",
"baseColor": "mist",
"cssVariables": true,
"prefix": ""
-9
View File
@@ -1,9 +0,0 @@
{
"label": "Advanced",
"collapsed": true,
"position": 5,
"link": {
"type": "generated-index",
"title": "Advanced Concepts or features"
}
}
@@ -1,4 +0,0 @@
{
"collapsed": true,
"label": "Command line interface"
}
@@ -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
@@ -14,4 +14,4 @@ homarr fix-usernames
This will convert all credential usernames to lowercase and update the database accordingly.
![](./img/screenshot-cli.png)
![Terminal output confirming credential usernames were converted to lowercase](./img/screenshot-cli.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+26 -1
View File
@@ -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
@@ -14,7 +15,31 @@ docker exec -it <container-id> /bin/bash
homarr <command>
```
In QNAP Container Station, open the Homarr container's **Execute** action and select `/bin/bash`.
### QNAP Container Station
1. Open **Container Station**.
![Container Station in the QNAP application menu](./img/334116069-e7ec4726-3df7-4dc6-a3e0-b745e7eef17f.png)
2. Select **Containers**.
![Containers menu in QNAP Container Station](./img/334116205-aa09ce40-0701-4f7d-b9f9-b431b9d207c5.png)
3. Select the Homarr container.
![Homarr container selected in QNAP Container Station](./img/334116303-714b5e07-b9de-4ec0-a2bc-def7ee38750f.png)
4. Select **Execute**.
![Execute action for the Homarr container in QNAP Container Station](./img/334116411-df1482f9-3aa9-4686-b77f-e0dca09e60b2.png)
5. Select `/bin/bash`, then select **Execute**.
![Bash selected in the QNAP execute console](./img/334116518-e1ddb515-99c7-4159-81bf-76e889d1e808.png)
6. Run `homarr <command>` in the console.
![Bash console open for Homarr in QNAP Container Station](./img/334116642-08c4f204-5cd0-4e4e-a785-0142e1b279ca.png)
See the pages in this section for available commands.
@@ -0,0 +1,4 @@
{
"title": "Command line interface",
"pages": ["index", "users", "integrations", "password-recovery", "fix-usernames", "development"]
}
@@ -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.
![](./img/screenshot-cli.png)
![Reset-password command help showing that username is required](./img/screenshot-cli.png)
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.
![](./img/screenshot-cli-result.png)
![Terminal output confirming a password reset and session deletion](./img/screenshot-cli-result.png)
@@ -1,8 +0,0 @@
{
"label": "Developer guides",
"position": 6,
"collapsed": true,
"link": {
"type": "generated-index"
}
}
@@ -57,8 +57,8 @@ Homarr becomes the instance administrator.
Use `pnpm docker:dev:up` instead when you only need Redis in the background. Running the migration also seeds the
default data; use `pnpm db:seed` to seed it explicitly later.
Workshop contributors can set up PocketBase and Docusaurus with the dedicated
[Workshop development guide](./workshop).
Workshop contributors can set up PocketBase and Fumadocs with the dedicated
[Workshop development guide](/docs/advanced/development/workshop).
### Useful commands
@@ -69,7 +69,7 @@ Workshop contributors can set up PocketBase and Docusaurus with the dedicated
- `pnpm dev:cli -- build --pr <number>` — build a pull request from a temporary checkout.
- `pnpm dev:cli -- rebuild <name>` — rebuild an image from its recorded checkout or pull request.
- `pnpm dev:cli:install` — optionally install the developer CLI as a `homarr` binary.
- `pnpm dev:docs` — Docusaurus only on port 3003.
- `pnpm dev:docs` — Fumadocs on port 3003.
- `pnpm docker:dev:up` — Redis development service in the background.
- `pnpm cli` — Homarr operations CLI.
- `pnpm db:migration:sqlite:run` — create or update a SQLite database.
@@ -82,3 +82,43 @@ Workshop contributors can set up PocketBase and Docusaurus with the dedicated
- `pnpm build` — production monorepo build.
- `docker build -t homarr .` — build a production Docker image.
- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:<name>` — run it.
## CI and focused validation
The main CI workflow runs lint, typechecking, affected workspace builds, workspace configuration checks, OpenAPI validation, and Custom Widget architecture and bundle checks. Unit and E2E suites do not run in CI. Preview images publish after the Fast gate and both architecture builds succeed; browser tests do not block publication. The container job builds an amd64 image when no preview image was built.
Workshop CI runs only when its workflow or Docker build inputs change. It validates Compose configuration and publishes production images on its configured release branches. Its Docker integration and image tests are manual; workspace typechecks run in the main Fast gate.
Choose the smallest relevant suite when changing behavior:
| Command | Scope | Requirements |
| ------------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
| `pnpm test <file>` | Focused unit or contract tests | Workspace dependencies |
| `pnpm test` | All unit and contract tests, without Docker integration suites | Workspace dependencies |
| `pnpm test:coverage` | Unit and contract tests with coverage | Workspace dependencies |
| `pnpm test:integration <file>` | Docker-backed service and database compatibility tests | Docker |
| `pnpm test:e2e <file>` | Browser and container scenarios | Docker; Playwright Chromium and system dependencies for browser tests |
| `pnpm test:docs-screenshots` | Regenerate Assistant documentation screenshots | Same browser prerequisites |
| `pnpm test:workshop` / `pnpm test:workshop-image` | Workshop integration / production-image validation | Docker |
Omit `<file>` to run all tests in that command's scope. E2E and screenshot commands use a local image tagged `homarr-e2e`, or an image selected with `HOMARR_E2E_IMAGE`. The default commands run once without automatic retries; coverage is opt-in. The screenshot generator is excluded from both unit and E2E commands.
Keep behavioral coverage for authorization, migrations, persistence, and widget execution. Avoid tests that only search source code for particular component names, JSX, CSS, or documentation wording.
## Documentation development
Run `pnpm dev:docs` to open the Next.js and Fumadocs site at `http://127.0.0.1:3003`. Content lives in
`apps/docs/docs/`; the nearest `meta.json` controls navigation. Keep integration and widget setup metadata in their
typed `index.ts` files alongside the page.
After changing content, navigation, or shared MDX components, run:
```sh
pnpm --filter @homarr/docs build
pnpm --filter @homarr/docs validate:links
pnpm --filter @homarr/docs verify:search
```
The build writes a static site to `apps/docs/out/`. Check heading links, search results, and the page's Markdown export
when changing shared components. The [Fumadocs documentation](https://fumadocs.dev/docs) describes supported components
and source APIs; match examples to the versions installed in this repository.
@@ -0,0 +1,4 @@
{
"title": "Developer guides",
"pages": ["getting-started", "widgets-and-integrations", "kubernetes", "workshop", "workshop-operator"]
}
@@ -34,6 +34,21 @@ Workshop uses PocketBase. The production image serves the Workshop website and A
Public URL variables accept HTTP(S) origins without embedded credentials, query strings, or fragments. Restart the
container after changing runtime configuration.
## Documentation search and AI
The documentation uses Fumadocs search with an index generated during the website build. Search runs in the browser
and needs no hosted search credentials or crawler.
The **Ask AI** launcher connects to Kapa separately. `KAPA_WEBSITE_ID` is a public website integration ID, read when
building the documentation. The default uses Homarr's integration. For a private deployment, pass your own ID or
an empty value to disable it: `docker build --build-arg KAPA_WEBSITE_ID= -f apps/workshop/Dockerfile .`.
Changing this value requires rebuilding the image; setting it on an already-built container has no effect.
For your own Kapa integration, enable the deployment domain in Kapa and configure a Website Crawl source for your
published docs. Preview the `main` content selector and verify extracted headings, code, and integration details.
After a migration, refresh the source and check answer citations. `/llms.txt` alone does not configure Kapa ingestion.
See the [Kapa website widget guide](https://docs.kapa.ai/integrations/website-widget/quickstart).
## Authentication and email
Configure both GitHub OAuth variables and use `https://<workshop-host>/api/oauth2-redirect` as the callback. Supplying
@@ -4,13 +4,13 @@ sidebar_position: 4
tags:
- Workshop
- Developer
- Docusaurus
- Fumadocs
- PocketBase
---
# Workshop development
See the [main development guide](./getting-started) for repository prerequisites.
See the [main development guide](/docs/advanced/development/getting-started) for repository prerequisites.
Start PocketBase:
@@ -19,7 +19,7 @@ cp apps/workshop/.env.example apps/workshop/.env
docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
```
Start Docusaurus in another terminal:
Start Fumadocs in another terminal:
```sh
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
@@ -30,7 +30,8 @@ Using the `PUID` and `PGID` will require you to set the correct permissions on t
| `PUID` | User ID to run the container as | Any valid user ID | `0` |
| `PGID` | Group ID to run the container as | Any valid group ID | `0` |
| `LOG_LEVEL` | Log level to use | `debug` / `info` / `warn` / `error` | `info` |
| `NO_EXTERNAL_CONNECTION` | Disables Homarr's internet-backed requests, including update checks, weather, and RSS feeds | `true` or `false` | `false` |
| `BASE_URL` | Public Homarr origin used by MCP OAuth metadata and redirects. Set this when a reverse proxy does not preserve the public host and protocol. | HTTP(S) origin without a path | - |
| `NO_EXTERNAL_CONNECTION` | Disables Homarr's internet-backed requests, including update checks, weather, location search, and RSS feeds | `true` or `false` | `false` |
| `ENABLE_DNS_CACHING` | Enables dns caching. Enabled by default, but if you experience issues with IPv6 or static IPs, see [#4006](https://github.com/homarr-labs/homarr/issues/4006) or disable this flag | `true` or `false` | `true` |
| `HOMARR_DISABLE_IPV6` | Disables the IPv6 listener of the internal nginx proxy, even when the host supports IPv6. IPv6 is normally disabled automatically when the host has no IPv6 configured. See [#4596](https://github.com/homarr-labs/homarr/issues/4596) | `true` or `false` | `false` |
| `HOMARR_WEBSITE_URL` | Main Homarr documentation/site base used by runtime links. Must be an absolute HTTP(S) URL. | HTTP(S) URL | `https://homarr.dev` |
@@ -138,4 +139,4 @@ The advanced deployments environment variables should only be used if you know w
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts. |
All public URL variables reject non-HTTP protocols, embedded credentials, query strings, and fragments, and normalize
trailing slashes when Homarr or Docusaurus starts.
trailing slashes when Homarr or the documentation site starts.
+60
View File
@@ -0,0 +1,60 @@
---
title: Advanced configuration
description: Configure deployment, authentication, networking, styling, automation, and development workflows.
---
import {
IconCode,
IconIcons,
IconKey,
IconKeyboard,
IconNetwork,
IconPalette,
IconTerminal2,
IconUserCog,
IconVariable,
} from "@tabler/icons-react";
These guides cover changes that affect how Homarr is deployed, secured, operated, or extended.
## Deploy and secure
<Cards>
<Card icon={<IconVariable />} title="Environment variables" href="/docs/advanced/environment-variables">
Configure Homarr at deployment time.
</Card>
<Card icon={<IconKey />} title="Single sign-on" href="/docs/advanced/single-sign-on">
Connect an external identity provider.
</Card>
<Card icon={<IconNetwork />} title="Proxies and certificates" href="/docs/advanced/proxy">
Run Homarr behind a reverse proxy.
</Card>
<Card icon={<IconUserCog />} title="Run as a different user" href="/docs/advanced/running-as-different-user">
Change the user and group used by the container.
</Card>
</Cards>
## Customize
<Cards>
<Card icon={<IconPalette />} title="Custom styling" href="/docs/advanced/styling">
Apply custom CSS to Homarr.
</Card>
<Card icon={<IconIcons />} title="Icons" href="/docs/advanced/icons">
Configure icon sources and behavior.
</Card>
<Card icon={<IconKeyboard />} title="Keyboard shortcuts" href="/docs/advanced/keyboard-shortcuts">
Navigate and operate Homarr with the keyboard.
</Card>
</Cards>
## Develop and automate
<Cards>
<Card icon={<IconTerminal2 />} title="Command line interface" href="/docs/advanced/command-line">
Use administrative commands from the terminal.
</Card>
<Card icon={<IconCode />} title="Developer guides" href="/docs/advanced/development/getting-started">
Set up a development environment and contribute code.
</Card>
</Cards>
@@ -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.
+19
View File
@@ -0,0 +1,19 @@
{
"title": "Advanced",
"defaultOpen": false,
"pages": [
"index",
"---Deploy and secure---",
"environment-variables",
"single-sign-on",
"proxy",
"running-as-different-user",
"---Customize---",
"styling",
"icons",
"keyboard-shortcuts",
"---Develop and automate---",
"command-line",
"development"
]
}
+4 -4
View File
@@ -9,14 +9,14 @@ tags:
- Security
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@/components/mdx/legacy-tabs";
import { TabItem } from "@/components/mdx/legacy-tabs";
## Allowing self-signed certificates
Some users may come across a barrier, where they're unable to receive a 200 response from the Ping widget for some apps, while using self-signed certificates or a local certificate authory.
:::note What's going on?
:::note[What's going on?]
Homarr is trying to communicate to your apps via the integrations.
It usually doesn't matter if Homarr is running on `http` or `https`.
@@ -40,7 +40,7 @@ services:
volumes:
- ./homarr/appdata:/appdata
environment:
- BASE_URL=your.internal.dns.address.here.com
- BASE_URL=https://your.internal.dns.address.here.com
- SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
networks:
- proxy
+163 -169
View File
@@ -10,14 +10,11 @@ tags:
- Active Directory
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
# Single Sign On
Homarr supports multiple authentication options, from internal userbase (credentials), to LDAP (with Active directory support), and OIDC.
### Auth configuration common variables
## Common configuration
| Environment Variable | Description | Default Value |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
@@ -26,198 +23,193 @@ Homarr supports multiple authentication options, from internal userbase (credent
| AUTH_SESSION_EXPIRY_TIME | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") | "30d" |
| AUTH_COOKIE_PREFIX | Prefix used for all authentication cookies (session, csrf, callback, pkce, state, nonce). Change this if you run another Auth.js/NextAuth app on the same hostname as Homarr (browsers scope cookies by host, not by port) to avoid cookie name collisions. Only letters, numbers, hyphens and underscores are allowed. | `homarr` |
<Tabs>
<TabItem value="credentials" label="Credentials Provider" default>
## Credentials provider
This is the default provider.
This is the default provider.
First user is created using the onboarding process and the rest can be created by this user (see [user management](/docs/management/users))
First user is created using the onboarding process and the rest can be created by this user (see [user management](/docs/management/users))
</TabItem>
<TabItem value="ldap" label="LDAP provider">
## LDAP provider
This provider authenticates against an LDAP server.
This provider authenticates against an LDAP server.
Any user in LDAP server that signs in gets created in Homarr database.
Any user in LDAP server that signs in gets created in Homarr database.
Roles are fetched from LDAP groups. Groups with the same name of Homarr will be used to synchronize them.
Roles are fetched from LDAP groups. Groups with the same name of Homarr will be used to synchronize them.
<details>
<summary>
Example Setup
</summary>
<div>
<details>
<summary>
Example Setup
</summary>
<div>
In this setup we are using [lldap](https://github.com/lldap/lldap). Install your server using docker or [as a service](https://github.com/lldap/lldap/blob/main/example_configs/lldap.service).
In this setup we are using [lldap](https://github.com/lldap/lldap). Install your server using docker or [as a service](https://github.com/lldap/lldap/blob/main/example_configs/lldap.service).
Minimal configuration requires 4 env variables, LDAP URI, base, user and password for querying data.
Minimal configuration requires 4 env variables, LDAP URI, base, user and password for querying data.
There's more variables, but all have defaults corresponding to lldap defaults. These might need to be changed if you have different LDAP provider.
```
docker run ...
AUTH_PROVIDERS="ldap"
AUTH_LDAP_URI="ldap://example.com:3890"
AUTH_LDAP_BASE="dc=example,dc=com" // Same as LLDAP_LDAP_BASE_DN
AUTH_LDAP_BIND_DN="uid=admin,ou=People,dc=example,dc=com"
AUTH_LDAP_BIND_PASSWORD="adminpass" // Same as LLDAP_LDAP_USER_PASS
```
```
#Docker compose
version: x
services:
homarr:
environment:
AUTH_PROVIDERS: ldap
AUTH_LDAP_URI: ldap://example.com:3890
AUTH_LDAP_BASE: dc=example,dc=com #Same as LLDAP_LDAP_BASE_DN
AUTH_LDAP_BIND_DN: uid=admin,ou=People,dc=example,dc=com
AUTH_LDAP_BIND_PASSWORD: adminpass #Same as LLDAP_LDAP_USER_PASS
```
There's more variables, but all have defaults corresponding to lldap defaults. These might need to be changed if you have different LDAP provider.
```
docker run ...
AUTH_PROVIDERS="ldap"
AUTH_LDAP_URI="ldap://example.com:3890"
AUTH_LDAP_BASE="dc=example,dc=com" // Same as LLDAP_LDAP_BASE_DN
AUTH_LDAP_BIND_DN="uid=admin,ou=People,dc=example,dc=com"
AUTH_LDAP_BIND_PASSWORD="adminpass" // Same as LLDAP_LDAP_USER_PASS
```
```
#Docker compose
version: x
services:
homarr:
environment:
AUTH_PROVIDERS: ldap
AUTH_LDAP_URI: ldap://example.com:3890
AUTH_LDAP_BASE: dc=example,dc=com #Same as LLDAP_LDAP_BASE_DN
AUTH_LDAP_BIND_DN: uid=admin,ou=People,dc=example,dc=com
AUTH_LDAP_BIND_PASSWORD: adminpass #Same as LLDAP_LDAP_USER_PASS
```
In lldap, create a user and admin group, assign this user to the external admin group configured during onboarding. You can log in using this user and he will be in the group.
In lldap, create a user and admin group, assign this user to the external admin group configured during onboarding. You can log in using this user and he will be in the group.
Here is another example for Active Directory:
Here is another example for Active Directory:
```
AUTH_LDAP_URI="ldap://ldap.abc.xyz:389
AUTH_LDAP_BASE="DC=abc,DC=xyz"
AUTH_LDAP_BIND_DN="CN=Administrator,CN=Users,DC=abc,DC=xyz"
AUTH_LDAP_BIND_PASSWORD="YourAdministratorPassword"
AUTH_LDAP_USERNAME_ATTRIBUTE="sAMAccountName"
AUTH_LDAP_USER_MAIL_ATTRIBUTE="userPrincipalName"
AUTH_LDAP_GROUP_CLASS="group"
AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE="member"
AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE="dn"
AUTH_LDAP_SEARCH_SCOPE="sub"
AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG="(sAMAccountType=805306368)"
```
User mail attribute is set to userPrincipalName as it follows the right schema, but it is recommended to use real emails and the default 'mail' value.
```
AUTH_LDAP_URI="ldap://ldap.abc.xyz:389
AUTH_LDAP_BASE="DC=abc,DC=xyz"
AUTH_LDAP_BIND_DN="CN=Administrator,CN=Users,DC=abc,DC=xyz"
AUTH_LDAP_BIND_PASSWORD="YourAdministratorPassword"
AUTH_LDAP_USERNAME_ATTRIBUTE="sAMAccountName"
AUTH_LDAP_USER_MAIL_ATTRIBUTE="userPrincipalName"
AUTH_LDAP_GROUP_CLASS="group"
AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE="member"
AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE="dn"
AUTH_LDAP_SEARCH_SCOPE="sub"
AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG="(sAMAccountType=805306368)"
```
User mail attribute is set to userPrincipalName as it follows the right schema, but it is recommended to use real emails and the default 'mail' value.
</div>
</details>
</div>
</details>
### Configuration
### LDAP configuration
| Environment Variable | Description | Default value |
|-------------------------------------------|-----------------------------------------------------------|---------------------|
| ``AUTH_LDAP_URI`` | URI of your LDAP server | --- |
| ``AUTH_LDAP_BASE`` | Base dn of your LDAP server | --- |
| ``AUTH_LDAP_BIND_DN`` | User used for finding users and groups | --- |
| ``AUTH_LDAP_BIND_PASSWORD`` | Password for bind user | --- |
| ``AUTH_LDAP_USERNAME_ATTRIBUTE`` | Attribute used for username | uid |
| ``AUTH_LDAP_USER_MAIL_ATTRIBUTE`` | Attribute used for mail field | mail |
| ``AUTH_LDAP_GROUP_CLASS`` | Class used for querying groups | groupOfUniqueNames |
| ``AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE`` | Attribute used for querying group member | member |
| ``AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE`` | User attribute used for comparing with group member | dn |
| ``AUTH_LDAP_SEARCH_SCOPE`` | Serach scopes between base, one and sub | base |
| ``AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG`` | Extra arguments for user search filter (& based) | --- |
| ``AUTH_LDAP_GROUP_FILTER_EXTRA_ARG`` | Extra arguments for user's groups search filter (& based) | --- |
| Environment Variable | Description | Default value |
|-------------------------------------------|-----------------------------------------------------------|---------------------|
| ``AUTH_LDAP_URI`` | URI of your LDAP server | --- |
| ``AUTH_LDAP_BASE`` | Base dn of your LDAP server | --- |
| ``AUTH_LDAP_BIND_DN`` | User used for finding users and groups | --- |
| ``AUTH_LDAP_BIND_PASSWORD`` | Password for bind user | --- |
| ``AUTH_LDAP_USERNAME_ATTRIBUTE`` | Attribute used for username | uid |
| ``AUTH_LDAP_USER_MAIL_ATTRIBUTE`` | Attribute used for mail field | mail |
| ``AUTH_LDAP_GROUP_CLASS`` | Class used for querying groups | groupOfUniqueNames |
| ``AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE`` | Attribute used for querying group member | member |
| ``AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE`` | User attribute used for comparing with group member | dn |
| ``AUTH_LDAP_SEARCH_SCOPE`` | Serach scopes between base, one and sub | base |
| ``AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG`` | Extra arguments for user search filter (& based) | --- |
| ``AUTH_LDAP_GROUP_FILTER_EXTRA_ARG`` | Extra arguments for user's groups search filter (& based) | --- |
</TabItem>
<TabItem value="oidc" label="OIDC provider">
## OIDC provider
This provider authenticates using OIDC protocol.
This provider authenticates using OIDC protocol.
Users signed in using OIDC are created in Homarr.
Users signed in using OIDC are created in Homarr.
Roles are fetched from group claims. This can also be changed to roles for example added to a azure app registration by using the `AUTH_OIDC_GROUPS_ATTRIBUTE`.
Roles are fetched from group claims. This can also be changed to roles for example added to a azure app registration by using the `AUTH_OIDC_GROUPS_ATTRIBUTE`.
If you'd rather manage group memberships for OIDC users locally (instead of via the IdP), set `AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT=true`. While enabled, Homarr stops syncing OIDC users' group memberships from the groups claim, and admins can add/remove OIDC users on a group's members page. If you turn this back to `false`, the next login of each user resumes the regular sync and reconciles their groups with the IdP's claim again, which may remove memberships that were added manually while local management was enabled.
If you'd rather manage group memberships for OIDC users locally (instead of via the IdP), set `AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT=true`. While enabled, Homarr stops syncing OIDC users' group memberships from the groups claim, and admins can add/remove OIDC users on a group's members page. If you turn this back to `false`, the next login of each user resumes the regular sync and reconciles their groups with the IdP's claim again, which may remove memberships that were added manually while local management was enabled.
To let an existing credentials user also sign in with OIDC, set `AUTH_OIDC_ENABLE_DANGEROUS_CREDENTIALS_LINKING=true`. On the first OIDC sign-in, Homarr links a credentials user with the same email address instead of creating a second user. The user keeps their password login, local username and profile, groups, permissions, boards, settings, and API keys. OIDC profile and group claims do not overwrite the credentials-owned data. Homarr requires the OIDC profile to contain `email_verified: true` while this option is enabled. Only enable this when your OIDC provider verifies email addresses, because a matching email could otherwise be used to take over an existing account.
To let an existing credentials user also sign in with OIDC, set `AUTH_OIDC_ENABLE_DANGEROUS_CREDENTIALS_LINKING=true`. On the first OIDC sign-in, Homarr links a credentials user with the same email address instead of creating a second user. The user keeps their password login, local username and profile, groups, permissions, boards, settings, and API keys. OIDC profile and group claims do not overwrite the credentials-owned data. Homarr requires the OIDC profile to contain `email_verified: true` while this option is enabled. Only enable this when your OIDC provider verifies email addresses, because a matching email could otherwise be used to take over an existing account.
<details>
<summary>
Example Setup
</summary>
<div>
<details>
<summary>
Example Setup
</summary>
<div>
In this example we will be using [Authelia](https://github.com/authelia/authelia).
In this example we will be using [Authelia](https://github.com/authelia/authelia).
You can use any setup, but the simplest is [local](https://github.com/authelia/authelia/blob/master/examples/compose/local/authelia/configuration.yml).
You can use any setup, but the simplest is [local](https://github.com/authelia/authelia/blob/master/examples/compose/local/authelia/configuration.yml).
You also have to [enable OIDC in Authelia](https://www.authelia.com/configuration/identity-providers/open-id-connect/).
You also have to [enable OIDC in Authelia](https://www.authelia.com/configuration/identity-providers/open-id-connect/).
Create a client for homarr. To generate client secret you can [use authelia](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-client-secrets). This is an example config:
Create a client for homarr. To generate client secret you can [use authelia](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-client-secrets). This is an example config:
```yaml
identity_providers:
oidc:
...
clients:
- id: homarr
secret: <secret_hash>
public: false
authorization_policy: one_factor
redirect_uris:
- https://example.com/api/auth/callback/oidc
- http://localhost:3000/api/auth/callback/oidc
scopes:
- openid
- groups
- profile
- email
userinfo_signing_algorithm: none
consent_mode: implicit # self hosted
```
```yaml
identity_providers:
oidc:
...
clients:
- id: homarr
secret: <secret_hash>
public: false
authorization_policy: one_factor
redirect_uris:
- https://example.com/api/auth/callback/oidc
- http://localhost:3000/api/auth/callback/oidc
scopes:
- openid
- groups
- profile
- email
userinfo_signing_algorithm: none
consent_mode: implicit # self hosted
```
In Homarr use following env variables.
In Homarr use following env variables.
```
AUTH_PROVIDERS="oidc"
AUTH_OIDC_ISSUER="https://auth.example.com"
AUTH_OIDC_CLIENT_SECRET="client_secret"
AUTH_OIDC_CLIENT_ID="homarr"
AUTH_OIDC_CLIENT_NAME="Authelia"
AUTH_OIDC_FORCE_USERINFO="true" --> from v4.39 of authelia and above this is required
```
```
AUTH_PROVIDERS="oidc"
AUTH_OIDC_ISSUER="https://auth.example.com"
AUTH_OIDC_CLIENT_SECRET="client_secret"
AUTH_OIDC_CLIENT_ID="homarr"
AUTH_OIDC_CLIENT_NAME="Authelia"
AUTH_OIDC_FORCE_USERINFO="true" --> from v4.39 of authelia and above this is required
```
For an azure app registration the setup could look like this:
For an azure app registration the setup could look like this:
```
AUTH_PROVIDERS="oidc"
AUTH_OIDC_ISSUER="https://login.microsoftonline.com/<teanant-id>/v2.0"
AUTH_OIDC_CLIENT_SECRET="<client-secret>"
AUTH_OIDC_CLIENT_ID="<client-id>"
AUTH_OIDC_CLIENT_NAME="Azure"
AUTH_OIDC_SCOPE_OVERWRITE="openid email profile" # Groups scope does not exist in azure
AUTH_OIDC_GROUPS_ATTRIBUTE="roles" # We use the roles of the app registration so that we don't need to use uuids as our groups
```
```
AUTH_PROVIDERS="oidc"
AUTH_OIDC_ISSUER="https://login.microsoftonline.com/<teanant-id>/v2.0"
AUTH_OIDC_CLIENT_SECRET="<client-secret>"
AUTH_OIDC_CLIENT_ID="<client-id>"
AUTH_OIDC_CLIENT_NAME="Azure"
AUTH_OIDC_SCOPE_OVERWRITE="openid email profile" # Groups scope does not exist in azure
AUTH_OIDC_GROUPS_ATTRIBUTE="roles" # We use the roles of the app registration so that we don't need to use uuids as our groups
```
</div>
</details>
</div>
</details>
### Configuration
### OIDC configuration
| Environment Variable | Description | Default value |
|-----------------------------|------------------------------------------------------------|---------------|
| ``AUTH_OIDC_ISSUER`` | Issuer URI of OIDC provider. This has generally to be **without trailing slash** except for **Authentik** | --- |
| ``AUTH_OIDC_CLIENT_ID`` | ID of OIDC client (application) | --- |
| ``AUTH_OIDC_CLIENT_SECRET`` | Secret of OIDC client (application) | --- |
| ``AUTH_OIDC_CLIENT_NAME`` | Display name of provider (in login screen) | OIDC |
| ``AUTH_OIDC_AUTO_LOGIN`` | Automatically redirect to OIDC login | false |
| ``AUTH_OIDC_SCOPE_OVERWRITE`` | Overwrite default scopes (openid, profile, email) | openid email profile groups |
| ``AUTH_OIDC_GROUPS_ATTRIBUTE`` | Attribute used for groups (roles) claim | groups |
| ``AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT`` | Stop syncing group memberships of OIDC users from the groups claim and manage them locally via the group members page instead | false |
| ``AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE`` | Overwrite name attribute. By default it will use preferred_username if it does not contain a `@` and otherwise name. | --- |
| ``AUTH_OIDC_FORCE_USERINFO`` | Force userinfo endpoint to be used for user information. | false |
| ``AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING`` | Enable account linking for OIDC provider. This will link the OIDC accounts by email. Be sure that you have verified emails to prevent stealing accounts. | false |
| ``AUTH_OIDC_ENABLE_DANGEROUS_CREDENTIALS_LINKING`` | Link an OIDC identity to an existing credentials user with the same email. The credentials login and existing user data are preserved. Requires the OIDC profile to contain `email_verified: true`. | false |
| ``AUTH_OIDC_TOKEN_ENDPOINT_AUTH_METHOD`` | Override method for the token endpoint authentication. Supported values are `client_secret_basic`, `client_secret_post`, `client_secret_jwt` and `none`. | client_secret_basic |
| Environment Variable | Description | Default value |
|-----------------------------|------------------------------------------------------------|---------------|
| ``AUTH_OIDC_ISSUER`` | Issuer URI of OIDC provider. This has generally to be **without trailing slash** except for **Authentik** | --- |
| ``AUTH_OIDC_CLIENT_ID`` | ID of OIDC client (application) | --- |
| ``AUTH_OIDC_CLIENT_SECRET`` | Secret of OIDC client (application) | --- |
| ``AUTH_OIDC_CLIENT_NAME`` | Display name of provider (in login screen) | OIDC |
| ``AUTH_OIDC_AUTO_LOGIN`` | Automatically redirect to OIDC login | false |
| ``AUTH_OIDC_SCOPE_OVERWRITE`` | Overwrite default scopes (openid, profile, email) | openid email profile groups |
| ``AUTH_OIDC_GROUPS_ATTRIBUTE`` | Attribute used for groups (roles) claim. Dot-separated paths such as `resource_access.homarr.roles` are supported. | groups |
| ``AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT`` | Stop syncing group memberships of OIDC users from the groups claim and manage them locally via the group members page instead | false |
| ``AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE`` | Overwrite name attribute. Dot-separated paths are supported. By default it will use preferred_username if it does not contain a `@` and otherwise name. | --- |
| ``AUTH_OIDC_FORCE_USERINFO`` | Force userinfo endpoint to be used for user information. | false |
| ``AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING`` | Enable account linking for OIDC provider. This will link the OIDC accounts by email. Be sure that you have verified emails to prevent stealing accounts. | false |
| ``AUTH_OIDC_ENABLE_DANGEROUS_CREDENTIALS_LINKING`` | Link an OIDC identity to an existing credentials user with the same email. The credentials login and existing user data are preserved. Requires the OIDC profile to contain `email_verified: true`. | false |
| ``AUTH_OIDC_TOKEN_ENDPOINT_AUTH_METHOD`` | Override method for the token endpoint authentication. Supported values are `client_secret_basic`, `client_secret_post`, `client_secret_jwt` and `none`. | client_secret_basic |
### Permission System
### OIDC permissions
To give a user special permissions, first create a new group in homarr and assign the permission desired.
For example if the group on homarr is called `homarr-admins` a new group on the oidc provider, for example authentik needs to be created with the same name, that would be `homarr-admins`.
After assigning the group on the oidc provider to the user, and logging again into homarr, the user should automatically get placed in that group and inherit the permissions defined.
To give a user special permissions, first create a new group in homarr and assign the permission desired.
For example if the group on homarr is called `homarr-admins` a new group on the oidc provider, for example authentik needs to be created with the same name, that would be `homarr-admins`.
After assigning the group on the oidc provider to the user, and logging again into homarr, the user should automatically get placed in that group and inherit the permissions defined.
</TabItem>
</Tabs>
## Example setups
@@ -244,18 +236,21 @@ Homarr supports multiple authentication options, from internal userbase (credent
- Create a group (e.g., `homarr-admins`) and assign users who require admin access.
**2. Example .env file:**
```env
OIDC_CLIENT_ID=identificationid #OIDC client ID
OIDC_CLIENT_SECRET=secretsecretsecret #OIDC client secret
Generate `SECRET_ENCRYPTION_KEY` with `openssl rand -hex 32`; do not copy an example key between installs.
```bash
OIDC_CLIENT_ID=<REPLACE_WITH_OIDC_CLIENT_ID>
OIDC_CLIENT_SECRET=<REPLACE_WITH_OIDC_CLIENT_SECRET>
OIDC_SLUG=homarr #Application slug in Authentik
AUTH_DOMAIN=auth.example.com #Authentik FQDN
ADMIN_GROUP=homarr-admins #Authentik group for Homarr admins
HOMARR_FQDN=homarr.example.com #Homarr FQDN
SECRET_ENCRYPTION_KEY=encryptencryptencrypt #Homarr encryption key
SECRET_ENCRYPTION_KEY=<REPLACE_WITH_64_HEX_CHARACTERS>
```
**3. Example Docker Compose configuration:**
```yaml {14-26}
```yaml {14-24}
services:
homarr:
image: ghcr.io/homarr-labs/homarr:latest
@@ -269,8 +264,6 @@ Homarr supports multiple authentication options, from internal userbase (credent
environment:
- TZ=America/Los_Angeles
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
- BASE_URL=https://${HOMARR_FQDN}
- NEXTAUTH_URL=https://${HOMARR_FQDN}
- AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback)
- AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
- AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
@@ -297,7 +290,7 @@ Homarr supports multiple authentication options, from internal userbase (credent
</div>
</details>
Additional OIDC configuration options are described in the OIDC tab above.
See [OIDC configuration](#oidc-configuration) for additional options.
</TabItem>
<TabItem value="google" label="Google (OIDC) Example" default>
@@ -322,15 +315,18 @@ Homarr supports multiple authentication options, from internal userbase (credent
- Record the `Client-ID` and `Client Key` (secret).
**2. Example .env file (Homarr):**
```env
OIDC_CLIENT_ID=identificationid #Google Client-ID
OIDC_CLIENT_SECRET=secretsecretsecret #Google Client Key (secret)
Generate `SECRET_ENCRYPTION_KEY` with `openssl rand -hex 32`; do not copy an example key between installs.
```bash
OIDC_CLIENT_ID=<REPLACE_WITH_GOOGLE_CLIENT_ID>
OIDC_CLIENT_SECRET=<REPLACE_WITH_GOOGLE_CLIENT_SECRET>
HOMARR_FQDN=homarr.example.com #Homarr FQDN
SECRET_ENCRYPTION_KEY=encryptencryptencrypt #Homarr encryption key
SECRET_ENCRYPTION_KEY=<REPLACE_WITH_64_HEX_CHARACTERS>
```
**3. Example Docker Compose configuration (Homarr):**
```yaml {16-22}
```yaml {14-20}
services:
homarr:
image: ghcr.io/homarr-labs/homarr:latest
@@ -344,8 +340,6 @@ Homarr supports multiple authentication options, from internal userbase (credent
environment:
- TZ=Europe/Berlin
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
- BASE_URL=https://${HOMARR_FQDN}
- NEXTAUTH_URL=https://${HOMARR_FQDN}
- AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback)
- AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
- AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
@@ -362,7 +356,7 @@ Homarr supports multiple authentication options, from internal userbase (credent
</div>
</details>
Additional OIDC configuration options are described in the OIDC tab above.
See [OIDC configuration](#oidc-configuration) for additional options.
</TabItem>
</Tabs>
-8
View File
@@ -1,8 +0,0 @@
{
"label": "Community",
"position": 6,
"collapsed": true,
"link": {
"type": "generated-index"
}
}
+9 -8
View File
@@ -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).
+10
View File
@@ -0,0 +1,10 @@
---
title: Community
description: Get help, contribute to Homarr, and support the project.
---
- [Frequently asked questions](/docs/community/faq): resolve common setup and operation issues.
- [Get in touch](/docs/community/get-in-touch): choose the right support or discussion channel.
- [Translations](/docs/community/translations): improve Homarr in your language.
- [Donate](/docs/community/donate): support ongoing development.
- [License](/docs/community/license): understand how Homarr is licensed.
+5
View File
@@ -0,0 +1,5 @@
{
"title": "Community",
"defaultOpen": false,
"pages": ["index", "faq", "get-in-touch", "translations", "donate", "license"]
}
@@ -1,9 +0,0 @@
{
"label": "Getting started",
"position": 1,
"collapsed": false,
"link": {
"type": "generated-index",
"title": "Getting started"
}
}
@@ -6,11 +6,68 @@ tags:
- Configuration
---
On first launch, onboarding creates the administrator, connects optional services, and builds the first board. A
compatible SQLite backup can also be restored from the welcome screen.
import {
IconAdjustments,
IconChecklist,
IconLayoutDashboard,
IconPlugConnected,
IconSearch,
IconSparkles,
} from "@tabler/icons-react";
If onboarding returns after a restart, check that `/appdata` is writable and persisted. See the
[Docker installation guide](/docs/getting-started/installation/docker).
import onboardingWelcome from "./img/onboarding-v2-welcome.png";
import onboardingBoard from "./img/onboarding-v2-board.png";
import onboardingReview from "./img/onboarding-v2-review.png";
After Homarr starts, open its URL. An unfinished installation redirects to the onboarding screen at `/init`. Choose
**Get started** for a new installation or **Restore backup** to import a compatible SQLite backup.
<Callout type="info" title="Persist /appdata before you continue">
Onboarding writes the administrator and board configuration to the database. If onboarding returns after a restart,
check that `/appdata` is writable and persisted. See the
[Docker installation guide](/docs/getting-started/installation/docker).
</Callout>
## Complete onboarding
Onboarding is split into six short sections. Integrations, Workshop, Assistant, and MCP are optional; skip them when you
only need a working board.
<Cards>
<Card icon={<IconAdjustments />} title="1. Essentials">
Choose the language, theme, usual server address, and anonymous analytics preference.
</Card>
<Card icon={<IconSearch />} title="2. Discover">
Review the database, Docker, Kubernetes, Assistant, and Workshop capabilities Homarr can reach.
</Card>
<Card icon={<IconPlugConnected />} title="3. Connect">
Import detected services or add integrations manually. Unfinished integrations can be skipped.
</Card>
<Card icon={<IconLayoutDashboard />} title="4. Board">
Set the board name, colors, corner radius, column count, and optional sidebars.
</Card>
<Card icon={<IconSparkles />} title="5. Extend">
Optionally connect the Workshop, configure an Assistant provider, or review the MCP endpoint.
</Card>
<Card icon={<IconChecklist />} title="6. Review">
Check the final summary, then build the board. Homarr opens the exact board it configured.
</Card>
</Cards>
The final screen links directly to the new board and the management area. Board layout, integrations, apps, and
appearance can all be changed later.
<Cards>
<Card title="Start or restore">
<img src={onboardingWelcome.src} alt="Homarr onboarding welcome screen with Get started and Restore backup actions" />
</Card>
<Card title="Configure the board">
<img src={onboardingBoard.src} alt="Board onboarding section for colors, columns, corner radius, and sidebars" />
</Card>
<Card title="Review before building">
<img src={onboardingReview.src} alt="Onboarding review showing the board settings Homarr will create" />
</Card>
</Cards>
## Core concepts
@@ -19,7 +76,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.
![Board management with board previews](./img/after-installation-boards.png)
![Board management with board previews](./img/after-installation-boards.webp)
See [Boards](/docs/management/boards) for responsive layouts, access control, appearance, and home-board settings.
@@ -28,7 +85,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.
![App management with saved shortcuts](./img/after-installation-apps.png)
![App management with saved shortcuts](./img/after-installation-apps.webp)
See [Apps](/docs/management/apps) or [Icons](/docs/advanced/icons).
@@ -37,26 +94,26 @@ 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.
![Integration catalog with compatible widgets](./img/after-installation-integrations.png)
![Integration catalog with compatible widgets](./img/after-installation-integrations.webp)
See [Managing integrations](/docs/management/integrations) and the
[integration catalog](/docs/category/integrations).
[integration catalog](/docs/integrations).
### Widgets
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.
![Board in edit mode with the add-content menu](./img/after-installation-widgets.png)
![Board in edit mode with the add-content menu](./img/after-installation-widgets.webp)
See the [widget catalog](/docs/category/widgets).
See the [widget catalog](/docs/widgets).
### Containers and rails
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.
![Board layout settings with fixed sidebars](./img/after-installation-containers-rails.png)
![Board layout settings with fixed sidebars](./img/after-installation-containers-rails.webp)
See [Boards](/docs/management/boards#containers-and-rails).
@@ -65,7 +122,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.
![Group permission settings](./img/after-installation-users-permissions.png)
![Group permission settings](./img/after-installation-users-permissions.webp)
See [Users and groups](/docs/management/users) and [single sign-on](/docs/advanced/single-sign-on).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Loaded 100 of 657 files, more files were not shown because too many files have changed in this diff. Show more