14 KiB
Services Homepage
A lightweight, self-hosted dashboard for quick access to your Docker services with a modern holographic UI design.
Features
- 🎨 Holographic Glass Design - Modern liquid glass buttons with animated shimmer effects
- 🎯 Dynamic Service Loading - Services configured via simple XML file
- 🔍 Smart URL Resolution - Automatic handling of ports, protocols, and hostnames
- 🎭 Icon Integration - Includes Simple Icons pack (3000+ brand logos)
- 📱 Responsive Layout - Works seamlessly on desktop and mobile
- 🐳 Docker-Ready - Single-container deployment with nginx
- ⚡ Lightweight - Minimal resources, fast loading
- 🔎 Search & Filter - Instant search with keyboard shortcut (press
/) - ℹ️ Connection Details - Info button shows hostname/port for each service
- 🎮 Keyboard Navigation - Arrow keys to navigate, Enter to open, Esc to clear
- 🟢 Status Indicators - Optional visual status (online/offline/maintenance)
- 🏥 Automatic Health Checks - Real-time ping tests to detect service availability
- 📂 Service Groups - Organize services into categorized sections
Quick Start
1. Deploy with Docker Compose
docker-compose up -d
The homepage will be available at http://localhost:8088
2. Configure Your Services
Edit services.xml to add your services organized into groups:
<?xml version="1.0" encoding="UTF-8"?>
<services>
<group name="Management">
<service
name="Portainer"
proto="https"
port="9443"
logo="portainer.svg"
/>
</group>
<group name="Media">
<service
name="Jellyfin"
proto="http"
port="8096"
host="jellyfin.example.com"
logo="jellyfin.svg"
/>
</group>
</services>
Or use services without groups (they'll appear in a single grid):
<?xml version="1.0" encoding="UTF-8"?>
<services>
<service name="Portainer" proto="https" port="9443" logo="portainer.svg" />
<service name="Jellyfin" proto="http" port="8096" logo="jellyfin.svg" />
</services>
3. Restart to Apply Changes
docker-compose restart
Configuration Guide
Service Attributes
| Attribute | Required | Description | Example |
|---|---|---|---|
name |
Yes | Display name for the service | "Nextcloud" |
proto |
No | Protocol (http/https) | "https" (default: "http") |
port |
No | Port number | "8080" |
host |
No | Custom hostname or full URL | "nextcloud.example.com" |
logo |
No | Icon filename in /logos/ |
"nextcloud.svg" |
status |
No | Set to "maintenance" to skip health check |
"maintenance" |
check-health |
No | Enable/disable auto health check | "true" (default) or "false" |
URL Resolution Logic
The service URL is built using this priority:
- Full URL - If
hoststarts withhttp://orhttps://, use as-is - Hostname with Port - If
hostcontains:port, useproto://host:port - Hostname Only - If
hostis set (no port), usehttps://host(ignoresprotoandport) - Fallback - Use current page hostname with specified
protoandport
Examples
<!-- Full URL (ignores proto and port) -->
<service name="Example" host="https://example.com/app" />
<!-- Public domain (defaults to HTTPS) -->
<service name="Nextcloud" host="cloud.example.com" logo="nextcloud.svg" />
<!-- Custom port on domain -->
<service name="Jellyfin" host="media.example.com:8096" proto="http" logo="jellyfin.svg" />
<!-- Local service (uses page hostname) -->
<service name="Portainer" proto="https" port="9443" logo="portainer.svg" />
<!-- Service with status indicator -->
<service name="Home Assistant" proto="http" port="8123" logo="homeassistant.svg" status="online" />
Keyboard Shortcuts
/- Focus the search bar (press from anywhere)- Arrow Keys - Navigate between service cards (Up/Down/Left/Right)
- Enter - Open the selected service in a new tab
- Esc - Clear the current selection
Info Button
When a service has both a hostname and port configured, a small info button (ⓘ) appears in the bottom-right corner of the card. Click it to view connection details including:
- Service name
- Hostname
- Port number
- Protocol
Status Indicators
Services are automatically checked for availability when the page loads. Status indicators appear in the top-right corner of each card:
<!-- Automatic health check (default behavior) -->
<service name="Jellyfin" proto="http" port="8096" logo="jellyfin.svg" />
<!-- Manual maintenance mode (skips health check) -->
<service name="Nextcloud" status="maintenance" host="cloud.example.com" logo="nextcloud.svg" />
<!-- Disable health check for a service -->
<service name="Legacy App" check-health="false" proto="http" port="8080" logo="app.svg" />
Status colors:
- Gray spinning (checking) - Currently testing service availability
- Green pulsing (online) - Service responded successfully to health check
- Red (offline) - Service failed to respond or timed out (5 seconds)
- Orange (maintenance) - Manual maintenance mode, health check skipped
How Health Checks Work
- Each service is automatically pinged when the page loads
- Uses a 5-second timeout per service
- Checks run in parallel for all services
- Services marked
status="maintenance"skip the health check - Set
check-health="false"to disable checking for specific services - No server-side component needed - runs entirely in the browser
Service Groups
Organize your services into categorized sections for better organization:
<services>
<group name="Management">
<service name="Portainer" proto="https" port="9443" logo="portainer.svg" />
<service name="Uptime Kuma" proto="http" port="3001" logo="uptime-kuma.svg" />
</group>
<group name="Media">
<service name="Jellyfin" proto="http" port="8096" logo="jellyfin.svg" />
<service name="Transmission" proto="http" port="9091" logo="transmission.svg" />
</group>
<group name="Storage">
<service name="Nextcloud" host="cloud.example.com" logo="nextcloud.svg" />
<service name="FileBrowser" proto="http" port="8986" logo="filebrowser.svg" />
</group>
</services>
Group Features
- Categorization: Group related services together (Media, Management, Development, etc.)
- Visual Separation: Each group has a styled header with an accent underline
- Smart Search: Searching filters services and hides empty groups automatically
- Backward Compatible: Services without groups still work (displayed in a single grid)
- Flexible: Use as many or as few groups as needed
Group Tips
- Use clear, descriptive group names (e.g., "Media Services" instead of "Group 1")
- Keep related services together for easier navigation
- Groups appear in the order defined in the XML
- Empty groups (no services) are automatically hidden
Icon Management
Using Included Icons
The repository includes Simple Icons (3000+ brand logos). Available icons are in:
logos/simple-icons/icons/
Adding Custom Icons
- Add your SVG file to the
logos/directory - Reference it in
services.xml:
<service name="MyApp" logo="myapp.svg" />
Icon Styling
All icons are automatically styled white using CSS filters. To customize:
Edit styles.css and modify the .card .logo rule:
.card .logo {
filter: brightness(0) invert(1); /* White icons */
/* OR */
filter: hue-rotate(180deg); /* Color shift */
}
Customization
Theme Colors
Edit CSS variables in styles.css:
:root {
--bg: #0f1720; /* Background color */
--card: #0b1220; /* Card background */
--accent: #4f46e5; /* Accent color (purple) */
--muted: #94a3b8; /* Muted text */
}
Holographic Effects
The holographic button effects include:
- Shimmer Animation - Continuous light sweep (8s loop, 3s on hover)
- Gradient Background - Purple/pink gradient blend
- Glowing Border - Animated gradient border on hover
- Backdrop Blur - Glass-like frosted effect
To adjust shimmer speed, modify the @keyframes shimmer animation:
.card::before {
animation: shimmer 8s infinite linear; /* Change 8s to adjust speed */
}
Layout
Change grid responsiveness in styles.css:
.grid {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
/* Adjust minmax() to change card width */
}
File Structure
services-homepage/
├── docker-compose.yml # Docker deployment config
├── Dockerfile # Custom nginx build (if needed)
├── index.html # Main HTML page
├── styles.css # Holographic UI styles
├── services.xml # Service definitions
├── README.md # This file
├── LICENSE # License information
└── logos/ # Icon directory
├── simple-icons/ # Simple Icons pack (3000+ logos)
├── default.svg # Fallback icon
├── gitea.svg
├── jellyfin.svg
├── nextcloud.svg
└── ...
Updating Icons
Replace All Icons with Simple Icons
cd logos
for f in *.svg; do
basename=$(basename "$f" .svg)
match=$(find simple-icons/icons -type f -iname "*${basename}*.svg" -print -quit)
[ -n "$match" ] && cp "$match" "$f" && echo "Updated: $f"
done
Find Available Icons
ls logos/simple-icons/icons/ | grep -i "keyword"
Backup and Recovery
Icon backups are automatically created in:
logos/backup-YYYYMMDD-HHMMSS/
To restore from backup:
cp logos/backup-20251123-231406/*.svg logos/
docker-compose restart
Troubleshooting
Cloudflare, HTTPS and Mixed Content
If you're serving the homepage over HTTPS (for example, via Cloudflare), your browser will block active (programmatic) HTTP requests to local IPs — this is "mixed content". That can cause the health checks for local services to fail or to be marked offline.
Recommendations:
- Enable HTTPS for your local services (e.g., configure TLS or use a reverse proxy with a valid certificate) and/or use Cloudflare Tunnel to serve the service with a domain and TLS.
- Or configure a server-side proxy that performs health checks and serves the results over HTTPS (for example, add a proxy endpoint in your nginx config and proxy_pass to the local IP/port); the browser will then make a same-origin secure request to the proxy rather than directly to the IP.
- In
services.xml, use thetailscale-ipattribute to supply an easily-editable Tailscale IP for local services that should be used for links and health checks. - In
services.xml, use thetailscale-ipattribute to supply an easily-editable Tailscale IP for local services that should be used for links and health checks.
Per-service local-ip override:
- If a specific service has a
local-ipattribute (for example,local-ip="192.168.2.180"), the server-side health proxy will use that local IP for the health check. This allows per-service control when the internal IP differs from the household-wide Tailscale IP or when services are bound to different hosts.
Cloudflare Insights & CORS:
- If you see console errors about
static.cloudflareinsights.comor messages like "CORS request did not succeed" or "integrity mismatch", this is likely a script injected by Cloudflare. This is not part of the homepage codebase and is injected by Cloudflare's edge. You can disable Cloudflare Analytics/Insights or adjust settings in the Cloudflare dashboard to remove or avoid that script if it's causing issues with CSP or integrity.
Services Not Loading
- Check
services.xmlsyntax:
xmllint --noout services.xml
-
Check browser console for errors (F12)
-
Verify file permissions:
chmod 644 services.xml index.html styles.css
chmod 755 logos/
Icons Not Displaying
- Verify icon exists:
ls -lh logos/youricon.svg
-
Check icon reference in
services.xmlmatches filename exactly -
Clear browser cache (Ctrl+Shift+R)
Container Issues
# View logs
docker logs services-homepage
# Restart container
docker-compose restart
# Rebuild if files changed
docker-compose up -d --force-recreate
Performance
- Image Size: ~45MB (nginx:alpine base)
- Memory Usage: ~5-10MB
- Load Time: <100ms (local network)
- Icons: Cached by browser after first load
Browser Support
- ✅ Chrome/Edge 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Mobile browsers (iOS Safari, Chrome Android)
Security
- All volumes mounted read-only (
:ro) - No external dependencies at runtime
- Logs automatically rotated (5MB max, 2 files)
- CORS not enabled (same-origin only)
Advanced Usage
Custom Nginx Config
Create nginx.conf and mount it:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
Adding Authentication
Use a reverse proxy (Nginx Proxy Manager, Caddy, Traefik) with basic auth:
location / {
auth_basic "Services";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://services-homepage:80;
}
Dynamic Port Updates
For services with dynamic ports (e.g., Transmission behind VPN), use environment variables:
<service name="Transmission" proto="http" port="${TRANS_PORT}" logo="transmission.svg" />
Then update via script or use a template processor.
Contributing
Contributions welcome! Completed features:
- ✅ Search/filter functionality with keyboard shortcut
- ✅ Keyboard navigation (arrow keys, Enter, Esc)
- ✅ Service status indicators (online/offline/maintenance)
- ✅ Automatic health checks with ping tests
- ✅ Info button showing connection details
- ✅ Service groups/categories
Areas for future improvement:
- Service health checks via custom endpoints
- Drag-and-drop reordering within groups
- Collapsible group sections
- Custom themes/color schemes
- Export/import service configurations
- Dashboard widgets (time, weather, etc.)
License
MIT License - See LICENSE file for details
Credits
- Icons: Simple Icons (CC0 1.0 Universal)
- Design Inspiration: Holographic UI by vishnu137 on CodePen
- Web Server: nginx Alpine
Support
For issues, questions, or suggestions:
- Check this README first
- Review browser console for errors
- Check Docker logs:
docker logs services-homepage - Verify
services.xmlsyntax
Version: 1.0.0
Last Updated: November 23, 2025