Skip to content

Home Portal

Unified dashboard for managing and accessing all homelab services.


Production

Development

  • Dev Server: http://localhost:3000 (run from host)
  • Host Path: /root/projects/home-portal
  • tmux Session: tmux attach -t home-portal

Overview

Home Portal is the central hub for accessing and managing all homelab infrastructure and services. It provides a unified interface for:

  • Service status monitoring
  • Quick access to all dashboards
  • Infrastructure management
  • Blog/notes system
  • Widget-based customizable dashboard

Tech Stack

  • Framework: Next.js 16 (App Router)
  • UI Library: React 19
  • Styling: Tailwind CSS v4
  • Database: Supabase (PostgreSQL)
  • Authentication: Supabase Auth (shared user pool)
  • Deployment: Kubernetes (k3s)

Database

Schema

Tables are organized in the home_portal PostgreSQL schema:

-- Example schema structure
home_portal.pages
home_portal.widgets
home_portal.services
home_portal.blog_posts
home_portal.user_settings

Supabase Connection

Production (Kubernetes):

NEXT_PUBLIC_SUPABASE_URL: http://10.89.97.214:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY: <shared-anon-key>

Development (LXC):

NEXT_PUBLIC_SUPABASE_URL: http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY: <local-dev-key>

See Multi-App Supabase Architecture for details on schema isolation.


Development

Access Project

# Navigate to project (on Proxmox host)
cd /root/projects/home-portal

Running Dev Server

# Start dev server
npm run dev

# Or attach to existing tmux session
tmux attach -t home-portal

Local Supabase

# Start Supabase (first time takes 2-3 minutes)
npx supabase start

# Access local Studio
open http://localhost:54323

Database Migrations

# Create new migration
npx supabase migration new migration_name

# Ensure schema is set in migration
echo "SET search_path TO home_portal;" > supabase/migrations/xxx_migration_name.sql

# Apply migrations locally
npx supabase db reset

# Apply to production (k8s Supabase)
/root/tower-fleet/scripts/migrate-app.sh home-portal

Supabase Storage

Buckets: - home-portal-service-icons - Custom service icons (public bucket) - home-portal-travel-photos - Travel blog photos (public bucket) - home-portal-gpx-tracks - GPS tracks (public bucket) - home-portal-avatars - User avatars (public bucket)

Documentation: See Supabase Storage Guide


Deployment

Kubernetes Manifests

Located at: /root/home-portal-k8s/k8s/

k8s/
├── namespace.yaml
├── configmap.yaml
├── secrets.yaml
├── deployment.yaml
└── service.yaml

Deploy to Production

cd /root/home-portal-k8s

# Apply all manifests
kubectl apply -f k8s/

# Check deployment status
kubectl get pods -n home-portal
kubectl get svc -n home-portal

# View logs
kubectl logs -n home-portal -l app=home-portal -f

Update Deployment

# After pushing new image or updating config
kubectl rollout restart deployment/home-portal -n home-portal

# Watch rollout
kubectl rollout status deployment/home-portal -n home-portal

Architecture

Production (Kubernetes)

Client Request (http://home.internal)
DNS Resolution (10.89.97.220)
NGINX Ingress Controller (10.89.97.220)
    ↓ (routes based on Host: home.internal)
home-portal Service (ClusterIP)
home-portal Pod(s)
Supabase API Gateway (Kong: 10.89.97.214:8000)
PostgreSQL (home_portal schema)

Development (Host-Based)

Developer
Proxmox Host (localhost:3000)
Next.js Dev Server
K8s Supabase Sandbox (10.89.97.221:8000)
PostgreSQL (home_portal schema)

Features

Core Features

  • Service Dashboard: Monitor and access all homelab services
  • Status Monitoring: Real-time health checks for critical services
  • Quick Links: Fast access to frequently used dashboards
  • Widget System: Customizable dashboard with draggable widgets
  • Blog/Notes: Personal knowledge base and documentation

Upcoming Features

  • Custom Service Icons: Upload custom icons for services via Supabase Storage (see Implementation Plan)
  • Integration with Proxmox API for VM/container management
  • Alert notifications for service failures
  • Resource usage graphs and monitoring
  • Mobile-responsive design improvements

API Endpoints

All API endpoints are available through Supabase PostgREST:

Base URL (Production)

http://10.89.97.214:8000/rest/v1/

Available Endpoints

# Get all pages
GET /rest/v1/pages?select=*

# Get user widgets
GET /rest/v1/widgets?select=*&user_id=eq.{uuid}

# Get monitored services
GET /rest/v1/services?select=*&active=eq.true

# Get blog posts
GET /rest/v1/blog_posts?select=*&published=eq.true

All requests require authentication via Authorization: Bearer <token> header.


Configuration

Environment Variables

Required: - NEXT_PUBLIC_SUPABASE_URL - Supabase API URL - NEXT_PUBLIC_SUPABASE_ANON_KEY - Supabase anonymous key - SUPABASE_SERVICE_ROLE_KEY - Supabase service role key (server-side only)

Optional: - JELLYFIN_URL - Jellyfin server URL for integration - PLEX_URL - Plex server URL for integration - PROXMOX_URL - Proxmox API URL for VM management - SERVICE_POLL_INTERVAL - Health check interval in seconds (default: 30)


Troubleshooting

Common Issues

Cannot connect to Supabase:

# Check Supabase services are running
kubectl get pods -n supabase

# Test API connectivity
curl http://10.89.97.214:8000/rest/v1/

Database migrations not applying:

# Verify schema exists
kubectl exec -n supabase postgres-0 -- psql -U postgres -c "\dn"

# Check if tables exist in home_portal schema
kubectl exec -n supabase postgres-0 -- psql -U postgres -c "\dt home_portal.*"

Dev server won't start:

# Check if port 3000 is in use
lsof -i :3000

# Check tmux session
tmux ls
tmux attach -t home-portal

# View logs
journalctl -u home-portal -f  # If running as systemd service

Icons Not Loading in Production

Symptoms: Custom service icons (uploaded via Supabase Storage) don't load when accessing via portal.bogocat.com, but CDN icons (Jellyfin, Plex, etc.) work fine.

Cause: Browser clients can't access internal Supabase URL (http://10.89.97.214:8000).

Solution: Configure public storage endpoint in .env.production:

NEXT_PUBLIC_SUPABASE_STORAGE_URL=https://storage.bogocat.com

Infrastructure: - VPS Caddy: storage.bogocat.com → K8s Ingress - K8s Ingress: storage.bogocat.com → Kong/Supabase Storage

Rebuild required after changing .env.production:

/root/tower-fleet/scripts/deploy-home-portal.sh auto -y

Documentation: See Supabase Storage - Production External Access

Duplicate Dashboard Routes (/ and /slug)

Symptoms: Same dashboard appears at both / and /default (or similar slug).

Cause: Database has a layout with is_default = true AND a slug that matches a URL path (e.g., slug = "default").

How routing works: - / - Shows layout where is_default = true - /[layoutSlug] - Shows layout matching the slug

If the default layout has slug = "default", both routes show the same content.

Solution: Rename the default layout's slug to something non-conflicting:

# Check current layouts
kubectl exec -n supabase postgres-0 -- psql -U postgres -d postgres -c \
  "SELECT id, name, slug, is_default FROM home_portal.layouts ORDER BY is_default DESC;"

# Rename default layout slug to "home"
kubectl exec -n supabase postgres-0 -- psql -U postgres -d postgres -c \
  "UPDATE home_portal.layouts SET slug = 'home' WHERE is_default = true;"

# Ensure only ONE layout is default
kubectl exec -n supabase postgres-0 -- psql -U postgres -d postgres -c \
  "UPDATE home_portal.layouts SET is_default = false WHERE id != '<your-default-layout-id>';"

Best Practice: Use unique, descriptive slugs that don't conflict with common words: - Good: home, work, media-center - Avoid: default, main, dashboard


Support