Difficulty: Beginner  ·  June 2026

I Built a Homelab Dashboard in a Weekend

Three files. No framework. No build step.

I have had a homelab running for a while. A few servers, a handful of self-hosted services, the usual setup. For a long time I did what most people do and kept a browser bookmarks folder with links to everything. Portainer here, Jenkins there, GitLab somewhere else. It worked. It also felt like living out of a junk drawer.

What I actually wanted was one page. Open a new tab, see everything, click anything. No login, no cloud, no subscription.

"The goal was something that felt considered. Not a tool I tolerated, but a page I actually liked opening."

So on a Saturday I sat down and built it.

What It Does

Two pages. The main page shows a service grid, one card per self-hosted service with a name, icon, and host address. The bookmarks page is a full bookmark manager with search, tag filtering, and personal bookmarks that persist in localStorage.

Both pages share a single config file. You edit that one file and everything updates. Add a service and a new card appears. Add a bookmark with a tag and a new filter chip shows up automatically. No build step, no bundler, no package manager involved.

There is also live weather from Open-Meteo, a live clock, and time-of-day greetings. Small things that make the page feel alive.

What You Need

The barrier is about as low as it gets. Here is everything required:

A server or always-on machine
Docker installed
A text editor
About ten minutes
Cost: Free. Open-Meteo has no API key and no rate limits for personal use. The nginx Docker image is free. The whole thing runs on hardware you already own.

How It Works

One config file

Everything lives in config.js. Your name, location for weather, services, bookmarks, and greeting messages. Both pages load this file at runtime. You never touch index.html or bookmarks.html.

A service entry looks like this:

{ name: "Portainer", icon: "🐳", url: "http://your-server:9000", meta: "your-server:9000" }

Add an object to the array, get a new card on the dashboard. Remove one, it disappears. The grid reflows automatically. Same pattern for bookmarks. Tags are optional but they build the filter chips automatically from whatever values exist.

Weather

Weather reads your coordinates and timezone from config and hits the Open-Meteo API. No account required, no API key, no rate limits. It refreshes every ten minutes and fails silently if the fetch fails.

location: {
    label: "City, ST",
    lat:    44.9778,
    lon:   -93.2650,
    tz:    "America/Chicago",
}

Bookmarks

Built-in bookmarks come from config.js and are the same across every browser that opens the page. Personal bookmarks are added through the UI and stored in localStorage. They live in your browser, not on the server, and never touch the network. Both types are searchable and support tags.

Running It

One Docker command:

docker run -d \
  --name dashboard \
  -p 8080:80 \
  -v $(pwd):/usr/share/nginx/html:ro \
  nginx:alpine

Open http://localhost:8080 and you are done. For something permanent alongside other services, a Compose entry handles it:

services:
  dashboard:
    image: nginx:alpine
    container_name: dashboard
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./self-host-dashboard:/usr/share/nginx/html:ro

What I Would Do Differently

localStorage bookmarks are per-browser. If you open the dashboard on a different machine your personal bookmarks are not there. For a single-person homelab that is probably fine. For anything shared it is not. A small backend that reads and writes a JSON file on the server would fix it. A few lines of Python or Go. I did not build it because I did not need it, but it is the obvious next step.

The other thing I thought about is service status indicators. A dot on each card showing whether the service is actually reachable. CORS makes direct health checks from the browser unreliable so you would need a small proxy to do it cleanly. Maybe version two.

Get the Code

Free and open source. Edit config.js, run the Docker command, have a dashboard.

github.com/MichaelCoughlinAN/hiimmichael

The README has the full quickstart. If you run it or build on top of it I would like to see it.

About Author Blog Contact