Version 1.0.0
A modern, self-hosted homepage dashboard for the homelab community.
Yes, there are already plenty of homelab dashboard and bookmark applications out there: Homer, Dashy, Heimdall, Homepage, Organizr, Homarr, and more. If you're using one and happy with it, stick with it. Seriously.
I created HOPS because none of the existing options matched what I wanted:
100% GUI-based editing - I wanted to click, drag, and configure everything visually. No YAML files. No JSON editing. No "simple" configuration files that inevitably become not-so-simple. If you enjoy hand-crafting configuration files, HOPS isn't for you. Close this tab. Now.
Native installation - I have no experience with Docker or containers, and I didn't want to learn just to run a dashboard. HOPS is a single Go binary with a SQLite database. Download, run, done.
Power features without complexity - Drag-and-drop everything. Multiple dashboards. Tabs. Groups. Background slideshows. Theme customization. Status monitoring. All configurable through the UI.
HOPS won't be for everyone. That's fine. But if you've been frustrated editing YAML indentation at 11pm, or wished you could just click to add a new bookmark, maybe give it a try.
Already using Homer, Dashy, or Heimdall? HOPS can import your existing configuration, so you can try it without starting from scratch.
Start the backend:
cd backend
go run cmd/hops/main.go --port 8080 --data ../data
Start the frontend:
cd frontend
pnpm install
pnpm dev
Access the application:
adminadminImportant: Change the default password after first login via the "Change Password" button in the admin panel.
cd backend
go build -o hops ./cmd/hops
cd frontend
pnpm build
The backend will serve the static frontend files from ../frontend/build.
cd backend
./hops --port 8080 --data ../data --frontend ../frontend/build
hops/
├── backend/
│ ├── cmd/
│ │ └── hops/ # Main application
│ ├── internal/
│ │ ├── api/ # HTTP handlers and routing
│ │ ├── auth/ # Authentication service
│ │ ├── config/ # Configuration management
│ │ ├── database/ # SQLite setup and migrations
│ │ ├── models/ # Data models
│ │ ├── status/ # Status checking (HTTP/ICMP)
│ │ └── integrations/ # External service integrations
│ └── go.mod
├── frontend/
│ ├── src/
│ │ ├── lib/
│ │ │ ├── components/ # Reusable Svelte components
│ │ │ ├── stores/ # Svelte stores (state management)
│ │ │ ├── types/ # TypeScript type definitions
│ │ │ └── utils/ # Utility functions (API client, etc.)
│ │ └── routes/
│ │ ├── admin/ # Admin panel route
│ │ ├── [dashboard]/ # Dynamic dashboard routes
│ │ └── d/[secret]/ # Secret URL dashboards
├── data/ # SQLite database and uploads
│ └── backgrounds/ # Uploaded background images
└── docs/ # Documentation
GET /api/config - Get dashboard configurationGET /api/status/:entryId - Get status for an entryPOST /api/auth/login - Admin loginGET /api/backgrounds - List background images and categoriesPUT /api/config/update - Update configurationPOST /api/auth/logout - LogoutPOST /api/auth/change-password - Change passwordGET /api/config/export - Export configurationPOST /api/config/import - Import configurationPOST /api/backgrounds - Upload background imagePUT /api/backgrounds/:id - Update background metadataDELETE /api/backgrounds/:id - Delete background imageGET /api/backgrounds/categories - List background categoriesPOST /api/backgrounds/categories - Create background categoryThe configuration is stored in SQLite as JSON. Example structure:
{
"dashboards": [
{
"id": "home",
"name": "Home",
"path": "/home",
"background": {
"type": "slideshow",
"images": ["https://example.com/bg1.jpg", "/backgrounds/custom.jpg"],
"interval": 30,
"transition": "kenburns",
"transitionDuration": 2.5,
"fit": "cover"
},
"tabs": [
{
"id": "services",
"name": "Services",
"color": "#3b82f6",
"opacity": 0.95,
"groups": [
{
"id": "media",
"name": "Media",
"collapsed": false,
"color": "#8b5cf6",
"opacity": 0.9,
"entries": [
{
"id": "plex",
"name": "Plex",
"url": "https://plex.local",
"icon": "simple-icons:plex",
"openMode": "newtab",
"size": "medium"
}
]
}
]
}
]
}
],
"theme": {
"mode": "dark"
},
"settings": {
"searchHotkey": "/",
"defaultView": "/home"
}
}
HOPS has no special reverse proxy requirements. Simply proxy to the backend port (default 8080) with your preferred solution (Caddy, nginx, Traefik, etc.).
Create /etc/systemd/system/hops.service:
[Unit]
Description=HOPS - Home Operations Portal System
After=network.target
[Service]
Type=simple
User=your-username
WorkingDirectory=/path/to/hops/backend
ExecStart=/path/to/hops/backend/hops --port 8080 --data /path/to/hops/data --frontend /path/to/hops/frontend/build
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable hops
sudo systemctl start hops
| Shortcut | Action |
|---|---|
Ctrl+C |
Copy selected tile |
Ctrl+X |
Cut selected tile |
Ctrl+V |
Paste tile into focused group |
Escape |
Close modal / Cancel edit |
Ctrl+Enter |
Save and close modal |
Shortcuts work when edit mode is enabled.
Future improvements under consideration:
This is a personal project, but feel free to fork and customize for your needs!
Found a bug or have an idea? Please report issues, bugs, or suggestions for improvements via GitHub Issues. I maintain this project in my limited spare time, so while I'll do my best to review and consider all feedback, I can't guarantee when (or if) I'll be able to address them. Your patience is appreciated!
MIT