10 KiB
Services Homepage - Features Documentation
Overview
This services homepage provides a modern, feature-rich interface for managing and monitoring your self-hosted services. All features include persistent storage using browser localStorage.
🎯 Core Features
1. Custom Health Check Endpoints
Health checks now support custom endpoints for services that don't respond on the root path.
Configuration
Add the health-path attribute to any service in services.xml:
<service id="jellyfin"
name="Jellyfin"
proto="http"
port="8096"
health-path="/health"
logo="jellyfin.svg" />
Supported attributes:
health-path: Custom path to ping (e.g.,/api/health,/ping,/status)- If not specified, defaults to root path (
/)
How It Works
The health-proxy service:
- Parses
services.xmland finds the service byid - Uses
local-ip>tailscale-ip>hostfor the target - Appends the custom
health-pathto the URL - Performs a HEAD request to check availability
2. Drag-and-Drop Service Reordering 🖱️
Reorganize your services within each group by dragging and dropping cards.
Usage
- Click and hold any service card
- Drag it to the desired position within the same group
- Release to drop
- Order is automatically saved to localStorage
Features
- ✅ Reordering within groups only (maintains organization)
- ✅ Visual feedback during drag (opacity, border highlight)
- ✅ Persistent across browser sessions
- ✅ Per-group order tracking
Reset Order
To reset to default XML order:
localStorage.removeItem('services-order');
location.reload();
3. Collapsible Service Groups 📁
Collapse and expand service groups to focus on what matters.
Usage
- Click on any group header (or the ▼ icon)
- The group will collapse/expand
- State is saved automatically
Features
- ✅ Animated collapse/expand
- ✅ Persistent state per group
- ✅ Visual indicator (rotating arrow)
- ✅ Keyboard accessible
Reset State
localStorage.removeItem('collapsed-groups');
location.reload();
4. Theme System 🎨
Choose from 5 built-in themes or customize your own.
Available Themes
- Dark (Default) - Deep blue-gray tones
- Light - Clean white and blue
- Ocean - Deep teal and aqua
- Sunset - Purple and pink gradient
- Forest - Green and earth tones
Usage
- Click the 🎨 icon in the top-right corner
- Select a theme from the menu
- Theme is applied immediately and saved
Custom Themes
Themes are defined in js/themes.js. To add a custom theme:
customTheme: {
name: 'My Theme',
primary: '#hexcolor',
secondary: '#hexcolor',
accent: '#hexcolor',
text: '#hexcolor',
textMuted: '#hexcolor',
border: '#hexcolor',
cardBg: 'rgba(...)',
headerBg: 'rgba(...)',
overlayBg: 'rgba(...)'
}
Add your theme to the themes object and rebuild.
5. Export/Import Configuration 💾
Backup and restore your service configurations with JSON export/import.
Export Configuration
- Click 📥 Export button in the top-left
- Downloads
services-config-YYYY-MM-DD.jsonfile - Contains all services, groups, and settings
Import Configuration
- Click 📤 Import button
- Select a previously exported JSON file
- Downloads a new
services.xmlfile - Replace your existing
services.xmland rebuild containers
Export Format
{
"version": "1.0",
"exportDate": "2025-11-24T...",
"tailscaleIp": "100.124.17.41",
"groups": [
{
"name": "Management",
"services": [
{
"id": "portainer",
"name": "Portainer",
"proto": "https",
"port": "9443",
"logo": "portainer.svg"
}
]
}
]
}
6. Dashboard Widgets 📊
Add live widgets to your homepage header.
Available Widgets
⏰ Clock Widget
- Real-time clock with date
- Updates every second
- Always enabled by default
🌤️ Weather Widget
- Current weather for your location
- Temperature, conditions, wind, humidity
- Requires OpenWeatherMap API key (free)
- Auto-location or custom city
💭 Daily Quote Widget
- Random inspirational quotes
- Fetched from quotable.io API
- Changes on each page load
Configuration
- Click ⚙️ Widgets button
- Enable/disable widgets with checkboxes
- For weather:
- Get API key from OpenWeatherMap
- Enter API key
- Set location to
autoor city name (e.g.,Toronto)
- Click Save & Reload
Settings Storage
All widget settings stored in localStorage:
{
"clock": {"enabled": true},
"weather": {
"enabled": true,
"apiKey": "your-api-key",
"location": "auto"
},
"quote": {"enabled": false}
}
🛠️ Technical Details
Architecture
┌─────────────────┐
│ Browser │
│ (HTTPS) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Nginx │◄── Serves static files
│ (Port 8088) │◄── Reverse proxy /healthcheck
└────────┬────────┘
│
▼
┌─────────────────┐
│ Health Proxy │
│ (Flask:8081) │◄── Server-side health checks
│ │◄── Parses services.xml
└─────────────────┘
Files Structure
services-homepage/
├── index.html # Main HTML
├── styles.css # Styles with CSS variables
├── services.xml # Service configuration
├── nginx.conf # Nginx config with resolver
├── docker-compose.yml # Multi-container setup
├── backend/
│ └── health-proxy.py # Flask health check service
└── js/
├── galaxy-background.js # 3D background
├── services-loader.js # Core service rendering
├── drag-drop.js # Drag-and-drop module
├── collapsible-groups.js # Group collapse module
├── themes.js # Theme system
├── export-import.js # Config backup/restore
├── widgets.js # Dashboard widgets
├── search.js # Search functionality
├── keyboard-nav.js # Arrow key navigation
└── readme-loader.js # Markdown rendering
localStorage Keys
| Key | Purpose | Format |
|---|---|---|
services-order |
Drag-drop order | {"GroupName": ["id1", "id2"]} |
collapsed-groups |
Collapsed state | {"GroupName": true/false} |
selected-theme |
Active theme | "dark"/"light"/"ocean"/... |
enabled-widgets |
Widget settings | {clock: {...}, weather: {...}} |
📋 services.xml Reference
Root Element
<services tailscale-ip="100.124.17.41">
Attributes:
tailscale-ip: Default IP for local services (optional)
Service Element
<service
id="unique-id"
name="Display Name"
proto="http|https"
port="8080"
host="domain.com"
logo="logo.svg"
status="maintenance"
check-health="true|false"
health-path="/custom/path"
local-ip="192.168.1.100"
/>
Attributes:
| Attribute | Required | Description | Example |
|---|---|---|---|
id |
Recommended | Unique identifier | portainer |
name |
Required | Display name | Portainer |
proto |
Optional | Protocol (default: http) |
https |
port |
Optional | Port number | 9443 |
host |
Optional | Custom hostname/URL | cloud.example.com |
logo |
Optional | Logo filename in /logos/ |
portainer.svg |
status |
Optional | Manual status override | maintenance |
check-health |
Optional | Enable health check (default: true) |
false |
health-path |
Optional | Custom health endpoint | /api/health |
local-ip |
Optional | Override IP for health checks | 192.168.1.100 |
🔧 Troubleshooting
Health Checks Return 502
Problem: Nginx can't resolve the health-proxy container.
Solution: Ensure nginx.conf contains:
resolver 127.0.0.11 valid=30s;
Widgets Not Appearing
Problem: Widgets enabled but not showing.
Solutions:
- Check browser console for errors
- For weather: Verify API key is correct
- Clear localStorage and reconfigure:
localStorage.removeItem('enabled-widgets'); location.reload();
Drag-and-Drop Not Working
Problem: Cards won't drag.
Solutions:
- Ensure JavaScript is enabled
- Check browser console for errors
- Verify cards have
draggable="true"attribute - Try different browser
Theme Not Persisting
Problem: Theme resets after reload.
Solutions:
- Check browser allows localStorage
- Try incognito/private mode to test
- Clear site data and reconfigure
🚀 Future Enhancement Ideas
- Custom widget creation API
- Service groups reordering (drag groups)
- Dark mode auto-switch (time-based)
- Service uptime statistics
- Notification system for service outages
- Mobile app (PWA)
- Multi-user configurations
- Service tags and filtering
- Global search across all services
- Service dependencies visualization
📝 Changelog
v2.0.0 - 2025-11-24
Added:
- ✨ Custom health check endpoints support
- ✨ Drag-and-drop service reordering
- ✨ Collapsible service groups
- ✨ Theme system (5 themes)
- ✨ Export/import configuration
- ✨ Dashboard widgets (clock, weather, quote)
- 🐛 Fixed nginx DNS resolver for health-proxy
- 🐛 Fixed services.xml mount in health-proxy container
v1.0.0 - Previous
- ✅ Basic service listing from XML
- ✅ Automatic health checks
- ✅ Search functionality
- ✅ Keyboard navigation
- ✅ 3D galaxy background
- ✅ README rendering
🤝 Contributing
To add features:
- Create new module in
/js/ - Export functions via
window.moduleName - Import in
index.html - Initialize in DOMContentLoaded listener
- Add styles to
styles.css - Document in this file
📄 License
This project is open source. See LICENSE file for details.
Questions or Issues? Check the README.md or open an issue.