A focused, self-hosted dashboard for the applications and services you use every day.
Live Demo · Features · Quick Start · Configuration · API · Testing
Try it now — no install required: https://server-hub-gules.vercel.app
Everyone can use the hosted instance for free. Create an account, manage your own services, bookmarks, and settings — data is isolated per user via Supabase RLS.
Server Hub brings self-hosted applications, infrastructure tools, and frequently used links into one private homepage. It combines a lightweight Python backend (stdlib only) with a static frontend — no build step, no npm install.
Two deployment models are supported:
- Vercel + Supabase — serverless, multi-user with per-user data isolation (recommended).
- Standalone Python — single-user installation on a VPS, Proxmox LXC, or any Linux host.
- Personal dashboard — responsive service cards, category sections, bookmarks, clock, greeting, page title, and subtitle.
- Search — instant service filtering plus optional passthrough to Google, DuckDuckGo, Bing, Startpage, or a configured SearXNG instance.
- Automatic categorization — local keyword matching from name, URL, and description with manual overrides.
- Service management — add, edit, delete, reorder, and configure health pings from the dashboard or Settings.
- Bookmarks — compact frequently-used links with optional colors and icons.
- Themes and wallpapers — light / dark / auto, built-in gradients, custom image URLs, and contrast controls.
- System monitoring — local CPU / memory / disk via
/proc, plus multi-server monitoring through Beszel (singlebeszelor multi-instancebeszelsarray). - Multi-user accounts — Supabase Auth with RLS-isolated settings, services, bookmarks, and logs.
- Two-factor authentication — optional TOTP via authenticator app.
- Backup and restore — export and import settings, services, and bookmarks as JSON.
- Security controls — signed
HttpOnlysessions, CSRF checks, rate limiting, body-size limits, security headers, and path-traversal protection.
-
Create a Supabase project.
-
Run
supabase-schema.sqlonce in the Supabase SQL Editor. -
Import this repository into Vercel.
-
Add the environment variables described in Configuration.
-
Deploy and open the resulting Vercel URL — or try the live instance at https://server-hub-gules.vercel.app.
Note: Vercel deploy requires a Supabase project and running
supabase-schema.sql— the one-click Deploy button is omitted because it cannot provision the database automatically.
Vercel uses api/index.py as the Python Function entry point. It adapts the existing backend to Vercel's WSGI runtime while static assets are served through the same application.
Keep
SUPABASE_SERVICE_ROLE_KEYprivate. It is only used server-side for administrative signup and must never be exposed to browser code.
git clone https://github.com/eco-null/server-hub-multi.git
cd server-hub-multi
HUB_PASSWORD=change-me python3 server.pyOpen http://localhost:8642 and sign in with the configured username and password. For a persistent Proxmox LXC deployment, see SETUP-LXC.md.
Configuration is provided through environment variables. With Supabase variables present, Server Hub runs in multi-user mode; without them it falls back to single-user authentication.
| Variable | Default | Description |
|---|---|---|
SUPABASE_URL |
(empty) | Supabase project URL; enables multi-user mode with SUPABASE_ANON_KEY. |
SUPABASE_ANON_KEY |
(empty) | Supabase publishable / anon key for Auth and RLS-scoped requests. |
SUPABASE_SERVICE_ROLE_KEY |
(empty) | Server-only key for administrative signup flows. Never expose publicly. |
SUPABASE_SERVICE_KEY |
(empty) | Backward-compatible alias for the service-role key. |
HUB_USER |
admin |
Username in standalone single-user mode. |
HUB_PASSWORD |
— | Required in standalone mode; use a strong password. |
SESSION_SECRET |
(generated locally) | Stable signing key for sessions. Required on Vercel so sessions survive across instances. |
HUB_HOST |
0.0.0.0 |
Bind address for standalone mode. |
HUB_PORT |
8642 |
Listen port for standalone mode. |
HUB_DISK_PATH |
/ |
Filesystem path for the local disk widget. Use /host when the container exposes the host root there. |
BESZEL_URL |
(empty) | Optional Beszel hub URL, e.g. http://beszel:9520. |
BESZEL_USER |
(empty) | Beszel account used by the server-side proxy. |
BESZEL_PASSWORD |
(empty) | Beszel account password. |
Generate secrets with:
openssl rand -base64 32The schema creates:
profiles— user profile and unique username.user_settings— per-user dashboard settings and layout (JSONB; supportsbeszelandbeszels).user_servicesanduser_bookmarks— per-user links and bookmarks.user_logs— per-user frontend error and conflict logs.- Row Level Security policies restricting every record to its owning
auth.uid(). - A signup trigger that creates the initial profile and settings row.
Passwords remain in Supabase Auth. Server Hub forwards the signed-in user's token to the database API and never shares data between users.
Set BESZEL_URL, BESZEL_USER, and BESZEL_PASSWORD (env) or configure per-user Beszel instances in Settings. The dashboard supports:
- Legacy single instance:
settings.beszel = { url, user, password } - Multi-instance:
settings.beszels = [{ id, name, url, user, password }, ...]— the backend aggregatesGET /api/beszelacross all configured hubs and returns per-systemcpu,mem,disk,status, anduptime.
If Beszel is unavailable or not configured, the local /api/stats widget remains available where the host exposes Linux /proc statistics.
All API endpoints require an authenticated session unless noted otherwise. Mutating requests must be same-origin. JSON bodies are limited to 64 KiB.
| Method | Path | Description |
|---|---|---|
POST |
/login |
Sign in; supports username/email and the second TOTP step. |
POST |
/register |
Create a Supabase account. |
GET |
/logout |
End the current session. |
GET |
/api/me |
Return the current user. |
GET |
/api/bootstrap |
Coalesced batch: services + bookmarks + settings + layout + user in one round-trip (ETag / 304 supported). |
GET/POST |
/api/services |
List or create services. |
PUT/DELETE |
/api/services/<id> |
Update or delete a service. |
GET/POST |
/api/bookmarks |
List or create bookmarks. |
PUT/DELETE |
/api/bookmarks/<id> |
Update or delete a bookmark. |
GET/PUT |
/api/settings |
Read or save settings and layout (supports beszel and beszels array). |
GET/POST |
/api/logs |
Read or record frontend error / conflict logs. |
GET |
/api/stats |
Return local host CPU, memory, and disk statistics. |
GET/POST |
/api/beszel |
GET — return aggregated Beszel systems; POST — test a Beszel connection. |
GET |
/api/2fa/setup |
Create a pending TOTP setup payload. |
POST |
/api/2fa/enable |
Enable TOTP after code verification. |
POST |
/api/2fa/disable |
Disable TOTP after code verification. |
Service objects use { id, name, url, desc, icon, ping, categoryOverride }. Bookmark objects use { id, name, url, icon, color }.
| File | Purpose |
|---|---|
index.html |
Main dashboard, search, service/bookmark editors, pings, stats, and frontend error reporting. |
settings.html |
Theme, wallpaper, feature toggles, Beszel, 2FA, link management, and backup/restore. |
login.html |
Combined sign-in and account creation interface, including the TOTP step. |
register.html |
Compatibility entry point for the registration route. |
settings.js |
Shared settings persistence, local storage, server synchronization, and wallpaper handling. |
categorize.js |
Local category keyword rules and matcher. |
server.py |
Standalone HTTP server, authentication, API routes, persistence, statistics, and Beszel proxy. |
api/index.py |
Vercel WSGI bridge for the Python server. |
auth.py |
Dependency-free Supabase Auth and PostgREST client. |
supabase-schema.sql |
Supabase tables, RLS policies, triggers, and signup helper functions. |
services.json |
Standalone-mode service/bookmark storage; the checked-in file starts empty. |
test_server.py |
Python integration and security test suite (pytest). |
tests.html |
Browser-side test suite. |
SETUP-LXC.md |
Proxmox LXC and systemd deployment guide. |
SETUP.md |
Cloudflare Access and public-domain hardening guide. |
python3 -m pytest test_server.py -qFor the browser suite, serve the repository over HTTP and open tests.html:
python3 -m http.server 8000
# then visit http://localhost:8000/tests.htmlServing over HTTP is required — browsers may restrict localStorage on file:// URLs.
- Vercel — use the included
vercel.json, setSESSION_SECRET, and configure Supabase variables. - Proxmox LXC — follow
SETUP-LXC.mdfor a systemd service and persistentservices.json. - Cloudflare Access — follow
SETUP.mdwhen placing an additional edge authentication layer in front of a public deployment.
- Supabase mode isolates user data with Postgres RLS.
- Sessions use signed cookies with
HttpOnly,SameSite=Lax, andSecurewhen HTTPS is detected (__Host-prefix on Vercel). - Passwords are handled by Supabase Auth in multi-user mode and read from environment variables in standalone mode.
- Login and registration include per-IP rate limiting; API and login request bodies are capped at 64 KiB.
- Mutating requests enforce same-origin checks and
Sec-Fetch-Sitevalidation; responses includenosniff,SAMEORIGIN,no-referrer, and CSP protections. - Beszel credentials and TOTP secrets are kept server-side and never returned to the browser.
- Standalone-mode sessions are held in memory; restarting
server.pysigns users out. - Standalone-mode links and bookmarks are persisted in
services.json; back up this file with the deployment. - Local system statistics depend on Linux
/proc; use Beszel for cross-host monitoring. - The default frontend loads Tailwind, fonts, QRCode.js, and optional wallpaper assets from CDNs. A network-restricted deployment should vendor or replace these assets.
