How to Install Dashy on Ubuntu 24.04 VPS: Privacy-Focused Self-Hosted Dashboard
If you run more than a handful of services on your VPS, you probably have a mental (or bookmark-folder) map of where everything lives: Portainer on one port, Grafana on another, a Git server on a subdomain, a media stack somewhere else. Dashy replaces that scattered mess with a single, beautiful start page that you fully own and control. It is a Vue.js single-page application that renders a YAML-defined list of your services, checks whether they are up, embeds live widgets (weather, crypto prices, system stats, RSS feeds), and gates the whole thing behind authentication if you want it to.
This guide walks through a production-ready Dashy install on Ubuntu 24.04 using Docker Compose, from first ssh to an HTTPS-protected dashboard at your own domain. You will end up with a persistent config volume, an Nginx reverse proxy, Let's Encrypt certificates, automated backups of your conf.yml, and a clear mental model of how sections, items, widgets, and themes fit together.
Want a one-click option? Our CloudCore Starter VPS is the ideal home for Dashy: plenty of headroom for the container, an NVMe disk for fast rebuilds, and a real public IP so you can put a proper TLS certificate on top.
Table of Contents
What is Dashy?
Dashy is an open-source, feature-rich dashboard for organizing the services you host and the links you care about. Under the hood it is a Vue 3 single-page app served by a Node.js backend that also handles status checks and a couple of proxy endpoints for widgets that cannot call third-party APIs directly from the browser (for example, anything blocked by CORS). The project is authored and maintained primarily by Alicia Sykes and the official Docker image is published as lissy93/dashy on Docker Hub.
What makes Dashy stand out from the crowded self-hosted-dashboard space:
- Extensive widget library. Out of the box you get status-check tiles, weather, crypto prices, stock tickers, system info (CPU, RAM, disk, uptime from the host where Dashy runs), RSS feeds, GitHub repo stats, public IP and geolocation, cron-ping health, code-repo releases, and dozens more. Widgets are just YAML entries, not code you have to write.
- Declarative YAML configuration. The entire dashboard is described by a single
conf.ymlfile. This is trivial to version-control, diff, migrate between servers, and restore from backup. - Built-in authentication. Dashy ships with two auth modes: a lightweight JWT-based scheme (a simple list of users and bcrypt-like hashed passwords in
conf.yml) and full Keycloak SSO for organizations that already run an identity provider. - Theme library. Around two dozen themes are bundled (Material, Nord, Adventure, Minimal Dark, Hacker Terminal, and more) and you can author your own with CSS variables.
- Two editing modes. Edit the YAML directly on disk, or use the visual config editor in the UI and let Dashy write the file for you.
- Offline-first. Everything except a few third-party widgets runs locally; your browsing history on the dashboard stays on your server.
Why Self-Host Your Dashboard?
You could use a hosted service like Start.me, Netvibes, or any of the browser new-tab extensions. Running your own Dashy instance on your own VPS buys you a few concrete things instead:
- Privacy of your link graph. The list of services you access and how you organize them is a surprisingly revealing personal/organizational fingerprint. Hosted dashboards see all of it. Dashy sees none of it except your own server.
- No upstream outages. When the hosted service has a bad day, your start page disappears. A self-hosted dashboard is as reliable as your VPS.
- Internal service linking. Dashy happily links to
http://10.0.0.5:9000orhttps://grafana.internal.lan. Hosted dashboards cannot reach RFC1918 addresses at all. - No arbitrary limits. Free tiers cap the number of links, widgets, or themes. A self-hosted install has no such limits.
- Integration with your stack. Status checks can hit
http://traefik:8080/pingby Docker service name when Dashy shares a network. That is impossible with an external dashboard.
| Dashboard | Config style | Widgets | Auth | Notes |
|---|---|---|---|---|
| Dashy | YAML, UI editor | 40+ widgets, status checks | JWT + Keycloak | Most feature-rich, Vue SPA |
| Homepage | YAML (split files) | Strong service integrations (Sonarr, Pi-hole, etc.) | Reverse-proxy auth | Lightweight, Next.js |
| Heimdall | Web UI (DB) | Minimal | Built-in | Simplest, PHP-based |
| Flame | Web UI (DB) | Weather, search | Password | Minimalist terminal aesthetic |
Prerequisites
Before starting, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access.
- SSH access to the server.
- At least 1 GB of RAM and 1 vCPU. Dashy is lightweight — the container idles under 150 MB of RAM.
- A domain name (e.g.
dash.example.com) with an A record pointing at your VPS IP, if you want HTTPS via Nginx (Step 11). - Docker and Docker Compose installed. Step 1 handles this if you have not done it yet. You can also refer to the dedicated Docker on Ubuntu 24.04 guide.
Recommended plan: CloudCore Starter>
Dashy will run on almost anything, but you will likely want the same VPS to host a bunch of other services it links to. The CloudCore Starter gives you:>
- 4 vCPU cores
- 8 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth>
That is enough for Dashy, Nginx, Docker, and a solid half-dozen companion services (Uptime Kuma, Portainer, Grafana, Gitea, and so on) without breaking a sweat.
Connect to your server:
ssh root@your-server-ipStep 1: Update System and Install Docker
Refresh the package index and apply any pending upgrades:
sudo apt update && sudo apt upgrade -yIf the kernel was updated, reboot and reconnect:
sudo rebootInstall Docker Engine and the Compose plugin using the official convenience script:
curl -fsSL https://get.docker.com | sudo sh
sudo apt install -y docker-compose-pluginVerify both are installed:
docker --version
docker compose versionExpected output (versions will vary):
Docker version 26.1.3, build b72abbb
Docker Compose version v2.27.0Add your non-root user to the docker group so you do not have to sudo every command (log out and back in for it to apply):
sudo usermod -aG docker $USERStep 2: Create the Dashy Directory Structure
Pick a home for Dashy's config. /opt/dashy is a clean choice on a dedicated VPS:
sudo mkdir -p /opt/dashy
sudo chown -R $USER:$USER /opt/dashy
cd /opt/dashyCreate a user-data directory — this is the single volume Dashy needs — and an empty conf.yml inside it:
mkdir -p /opt/dashy/user-data
touch /opt/dashy/user-data/conf.ymlWhy user-data? Inside the container, Dashy reads /app/user-data/conf.yml and writes backups and uploaded assets (like custom icons) to the same directory. Mounting a single host directory there gives you persistence with no surprises.
Step 3: Write the Docker Compose File
Create /opt/dashy/docker-compose.yml:
services:
dashy:
image: lissy93/dashy:latest
container_name: dashy
restart: unless-stopped
ports:
- "127.0.0.1:4000:8080"
volumes:
- ./user-data:/app/user-data
environment:
- NODE_ENV=production
- UID=1000
- GID=1000
healthcheck:
test: ["CMD", "node", "/app/services/healthcheck"]
interval: 90s
timeout: 10s
retries: 3
start_period: 40sA few notes on the choices:
- Image tag:
lissy93/dashy:latestis fine for most people. If you want reproducible deploys, pin a version — for examplelissy93/dashy:3.1.1— and update it deliberately. - Port binding
127.0.0.1:4000:8080: the container listens on 8080 internally; we publish it only on localhost of the host. Nginx (Step 11) will provide the public-facing TLS endpoint. If you want to expose Dashy directly on the internet without Nginx, change this to4000:8080— but please do not skip the auth configuration in Step 9. - Volumes: bind-mount
./user-datasoconf.ymlsurvives container rebuilds and you can edit it with a normal text editor on the host. - UID/GID: match these to the owner of
./user-dataon the host so the container can write backups and uploads.
Step 4: Create Your First conf.yml
Open /opt/dashy/user-data/conf.yml in your editor of choice and paste this minimal starter config:
pageInfo: title: My VPS Dashboard description: Everything I host, in one place navLinks: - title: GitHub path: https://github.comappConfig: theme: nord-frost layout: auto iconSize: medium language: en statusCheck: true statusCheckInterval: 300
sections: - name: Infrastructure icon: fas fa-server items: - title: Portainer description: Container management icon: hl-portainer url: https://portainer.example.com - title: Traefik description: Reverse proxy dashboard icon: hl-traefik url: https://traefik.example.com/dashboard/ - name: Monitoring icon: fas fa-chart-line items: - title: Grafana description: Metrics and dashboards icon: hl-grafana url: https://grafana.example.com - title: Uptime Kuma description: Status page icon: hl-uptime-kuma url: https://status.example.com
This gives you two sections, four items with icons drawn from dashboard-icons (the hl-* prefix), status checking enabled globally, and the Nord Frost theme. Adjust URLs to match what you actually run.
Step 5: Launch Dashy
From /opt/dashy, bring the stack up:
docker compose up -dExpected output:
[+] Running 1/1
✔ Container dashy StartedConfirm the container is healthy:
docker compose psExpected output:
NAME IMAGE STATUS PORTS
dashy lissy93/dashy:latest Up 30 seconds (healthy) 127.0.0.1:4000->8080/tcpBecause we bound to 127.0.0.1:4000, test locally via SSH tunnel or curl:
curl -I http://127.0.0.1:4000Expected output:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=UTF-8To open the dashboard in a browser before setting up Nginx, forward the port over SSH from your workstation:
ssh -L 4000:127.0.0.1:4000 root@your-server-ipThen visit http://localhost:4000.
Step 6: Organize Sections, Items, and Icons
Dashy's information model is two levels deep:
- Section — a titled group (e.g. Infrastructure, Media, Development). Each section can have its own icon, a
displayDatablock to control column span, and an optional per-section background. - Item — a single link inside a section. Each item has a
title, optionaldescription,icon,url,target(how the link opens:newtab,sametab,modal,workspace), and optionalstatusCheckandtags.
Icons: four sources
Dashy accepts icons from several sources and you just prefix the icon: value:
icon: fas fa-server, icon: fab fa-github. Full catalog at fontawesome.com.icon: hl-grafana, icon: hl-portainer. Maps to the walkxcode/dashboard-icons library — over 1,000 high-quality SVG icons of popular self-hosted apps. This is what you will use most often.icon: si-docker. Uses the Simple Icons brand pack.icon: favicon tells Dashy to download the favicon from the url field. Works for any site but quality varies./opt/dashy/user-data/item-icons/ and reference it as icon: /item-icons/myapp.png.Opening behavior
The target field controls where links go:
newtab— standard new browser tab (default).sametab— replace the dashboard page.modal— open in an iframe inside Dashy. Only works if the target site allows framing (X-Frame-Optionsnot set to DENY).workspace— launch in Dashy's workspace view, a tabbed multi-pane overlay. Excellent for cycling between Grafana, Portainer, and your logs on a big display.
Step 7: Add Widgets
Widgets are live data tiles. They live either at the top of a section (via a section's widgets: key) or as standalone sections. A sampler:
sections:
- name: Ops Widgets
widgets:
- type: system-info
options:
hideId: true
- type: public-ip
- type: weather
options:
apiKey: your-openweathermap-api-key
city: Berlin
units: metric
- type: crypto-price-chart
options:
asset: bitcoin
currency: USD
days: 30
- type: cron-ping
options:
hookId: your-cronhub-id
- type: rss-feed
options:
rssUrl: https://news.ycombinator.com/rss
limit: 5Popular widgets and what they need:
| Widget | Purpose | Required options |
|---|---|---|
status-check | Explicit HTTP check tile | url |
weather | Current conditions | apiKey (OpenWeatherMap), city |
crypto-price-chart | BTC/ETH/etc price history | asset, currency, days |
system-info | OS, uptime, load (reads from Dashy's host) | none |
public-ip | WAN IP + geo lookup | none |
rss-feed | Arbitrary RSS reader | rssUrl |
github-trending | Trending repos | lang, since |
uptime-kuma | Pull status from Uptime Kuma | url |
cron-ping | Health of a scheduled job | hookId |
gluetun-port-forward | VPN forwarded port display | url |
useProxy: true in the widget's options.The full catalog lives at dashy.to/docs/widgets.
Step 8: Enable Status Checks
Status checks turn every item into a live "is this up?" tile with a green/red indicator and an HTTP status code on hover.
Enable globally in appConfig:
appConfig:
statusCheck: true
statusCheckInterval: 300 # secondsOverride per item if you need different behavior:
- title: Grafana
url: https://grafana.example.com
statusCheck: true
statusCheckUrl: https://grafana.example.com/api/health
statusCheckHeaders:
Authorization: "Bearer xyz"
statusCheckAcceptCodes: "200,201,204"
statusCheckAllowInsecure: falseKey fields:
statusCheckUrl— if your app has a dedicated/healthor/pingendpoint, point at it instead of the homepage. Saves bandwidth and works even when the homepage redirects.statusCheckAcceptCodes— some apps return 401 or 403 on unauthenticated homepage hits but are actually healthy. Add those codes here to avoid false reds.statusCheckAllowInsecure: true— only for self-signed certs on a LAN. Never enable this for a public endpoint.
Step 9: Configure Authentication
Until you put Dashy behind auth, anyone who knows the URL can see every link on your dashboard. Two options:
Option A: Simple JWT Auth (built in, no extra services)
Generate a bcrypt-ish hash for your password. Dashy accepts the hex-encoded SHA-256 of the password, which you can produce right on the server:
echo -n "your-strong-password" | sha256sum | awk '{print $1}'Expected output:
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8Add an auth block to conf.yml:
appConfig:
auth:
users:
- user: admin
hash: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
type: admin
- user: viewer
hash: 7c6a180b36896a0a8c02787eeafb0e4c1c5e2e9d1c1d3fa4a94c4b7e2f91c8de
type: normalApply the change:
cd /opt/dashy
docker compose restartNow visiting Dashy presents a login screen. admin users can access the visual config editor; normal users just view the dashboard.
This auth is client-side JWT — tokens live in the browser and the underlying assets can still be fetched directly if someone knows exact URLs. It is perfectly fine for a personal dashboard but not a hard security boundary. Combine with Nginx basic auth or IP allow-lists (Step 11) for anything sensitive.
Option B: Keycloak SSO
If you already run Keycloak (or another OIDC provider fronted by Keycloak), Dashy can delegate to it:
appConfig:
auth:
enableKeycloak: true
keycloak:
serverUrl: https://sso.example.com
realm: my-realm
clientId: dashyIn Keycloak, create a new client with Public access type, dashy as the Client ID, and add your Dashy URL as a valid redirect URI (e.g. https://dash.example.com/*). Restart Dashy and the login button will bounce users through your Keycloak realm.
Keycloak is the right call when Dashy needs to share identity with the rest of your stack (Grafana, Nextcloud, GitLab, etc.).
Step 10: Themes and Appearance
Dashy ships with around two dozen themes. Some favorites:
nord-frost/nord-polar-night— cool muted blues.material— Google Material defaults, clean and professional.adventure— warm earth tones.hacker-terminal— green-on-black, monospaced, for the full SRE aesthetic.minimal-dark— pure dark mode with minimal chrome.thebe— clean light theme with pink accents.
appConfig:
theme: nord-frostUsers can switch themes from the UI; their choice is stored in localStorage and does not change the YAML.
Custom themes
Drop a CSS file into /opt/dashy/user-data/custom-css/ and reference it in appConfig:
appConfig:
customCss: "body { background-image: url('/item-icons/bg.jpg'); }"For more involved custom themes, define CSS variables. Dashy exposes --primary, --background, --text-color, and dozens more; the full list is in the theming docs.
Other appearance tweaks worth knowing:
layout: vertical | horizontal | auto— section stacking.iconSize: small | medium | largecssThemes: [my-first-theme, my-second-theme]— register custom theme names so they show up in the theme picker.
Step 11: Nginx Reverse Proxy with TLS
Point dash.example.com at your VPS in DNS first (A record, TTL 300). Then install Nginx and Certbot:
sudo apt install -y nginx certbot python3-certbot-nginxCreate /etc/nginx/sites-available/dashy:
server { listen 80; server_name dash.example.com; return 301 https://$host$request_uri; }server { listen 443 ssl http2; server_name dash.example.com;
ssl_certificate /etc/letsencrypt/live/dash.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/dash.example.com/privkey.pem;
# Security headers add_header X-Content-Type-Options nosniff; add_header Referrer-Policy strict-origin-when-cross-origin; add_header X-Frame-Options SAMEORIGIN;
client_max_body_size 10m;
location / { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
# Required for Dashy's save-config endpoint and long status checks proxy_read_timeout 300s; proxy_send_timeout 300s; } }
Enable and test:
sudo ln -s /etc/nginx/sites-available/dashy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxObtain a Let's Encrypt certificate:
sudo certbot --nginx -d dash.example.comCertbot will edit the Nginx config to serve HTTPS and install a systemd timer to renew automatically. Visit https://dash.example.com — you should see your dashboard with a valid green padlock.
For a deeper dive into Nginx tuning see our Nginx on Ubuntu 24.04 guide.
Step 12: Back Up conf.yml
Your entire Dashy setup lives in one file. Back it up daily to remote storage (S3-compatible, rsync to another VPS, or an encrypted Borg repo).
A minimal cron-driven snapshot to /opt/backups/dashy:
sudo mkdir -p /opt/backups/dashy
sudo tee /usr/local/bin/backup-dashy.sh > /dev/null <<'EOF'
#!/bin/bash
set -euo pipefail
STAMP=$(date +%Y%m%d-%H%M%S)
cp /opt/dashy/user-data/conf.yml /opt/backups/dashy/conf-${STAMP}.yml
keep last 30 days
find /opt/backups/dashy -name 'conf-*.yml' -mtime +30 -delete
EOF
sudo chmod +x /usr/local/bin/backup-dashy.shSchedule via cron:
sudo crontab -eAdd:
0 3 * /usr/local/bin/backup-dashy.shFor offsite copies, add an aws s3 cp or rclone copyto line after the cp in the script.
Restoring is as easy as copying the backup back to /opt/dashy/user-data/conf.yml and running docker compose restart dashy.
Editing Config: UI vs Manual YAML
Dashy gives you two ways to edit conf.yml. Understanding when each is safe will save you grief.
Visual config editor (UI)
With an admin user logged in, click the settings gear → Edit Config. You get a schema-driven form editor for sections, items, widgets, and app settings. When you save, Dashy writes the new YAML to /app/user-data/conf.yml inside the container — which, because of our bind mount, is /opt/dashy/user-data/conf.yml on the host.
Pros: guided, validated, prevents YAML typos, good for quick item additions.
Cons: comments and custom formatting in your YAML are lost on save; re-indenting is sometimes cosmetically different from what you wrote.
Manual YAML editing
Edit /opt/dashy/user-data/conf.yml with vim, nano, or VS Code over SSH/Remote-SSH.
Pros: full control, preserves comments, easy to manage in Git, unlocks features the UI editor does not yet support (custom CSS paths, certain widget options).
Cons: a stray indent crashes the dashboard on the next reload.
When to rebuild vs restart
Dashy hot-reloads most config changes when you hit "Reload" in the UI. In a few cases you need to explicitly restart the container:
- Auth changes (adding users, switching to Keycloak) —
docker compose restart dashy. - Custom CSS paths that reference new files mounted into the container.
- Image or version bump:
cd /opt/dashy
docker compose pull
docker compose up -dA hard rebuild (regenerating the frontend bundle) is only necessary if you have forked Dashy and modified the source. The Docker image ships a pre-built bundle.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
502 Bad Gateway from Nginx | Container not running, or Nginx pointing at wrong port | Check docker compose ps, confirm container is (healthy) and that Nginx proxy_pass matches 127.0.0.1:4000 |
| Dashboard shows "Config is invalid" banner | YAML syntax error | Run docker compose logs dashy — the error points to a line number. Lint with yamllint /opt/dashy/user-data/conf.yml before restarting |
| Widgets stuck on "Loading…" forever | CORS blocked browser-side request | Add useProxy: true inside the widget's options block |
| Status checks all red even though apps are up | Dashy backend cannot reach the URL from inside the container | If checking an internal container, use the Docker service name or attach Dashy to the same Docker network; do not use localhost |
| Can't save config from UI | user-data directory not writable by the container's UID | sudo chown -R 1000:1000 /opt/dashy/user-data |
| Login loop with Keycloak | Redirect URI mismatch | In Keycloak client, set Valid Redirect URIs to exactly your Dashy URL with a trailing /* |
| Status check hits Cloudflare interstitial | JS challenge blocking Dashy's backend | Add the VPS IP to a Cloudflare firewall bypass rule or use the app's direct origin URL |
| Weather widget shows "Invalid API key" | OpenWeatherMap free keys take up to 2 hours to activate | Wait, then restart: docker compose restart dashy |
Container unhealthy after upgrade | Breaking change in a minor version | docker compose logs dashy, then pin to a previous tag: image: lissy93/dashy:3.0.0 |
Viewing logs
cd /opt/dashy
docker compose logs -f dashyGrep for the common noisy lines:
docker compose logs dashy | grep -iE 'error|warn|invalid'FAQ
How much RAM does Dashy actually use?
Idle, Dashy uses 100-150 MB RAM. Under heavy status-checking (50+ items, 60-second interval) it climbs to 200-250 MB. CPU is negligible on any modern VPS — even a 1 vCPU instance handles it without noticeable load.
Can Dashy monitor services on other servers?
Yes. Status checks are plain HTTP/HTTPS requests made by Dashy's backend. Point them at any reachable URL — another VPS, a cloud endpoint, a LAN service accessible via VPN. For internal-only services, the Dashy container must be on the same network (Docker network, Tailscale tailnet, WireGuard tunnel).
Does Dashy support multiple dashboards for different users?
Kind of. The multi-page feature lets you define several pages in one conf.yml, each with its own sections. Combined with per-user JWT auth, you can restrict pages by user role. For fully independent dashboards per team, run multiple Dashy containers with separate user-data directories and different subdomains.
How do I migrate an existing Homepage or Heimdall setup to Dashy?
There is no automated import, but both Homepage's services.yaml and Heimdall's exported DB map cleanly to Dashy sections/items. Write a short script (Python or yq) that iterates the source list and emits Dashy's YAML schema. Start with 5-10 items by hand first so you learn the shape.
Is Dashy still actively maintained?
Yes. The lissy93/dashy repository sees regular commits, the Docker image is rebuilt on a predictable cadence, and the issue tracker is responsive. Release notes live at github.com/lissy93/dashy/releases.
Can I version-control my conf.yml in a private Git repo?
Absolutely, and you should. Create a repo, add /opt/dashy/user-data/conf.yml as a symlink or commit it from a working copy elsewhere, and push after each change. Combined with the backup script in Step 12 you get bulletproof recoverability.
Next Steps
Dashy becomes more valuable the more of your stack you link to from it. Some natural follow-ups:
- Install Homepage alongside Dashy — run both on separate subdomains and compare styles for a week before picking a permanent start page. Our Homepage install guide has the same Docker-Compose-and-Nginx structure, so setup takes 10 minutes.
- Set up Uptime Kuma — wire its status JSON into Dashy's
uptime-kumawidget for a richer monitoring tile. See our Uptime Kuma install guide. - Deploy Portainer for visual Docker management — perfect companion service to link from Dashy's Infrastructure section.
- Harden with Fail2ban on the SSH and Nginx ports that now serve your dashboard publicly.
- Put everything behind Tailscale if you want private-only access without public DNS — skip Step 11 entirely and bind Dashy to your tailnet IP instead.
- Read the official Dashy documentation — we covered the 90% path; docs cover the last 10% (custom pages, advanced widget options, API endpoints).
Get Dashy-Ready Hosting in One Click>
Every CloudCore Starter VPS ships with Ubuntu 24.04, Docker support, and a real public IPv4 — the exact base this guide assumes. Deploy in under 60 seconds and paste in the Docker Compose file above.>
- 4 vCPU cores
- 8 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- Public IPv4 included>
Launch your CloudCore Starter now and have Dashy live at your own domain before your coffee gets cold.