6.9 KiB
Homarr architecture
Contents
Applications and runtimes
| Path | Role |
|---|---|
apps/nextjs |
Main Next.js application and HTTP APIs on port 3000 |
apps/docs |
Docusaurus documentation site on port 3003 |
apps/tasks |
Cron-job initialization and scheduling runtime; it does not expose the old port-3002 API |
apps/websocket |
tRPC WebSocket subscriptions on port 3001 |
apps/workshop |
Go/PocketBase Workshop service and provider integration |
pnpm dev runs only @homarr/nextjs. Use pnpm --filter @homarr/tasks dev, pnpm --filter @homarr/websocket dev, or pnpm dev:docs when a task needs those runtimes.
In production, apps/nextjs/src/instrumentation-node.ts starts the tasks and WebSocket runtimes inside the Node process. Nginx listens on port 7575, proxies /websockets to port 3001, and sends other traffic to port 3000. Redis can run internally or externally.
Package map
Foundation and domain
core: environment validation, database and Redis factories, logging, infrastructure.definitions: domain enums, integration definitions, widget kinds, permissions.common: shared utilities with environment-specific entrypoints. Search it before adding helpers.validation: shared Zod schemas.server-settings: typed server-setting keys and access.
Data, identity, and transport
db: Drizzle schemas, queries, migrations, and three database drivers.auth: Auth.js/NextAuth configuration, sessions, providers, and API keys.api: tRPC routers, contexts, MCP metadata extraction, OpenAPI, and client/server entrypoints.redis: Redis clients, pub/sub channels, and caches.cron-jobs-core,cron-jobs,cron-job-status: scheduling primitives, job implementations, and status transport.
Backends and external systems
integrations: typed clients for external applications.request-handler: shared server request flows.docker,ping,image-proxy,icons,analytics: bounded infrastructure capabilities.custom-widgets: Custom JSX v2 schema, validation, runtime, and security contracts.workshop: TypeScript Workshop client, backend contracts, and schemas used by Homarr.
UI and composed features
ui: shared Mantine components, theme, and hooks. Search here before creating a primitive.form,forms-collection: Zod-backed form setup and reusable forms.modals,modals-collection: modal primitives and feature modals.boards: board state, edit mode, and cache coordination.settings,spotlight,notifications,onboarding: cross-feature UI flows.widgets: widget definitions, dynamic component loaders, and shared widget UI.translation: next-intl configuration and locale catalogs.cli: Node operations CLI.tools/homarr-devis the separate Go developer CLI.
Dependency direction
Prefer these directions; verify the exact dependency in package manifests before changing a boundary.
- Put infrastructure in
coreand stable domain names indefinitions. - Put reusable, dependency-light helpers in
common; use its public environment-specific entrypoints. - Build persistence in
db, identity inauth, transport inredis, and external clients inintegrations. - Compose server behavior in
api,cron-jobs, and request-handler packages. - Build primitives in
ui,form, andmodals; compose product surfaces in widgets and collection packages. - Keep app entrypoints in
apps/*thin. Avoid moving reusable behavior intoapps/nextjswhen a package already owns it.
Core patterns
tRPC and MCP
packages/api/src/root.ts lazily composes the application router. Server components use @homarr/api/server; client components use @homarr/api/client. MCP extraction requires the eager router in packages/api/src/mcp.ts, so MCP-enabled subrouters must be imported and registered there directly. Use the mcp-integration skill for exposure and security rules.
Databases
Drizzle schemas live in parallel files under packages/db/schema/. DB_DRIVER selects better-sqlite3, mysql2, or node-postgres; migrations live in matching directories under packages/db/migrations/. Apply schema behavior consistently across all supported drivers unless the task is explicitly driver-specific.
Widgets
packages/widgets/src/registry.ts owns the explicit widget-module and component-loader registries. packages/widgets/src/manifest.ts caches those loaders and exposes definitions, components, and combined resources. Widget modules use createWidgetDefinition and typed option builders. Data commonly comes from tRPC queries or Redis-backed cron results. Use existing shared widget components before creating local variants.
Integrations
packages/definitions/src/integration.ts owns integrationDefs and IntegrationKind. packages/integrations/src/base/creator.ts owns createIntegrationAsync; concrete integrations implement the shared base contract. Keep credentials and permission checks on server-owned paths.
Cron jobs
Jobs live in packages/cron-jobs/src/jobs/ and use createCronJob. apps/tasks initializes and starts the job group. Job results and status can flow through Redis and tRPC; the retired port-3002 management-API model is no longer part of the architecture.
Routing and localization
User routes live below apps/nextjs/src/app/[locale]/; the locale segment does not add a URL prefix. Route groups organize home and board surfaces. Administrative routes live under manage, Custom Widget configuration has a token route, and Workshop has a management surface.
Custom Widgets and Workshop
packages/custom-widgets owns the Custom JSX v2 contract. packages/workshop is the Homarr-side client/contract package; apps/workshop is the separate Go/PocketBase service. Use the homarr-custom-widget skill for authoring and security constraints.
Commands and conventions
- Read current scripts from the root or package
package.json; avoid caching commands that do not exist. - Use
pnpm devfor Next.js,pnpm dev:docsfor docs, andpnpm dev:cli -- <args>for the Go developer CLI. - Use
pnpm docker:dev:upfor the Redis development dependency. - Use package-filtered typechecks or focused tests for the touched behavior.
- Use
~/*only inside the Next.js app. Use public@homarr/*entrypoints across package boundaries. - Keep Mantine application styles separate from the Tailwind-based docs app.