--- name: homarr-dashboard-setup description: >- Deploy, configure, and populate a Homarr dashboard — Docker setup, user management, bulk-adding services via direct SQLite manipulation, layout planning, and troubleshooting. tags: [homarr, dashboard, self-hosted, homelab, sqlite, docker] --- # Homarr Dashboard Setup Populate a Homarr dashboard with all self-hosted services. Homarr stores everything in SQLite -- apps, items (widgets), layout positions. For bulk operations, direct database manipulation is faster than the web UI. ## Quick reference | Path | Value | |------|-------| | Container | `homarr` | | Image | `ghcr.io/homarr-labs/homarr:latest` | | Port | `8080:7575` | | Config dir | `./appdata` (bind mount to `/appdata`) | | DB location | `/db/db.sqlite` | | CLI tool | `docker exec homarr homarr ` | ## User management ### Create first admin (no users exist) ```bash docker exec homarr homarr recreate-admin --username ray # Shows a generated password ``` ### Set a specific password ```bash docker exec homarr homarr users update-password \ --username ray \ --password 'your-password-here' ``` ### Reset password to random (all sessions terminated) ```bash docker exec homarr homarr reset-password --username ray ``` ### List users ```bash docker exec homarr homarr users list ``` ## Database schema (key tables) ### `app` -- service definitions ``` id TEXT PRIMARY KEY -- 25-char alphanumeric ID name TEXT -- display name description TEXT -- optional tooltip icon_url TEXT -- URL to icon href TEXT -- click target URL ping_url TEXT -- optional separate status-check URL ``` ### `item` -- widgets on the board ``` id TEXT PRIMARY KEY board_id TEXT -- FK to board.id kind TEXT -- 'app' for app widgets, 'clock', 'weather', etc. options TEXT -- JSON: {"json":{"appId":"","openInNewTab":true,"showTitle":true}} advanced_options TEXT -- usually {"json":{}} ``` ### `item_layout` -- position/size on the grid ``` item_id TEXT -- FK to item.id section_id TEXT -- FK to section.id layout_id TEXT -- FK to layout.id x_offset INTEGER y_offset INTEGER width INTEGER height INTEGER ``` ### `board` -- the dashboard page ``` id TEXT PRIMARY KEY name TEXT -- unique name ``` ### `section` -- a column/grid area on a board ``` id TEXT PRIMARY KEY board_id TEXT -- FK to board.id ``` ### `section_layout` -- which layout template a section uses ``` id TEXT PRIMARY KEY section_id TEXT -- FK to section.id ``` ## Bulk-adding services via SQLite When adding 10+ services, direct SQLite inserts beat the UI. Steps: ### 1. Discover running services and their ports ```bash docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}' ss -tlnp | grep LISTEN ``` ### 2. Get board/section/layout IDs ```sql SELECT id, name FROM board; SELECT id, board_id FROM section; SELECT id, section_id FROM section_layout; ``` ### 3. Icon URLs Use the Homarr dashboard-icons repository: ``` https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/.png https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/.svg ``` Common icons: `immich.png`, `home-assistant.png`, `paperless-ngx.png`, `plex.png`, `audiobookshelf.png`, `mealie.png`, `qbittorrent.png`, `pihole.png`, `portainer.png`, `uptime-kuma.png`, `scrutiny.png`, `vaultwarden.png`, `pocketbase.png`, `glitchtip.png`, `searxng.png`, `sunshine.png` For missing icons, fall back to `chrome-beta.png` or another generic icon. ### 4. Generate IDs Homarr uses ~25-char alphanumeric IDs. Generate them in Python: ```python import secrets def gen_id(): return secrets.token_urlsafe(16)[:25].replace('-','0').replace('_','1') ``` ### 5. Insert apps ```sql INSERT INTO app (id, name, icon_url, href) VALUES ('', '', '', ''); ``` ### 6. Insert items (one per app) ```sql INSERT INTO item (id, board_id, kind, options, advanced_options) VALUES ('', '', 'app', '{"json":{"appId":"","openInNewTab":true,"showTitle":true}}', '{"json":{}}'); ``` ### 7. Insert layout records ```sql INSERT INTO item_layout (item_id, section_id, layout_id, x_offset, y_offset, width, height) VALUES ('', '', '', 0, 0, 1, 1); ``` ### 8. Restart container ```bash docker restart homarr ``` ### 9. Verify ```bash sqlite3 \ "SELECT l.x_offset, l.y_offset, l.width, l.height, a.name FROM item_layout l JOIN item i ON l.item_id = i.id LEFT JOIN app a ON json_extract(i.options, '$.json.appId') = a.id ORDER BY l.y_offset, l.x_offset;" ``` ## Pitfalls - **DB owned by root**: The SQLite file is created by the Docker container and owned by `root:root`. Use `sudo` or `docker exec` for DB access. - **Duplicate apps**: Homarr auto-discovers Docker containers and inserts into the `app` table. You may see duplicate entries -- e.g. the auto-discovered `homarr` + your manually created `Homarr`. Clean up old items/layouts if needed. - **Layout columns are `x_offset`, `y_offset`**: Not `x`/`y`. Forgetting this causes silent misplacement. - **IDs must be unique across `item` and `app` tables**: The random generator collisions are astronomically unlikely but verify if you suspect overlap. - **Restart required**: Direct DB writes are not live-reloaded. Always `docker restart homarr` after inserts. - **Container restarts reset uncommitted changes**: The DB is on a bind mount, but if your insert script hasn't committed before the container exits, changes are lost. Use explicit `connection.commit()`. - **json_extract in joins**: When joining `item` to `app`, use `json_extract(i.options, '$.json.appId')` to extract the app reference from the item's JSON options field. - **Nginx proxy vs direct access**: For services behind nginx on the same host, use `127.0.0.1:`. For services reaching other hosts or directly exposed, use `192.168.50.98:`.