Dashy is a self-hosted dashboard that replaces cloud startpages with a private, YAML-configured launchpad featuring real-time health checks and widgets.
Dashy is a self-hosted personal dashboard and startpage designed to organize application bookmarks and internal homelab services. It stores its interface layout in a single YAML configuration file while providing dynamic widgets, built-in network status checks, and multiple view modes. Running as a container or static web app, it keeps all routing and navigation inside your private network.
start.me operates as a hosted cloud service where user layouts, links, and feeds reside entirely on external servers. While this removes maintenance overhead, it introduces an operational barrier for private networks: cloud startpages cannot reach private IP addresses, local domain names, or internal subnets without exposing ports to the public internet. Dashy runs inside your firewall on bare metal, single-board computers, or container hosts. This local presence allows it to poll HTTP endpoints and run ICMP pings against internal subnets directly, showing availability states across local servers without leaking traffic off-site.
The operational trade-off centers on management overhead versus control. start.me offers instant setup with cloud sync across devices, but locks team features and ad-free boards behind recurring subscriptions. Dashy removes external subscription costs and keeps data private by storing configuration in a plain-text conf.yml file that fits standard Git workflows. In exchange, administrators handle container updates, volume backups, file permissions on the storage mount, and reverse proxy configuration when external access is needed.
conf.yml) that fits in version control alongside container manifests for reproducible deployments.conf.yml directly.Dashy vs start.me — which should you choose? Pick start.me if you want a zero-maintenance cloud startpage that syncs across devices out of the box. Choose Dashy if you run homelab services on private subnets, need local status checks, and want full control over your data.
Can Dashy monitor services on a local private network? Yes. Because Dashy runs inside your local network, it executes HTTP queries and ICMP pings directly against internal IP addresses and hostnames that cloud services cannot reach.
Is Dashy free for commercial and personal use? Yes. Dashy is free open-source software with no paid tiers, allowing unrestricted deployment and modification for personal or commercial homelabs.
Dashy is the right choice when you want a private, highly configurable startpage that tracks homelab services via local status checks and declarative YAML; it is not suited for users wanting a zero-maintenance hosted bookmark service. Active maintenance with commits in 2026 and an unrestrictive MIT license that permits arbitrary modification make it a dependable foundation for personal and homelab infrastructure.
We started this exact setup from scratch in a clean Linux machine on Oct 31, 2026 and checked it through Dashy's own API: every check passed. Recipes are re-checked regularly and removed if they stop working.
mkdir -p dashy && cd dashy
curl -fsSL https://open-source.store/tools/dashy/compose.yml -o compose.ymlcat > .env <<EOF
BASIC_AUTH_USERNAME=admin
BASIC_AUTH_PASSWORD=$(openssl rand -hex 12)
EOFBASIC_AUTH_USERNAME — Username for HTTP basic authentication.BASIC_AUTH_PASSWORD — Password for HTTP basic authentication. Generated above; keep it secret.docker compose up -dservices:
dashy:
image: ghcr.io/lissy93/dashy:4.9.0
ports:
- "8080:8080"
volumes:
- dashy_data:/app/user-data
environment:
- NODE_ENV=production
- DISABLE_PROXY_ENDPOINTS=true
- BASIC_AUTH_USERNAME=${BASIC_AUTH_USERNAME}
- BASIC_AUTH_PASSWORD=${BASIC_AUTH_PASSWORD}
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "/app/services/healthcheck.js"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
volumes:
dashy_data:
Lint, codegen, breaking-change checks and dependency management in one Git-native CLI. Open source, no registry lock-in.
Show that Dashy is listed as an open-source alternative. Paste into your README:
Ubuntu 24.04 · Apple container, Docker 29.1.3, Compose 2.40.3+ds1-0ubuntu1~24.04.1. Images pulled in 88 s: ghcr.io/lissy93/dashy:4.9.0.
Based on the upstream setup. Official compose. Image pinned to 4.9.0; persistent volume mounted, basic auth enabled via environment variables. CORS proxy and status/ping check endpoints are disabled, so widgets needing useProxy and item status dots will not work.
dashy-1 | 2026-10-31T22:45:39.622872881Z SyntaxError: Unexpected token '@', "@
dashy-1 | 2026-10-31T22:45:39.622873464Z " is not valid JSON
dashy-1 | 2026-10-31T22:45:39.622873922Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:45:39.622874631Z at createStrictSyntaxError (/app/node_modules/body-parser/lib/types/json.js:137:10)
dashy-1 | 2026-10-31T22:45:39.622875214Z at parse (/app/node_modules/body-parser/lib/types/json.js:86:15)
dashy-1 | 2026-10-31T22:45:39.622875756Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:45:39.622876256Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:45:39.622876797Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:45:39.622880631Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:45:39.622881256Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:45:39.622881839Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:45:39.622882339Z at endReadableNT (node:internal/streams/readable:1783:12) {
dashy-1 | 2026-10-31T22:45:39.622882881Z expose: true,
dashy-1 | 2026-10-31T22:45:39.622883381Z statusCode: 400,
dashy-1 | 2026-10-31T22:45:39.622883881Z status: 400,
dashy-1 | 2026-10-31T22:45:39.622884381Z body: '@\r\n',
dashy-1 | 2026-10-31T22:45:39.622884881Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:45:39.622885381Z }
dashy-1 | 2026-10-31T22:45:52.618840053Z SyntaxError: Expected ',' or '}' after property value in JSON at position 65 (line 1 column 66)
dashy-1 | 2026-10-31T22:45:52.618854053Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:45:52.618855262Z at parse (/app/node_modules/body-parser/lib/types/json.js:91:21)
dashy-1 | 2026-10-31T22:45:52.618856095Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:45:52.618856720Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:45:52.618857262Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:45:52.618858053Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:45:52.618864470Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:45:52.618865178Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:45:52.618865720Z at endReadableNT (node:internal/streams/readable:1783:12)
dashy-1 | 2026-10-31T22:45:52.618866262Z at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
dashy-1 | 2026-10-31T22:45:52.618866845Z expose: true,
dashy-1 | 2026-10-31T22:45:52.618867387Z statusCode: 400,
dashy-1 | 2026-10-31T22:45:52.618867887Z status: 400,
dashy-1 | 2026-10-31T22:45:52.618868428Z body: `{"id":"961455b47c0b86dc961e90b5893bff05","apiUrl":"","params":"{"id":"1' or '%1%' like (updatexml(0x3a,concat(1,(version())),1)) or '%%' like '"}"}`,
dashy-1 | 2026-10-31T22:45:52.618869387Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:45:52.618869887Z }
dashy-1 | 2026-10-31T22:45:52.618870387Z SyntaxError: Expected ',' or '}' after property value in JSON at position 65 (line 1 column 66)
dashy-1 | 2026-10-31T22:45:52.618871053Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:45:52.618871637Z at parse (/app/node_modules/body-parser/lib/types/json.js:91:21)
dashy-1 | 2026-10-31T22:45:52.618872178Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:45:52.618872720Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:45:52.618873262Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:45:52.618873803Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:45:52.618874387Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:45:52.618874887Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:45:52.618875387Z at endReadableNT (node:internal/streams/readable:1783:12)
dashy-1 | 2026-10-31T22:45:52.618875928Z at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
dashy-1 | 2026-10-31T22:45:52.618876512Z expose: true,
dashy-1 | 2026-10-31T22:45:52.618877012Z statusCode: 400,
dashy-1 | 2026-10-31T22:45:52.618877512Z status: 400,
dashy-1 | 2026-10-31T22:45:52.618878012Z body: `{"id":"961455b47c0b86dc961e90b5893bff05","apiUrl":"","params":"{"id":"1' or '%1%' like (updatexml(0x3a,concat(1,(version())),1)) or '%%' like '"}"}`,
dashy-1 | 2026-10-31T22:45:52.618878762Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:45:52.618879220Z }
dashy-1 | 2026-10-31T22:46:19.589575843Z SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
dashy-1 | 2026-10-31T22:46:19.589590343Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:46:19.589591635Z at parse (/app/node_modules/body-parser/lib/types/json.js:91:21)
dashy-1 | 2026-10-31T22:46:19.589592468Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:46:19.589593218Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:46:19.589597343Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:46:19.589597968Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:46:19.589598510Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:46:19.589599093Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:46:19.589599593Z at endReadableNT (node:internal/streams/readable:1783:12)
dashy-1 | 2026-10-31T22:46:19.589600260Z at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
dashy-1 | 2026-10-31T22:46:19.589600843Z expose: true,
dashy-1 | 2026-10-31T22:46:19.589601343Z statusCode: 400,
dashy-1 | 2026-10-31T22:46:19.589601843Z status: 400,
dashy-1 | 2026-10-31T22:46:19.589602343Z body: '{u:"admin", t:"undefined", p:"trilithic", d:"", r:false, w:false}',
dashy-1 | 2026-10-31T22:46:19.589603051Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:46:19.589603510Z }
dashy-1 | 2026-10-31T22:46:19.590864176Z SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
dashy-1 | 2026-10-31T22:46:19.590872051Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:46:19.590872801Z at parse (/app/node_modules/body-parser/lib/types/json.js:91:21)
dashy-1 | 2026-10-31T22:46:19.590873385Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:46:19.590873885Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:46:19.590874426Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:46:19.590875176Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:46:19.590875676Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:46:19.590876176Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:46:19.590876676Z at endReadableNT (node:internal/streams/readable:1783:12)
dashy-1 | 2026-10-31T22:46:19.590877176Z at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
dashy-1 | 2026-10-31T22:46:19.590877718Z expose: true,
dashy-1 | 2026-10-31T22:46:19.590878218Z statusCode: 400,
dashy-1 | 2026-10-31T22:46:19.590878718Z status: 400,
dashy-1 | 2026-10-31T22:46:19.590879176Z body: '{u:"admin", t:"undefined", p:"trilithic", d:"", r:false, w:false}',
dashy-1 | 2026-10-31T22:46:19.590879843Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:46:19.590880343Z }
dashy-1 | 2026-10-31T22:46:27.614966972Z SyntaxError: Unexpected token 't', "t" is not valid JSON
dashy-1 | 2026-10-31T22:46:27.614983555Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:46:27.614984847Z at createStrictSyntaxError (/app/node_modules/body-parser/lib/types/json.js:137:10)
dashy-1 | 2026-10-31T22:46:27.614985555Z at parse (/app/node_modules/body-parser/lib/types/json.js:86:15)
dashy-1 | 2026-10-31T22:46:27.614986138Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:46:27.614992805Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:46:27.614993388Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:46:27.614994055Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:46:27.614994555Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:46:27.614995055Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:46:27.614995555Z at endReadableNT (node:internal/streams/readable:1783:12) {
dashy-1 | 2026-10-31T22:46:27.614996055Z expose: true,
dashy-1 | 2026-10-31T22:46:27.614996555Z statusCode: 400,
dashy-1 | 2026-10-31T22:46:27.614997013Z status: 400,
dashy-1 | 2026-10-31T22:46:27.614997472Z body: 't',
dashy-1 | 2026-10-31T22:46:27.615000222Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:46:27.615000722Z }
dashy-1 | 2026-10-31T22:46:28.623945889Z SyntaxError: Unexpected token 't', "t" is not valid JSON
dashy-1 | 2026-10-31T22:46:28.623960889Z at JSON.parse (<anonymous>)
dashy-1 | 2026-10-31T22:46:28.623962139Z at createStrictSyntaxError (/app/node_modules/body-parser/lib/types/json.js:137:10)
dashy-1 | 2026-10-31T22:46:28.623962889Z at parse (/app/node_modules/body-parser/lib/types/json.js:86:15)
dashy-1 | 2026-10-31T22:46:28.624978972Z at /app/node_modules/body-parser/lib/read.js:162:18
dashy-1 | 2026-10-31T22:46:28.624990555Z at AsyncResource.runInAsyncScope (node:async_hooks:227:14)
dashy-1 | 2026-10-31T22:46:28.624991389Z at invokeCallback (/app/node_modules/raw-body/index.js:238:16)
dashy-1 | 2026-10-31T22:46:28.624992055Z at done (/app/node_modules/raw-body/index.js:227:7)
dashy-1 | 2026-10-31T22:46:28.624992597Z at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:287:7)
dashy-1 | 2026-10-31T22:46:28.624993139Z at IncomingMessage.emit (node:events:514:28)
dashy-1 | 2026-10-31T22:46:28.624993722Z at endReadableNT (node:internal/streams/readable:1783:12) {
dashy-1 | 2026-10-31T22:46:28.624994264Z expose: true,
dashy-1 | 2026-10-31T22:46:28.624994764Z statusCode: 400,
dashy-1 | 2026-10-31T22:46:28.624995305Z status: 400,
dashy-1 | 2026-10-31T22:46:28.624995764Z body: 't',
dashy-1 | 2026-10-31T22:46:28.624996264Z type: 'entity.parse.failed'
dashy-1 | 2026-10-31T22:46:28.624996764Z }