restructure documentation
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,527 +0,0 @@
|
||||
# Mesh Network Access Strategy (Option B)
|
||||
|
||||
> Hybrid approach: Public access for media/files, VPN-only for admin tools
|
||||
> All VPN access uses Headscale mesh IPs (10.99.0.x)
|
||||
> Created: 2025-11-11
|
||||
|
||||
## Core Principles
|
||||
|
||||
**RULE: All external/public access MUST route through NPM proxy**
|
||||
|
||||
**Why this rule is mandatory:**
|
||||
- ✅ **Let's Encrypt SSL**: Automatic certificate management in one place
|
||||
- ✅ **Unified logging**: All external access logged in NPM
|
||||
- ✅ **Security headers**: Consistent security policy (HSTS, CSP, etc.)
|
||||
- ✅ **Access control**: Single point to manage public access
|
||||
- ✅ **DDoS protection**: Can add Cloudflare/rate limiting at proxy level
|
||||
- ✅ **No port sprawl**: Only ports 80/443 exposed externally
|
||||
|
||||
**Access Patterns:**
|
||||
- **Internal/VPN access**: Direct mesh IPs → `http://10.99.0.1:8096`
|
||||
- **External/Public access**: Through NPM → `https://media.schweitz.net` → NPM forwards to mesh IP
|
||||
- **NEVER**: Direct port forwarding to services (except NPM and Headscale)
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Internet Users │
|
||||
└──────────────────┬──────────────────┬───────────────────┘
|
||||
│ │
|
||||
┌──────────▼────────┐ ┌─────▼──────────────────┐
|
||||
│ Public Access │ │ Headscale VPN │
|
||||
│ (Port 443) │ │ (Port 8085) │
|
||||
└──────────┬────────┘ └─────┬──────────────────┘
|
||||
│ │
|
||||
│ ┌──────▼──────────────────┐
|
||||
│ │ VPN Mesh Network │
|
||||
│ │ 10.99.0.0/16 │
|
||||
│ │ │
|
||||
│ │ tower-of-joy: 10.99.0.1│
|
||||
│ │ laptop: 10.99.0.2 │
|
||||
│ │ phone: 10.99.0.3 │
|
||||
│ └──────┬──────────────────┘
|
||||
│ │
|
||||
┌─────────▼──────────────────▼─────────────────┐
|
||||
│ tower-of-joy Services │
|
||||
│ ┌────────────────────────────────────────┐ │
|
||||
│ │ Public Services (via NPM) │ │
|
||||
│ │ - Jellyfin (media) │ │
|
||||
│ │ - Nextcloud (files) │ │
|
||||
│ │ - Organizr (optional) │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
│ ┌────────────────────────────────────────┐ │
|
||||
│ │ VPN-Only Services (mesh IPs) │ │
|
||||
│ │ - Portainer: 10.99.0.1:8001 │ │
|
||||
│ │ - Netdata: 10.99.0.1:19999 │ │
|
||||
│ │ - Uptime Kuma: 10.99.0.1:3001 │ │
|
||||
│ │ - NPM Admin: 10.99.0.1:81 │ │
|
||||
│ │ - Heimdall: 10.99.0.1:8888 │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Service Access Matrix
|
||||
|
||||
| Service | Mesh IP Access | Public Access | Use Case |
|
||||
|---------|---------------|---------------|----------|
|
||||
| **Organizr** | ✅ http://10.99.0.1:9999 | ✅ https://home.schweitz.net | Unified dashboard |
|
||||
| **Portainer** | ✅ http://10.99.0.1:8001 | ❌ VPN ONLY | Container management |
|
||||
| **Netdata** | ✅ http://10.99.0.1:19999 | ❌ VPN ONLY | System metrics |
|
||||
| **Uptime Kuma** | ✅ http://10.99.0.1:3001 | ❌ VPN ONLY | Service monitoring |
|
||||
| **Heimdall** | ✅ http://10.99.0.1:8888 | ❌ VPN ONLY | Alternative dashboard |
|
||||
| **NPM Admin** | ✅ http://10.99.0.1:81 | ❌ NEVER | Proxy config |
|
||||
| **Headscale** | ✅ http://10.99.0.1:8085 | ✅ Public :8085 | VPN control plane |
|
||||
| **Jellyfin** | ✅ http://10.99.0.1:8096 | ✅ https://media.schweitz.net | Media streaming |
|
||||
| **Nextcloud** | ✅ http://10.99.0.1:8082 | ✅ https://cloud.schweitz.net | File storage |
|
||||
| **Ollama** | ✅ http://10.99.0.1:11434 | ❌ VPN ONLY | ML API |
|
||||
|
||||
**Note:** Mesh IP `10.99.0.1` is assumed for tower-of-joy. Actual IP will be assigned by Headscale.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Connect tower-of-joy to Headscale
|
||||
|
||||
**First, get the server onto its own VPN mesh:**
|
||||
|
||||
```bash
|
||||
# Install Tailscale client on tower-of-joy
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
|
||||
# Connect to your Headscale server
|
||||
sudo tailscale up --login-server=http://192.168.86.149:8085 \
|
||||
--authkey=<your-preauth-key> \
|
||||
--hostname=tower-of-joy
|
||||
|
||||
# Verify connection
|
||||
tailscale status
|
||||
# Should show: tower-of-joy with mesh IP (e.g., 10.99.0.1)
|
||||
|
||||
# Get the mesh IP assigned to tower-of-joy
|
||||
tailscale ip -4
|
||||
# Note this IP - you'll use it in Organizr configuration
|
||||
```
|
||||
|
||||
**Verify from Headscale:**
|
||||
```bash
|
||||
# List all nodes in mesh
|
||||
docker exec headscale headscale nodes list
|
||||
|
||||
# Should show:
|
||||
# ID | Name | IP | Last Seen
|
||||
# 1 | tower-of-joy | 10.99.0.1 | now
|
||||
```
|
||||
|
||||
### Phase 2: Deploy Organizr
|
||||
|
||||
```bash
|
||||
# Create config directory
|
||||
mkdir -p ~/docker-data/organizr
|
||||
|
||||
# Deploy Organizr
|
||||
docker compose -f stacks/organizr.yml up -d
|
||||
|
||||
# Verify running
|
||||
docker ps | grep organizr
|
||||
```
|
||||
|
||||
### Phase 3: Configure Organizr with Mesh IPs
|
||||
|
||||
**Access Organizr setup:**
|
||||
- From local network: http://192.168.86.149:9999
|
||||
- From VPN: http://10.99.0.1:9999
|
||||
|
||||
**Complete setup wizard:**
|
||||
1. Choose installation type: "Personal"
|
||||
2. Create admin user
|
||||
3. Set timezone: Europe/Amsterdam
|
||||
4. Complete setup
|
||||
|
||||
**Add tabs using mesh IPs:**
|
||||
|
||||
Navigate to: Settings → Tab Editor
|
||||
|
||||
#### Tab: Portainer
|
||||
```
|
||||
Tab Name: Portainer
|
||||
Tab URL: http://10.99.0.1:8001
|
||||
Tab Type: iframe
|
||||
Category: Admin
|
||||
Icon: docker
|
||||
Enabled: Yes
|
||||
Active: Yes
|
||||
```
|
||||
|
||||
#### Tab: Netdata
|
||||
```
|
||||
Tab Name: Netdata
|
||||
Tab URL: http://10.99.0.1:19999
|
||||
Tab Type: iframe
|
||||
Category: Monitoring
|
||||
Icon: line-chart
|
||||
Enabled: Yes
|
||||
```
|
||||
|
||||
#### Tab: Uptime Kuma
|
||||
```
|
||||
Tab Name: Uptime
|
||||
Tab URL: http://10.99.0.1:3001
|
||||
Tab Type: iframe
|
||||
Category: Monitoring
|
||||
Icon: heartbeat
|
||||
Enabled: Yes
|
||||
```
|
||||
|
||||
#### Tab: Heimdall
|
||||
```
|
||||
Tab Name: Dashboard
|
||||
Tab URL: http://10.99.0.1:8888
|
||||
Tab Type: iframe
|
||||
Category: Home
|
||||
Icon: th
|
||||
Enabled: Yes
|
||||
```
|
||||
|
||||
#### Tab: Jellyfin (when deployed)
|
||||
```
|
||||
Tab Name: Media
|
||||
Tab URL: http://10.99.0.1:8096
|
||||
Tab Type: iframe
|
||||
Category: Apps
|
||||
Icon: film
|
||||
Enabled: Yes
|
||||
```
|
||||
|
||||
#### Tab: Nextcloud (when deployed)
|
||||
```
|
||||
Tab Name: Cloud
|
||||
Tab URL: http://10.99.0.1:8082
|
||||
Tab Type: iframe
|
||||
Category: Apps
|
||||
Icon: cloud
|
||||
Enabled: Yes
|
||||
```
|
||||
|
||||
### Phase 4: Configure NPM for Public Access
|
||||
|
||||
**Only expose these services publicly:**
|
||||
|
||||
Access NPM admin: http://10.99.0.1:81 (via VPN)
|
||||
|
||||
#### 1. Organizr (Public Dashboard)
|
||||
```
|
||||
Proxy Host Configuration:
|
||||
Domain Names: home.schweitz.net
|
||||
Scheme: http
|
||||
Forward Hostname/IP: 10.99.0.1
|
||||
Forward Port: 9999
|
||||
✓ Block Common Exploits
|
||||
✓ Websockets Support
|
||||
|
||||
SSL Tab:
|
||||
✓ Force SSL
|
||||
✓ HTTP/2 Support
|
||||
✓ HSTS Enabled
|
||||
Request New SSL Certificate (Let's Encrypt)
|
||||
```
|
||||
|
||||
#### 2. Jellyfin (Public Media)
|
||||
```
|
||||
Proxy Host Configuration:
|
||||
Domain Names: media.schweitz.net
|
||||
Scheme: http
|
||||
Forward Hostname/IP: 10.99.0.1
|
||||
Forward Port: 8096
|
||||
✓ Block Common Exploits
|
||||
✓ Websockets Support
|
||||
|
||||
SSL Tab:
|
||||
✓ Force SSL
|
||||
✓ HTTP/2 Support
|
||||
Request New SSL Certificate (Let's Encrypt)
|
||||
```
|
||||
|
||||
#### 3. Nextcloud (Public Files)
|
||||
```
|
||||
Proxy Host Configuration:
|
||||
Domain Names: cloud.schweitz.net
|
||||
Scheme: http
|
||||
Forward Hostname/IP: 10.99.0.1
|
||||
Forward Port: 8082
|
||||
✓ Block Common Exploits
|
||||
✓ Websockets Support
|
||||
|
||||
SSL Tab:
|
||||
✓ Force SSL
|
||||
✓ HTTP/2 Support
|
||||
Request New SSL Certificate (Let's Encrypt)
|
||||
|
||||
Custom Nginx Configuration:
|
||||
client_max_body_size 10G; # Allow large file uploads
|
||||
proxy_request_buffering off;
|
||||
```
|
||||
|
||||
### Phase 5: DNS Configuration
|
||||
|
||||
**Required DNS records:**
|
||||
```
|
||||
home.schweitz.net A <your-public-ip>
|
||||
media.schweitz.net A <your-public-ip>
|
||||
cloud.schweitz.net A <your-public-ip>
|
||||
```
|
||||
|
||||
**Or use wildcard:**
|
||||
```
|
||||
*.schweitz.net A <your-public-ip>
|
||||
```
|
||||
|
||||
### Phase 6: Router Port Forwarding
|
||||
|
||||
**CRITICAL: ONLY these ports exposed to internet:**
|
||||
```
|
||||
External Port 443 → 192.168.86.149:443 (NPM HTTPS - ALL public services)
|
||||
External Port 80 → 192.168.86.149:80 (NPM HTTP redirect to HTTPS)
|
||||
External Port 8085 → 192.168.86.149:8085 (Headscale VPN control plane)
|
||||
```
|
||||
|
||||
**⚠️ NEVER forward service ports directly!**
|
||||
- ❌ DO NOT forward port 8096 (Jellyfin)
|
||||
- ❌ DO NOT forward port 8082 (Nextcloud)
|
||||
- ❌ DO NOT forward port 9999 (Organizr)
|
||||
- ❌ DO NOT forward ANY service port except NPM and Headscale
|
||||
|
||||
**Why?**
|
||||
- All public services MUST go through NPM for SSL and logging
|
||||
- Direct port forwards bypass centralized security and logging
|
||||
- NPM provides unified Let's Encrypt management
|
||||
- NPM logs all external access for audit trails
|
||||
|
||||
## Access Patterns
|
||||
|
||||
### Scenario 1: Working from Home (Local Network)
|
||||
|
||||
**Can access via:**
|
||||
- Local IPs: http://192.168.86.149:9999
|
||||
- Mesh IPs: http://10.99.0.1:9999 (if VPN connected)
|
||||
- Public domains: https://home.schweitz.net
|
||||
|
||||
**Best practice:** Use mesh IPs consistently for uniform experience
|
||||
|
||||
### Scenario 2: Remote Work (Connected to Headscale VPN)
|
||||
|
||||
**From laptop/phone on VPN:**
|
||||
```bash
|
||||
# Verify VPN connection
|
||||
tailscale status
|
||||
|
||||
# Access Organizr
|
||||
http://10.99.0.1:9999
|
||||
|
||||
# All tabs work with mesh IPs:
|
||||
- Portainer: http://10.99.0.1:8001
|
||||
- Netdata: http://10.99.0.1:19999
|
||||
- Uptime Kuma: http://10.99.0.1:3001
|
||||
```
|
||||
|
||||
**Accessing public services:**
|
||||
- Can still use: https://media.schweitz.net (Jellyfin)
|
||||
- Or direct mesh: http://10.99.0.1:8096
|
||||
- Choose whichever is more convenient
|
||||
|
||||
### Scenario 3: Sharing with Family/Friends (No VPN)
|
||||
|
||||
**Public access only:**
|
||||
- Jellyfin: https://media.schweitz.net
|
||||
- Nextcloud: https://cloud.schweitz.net
|
||||
- Organizr: https://home.schweitz.net (if you want public dashboard)
|
||||
|
||||
**Cannot access:**
|
||||
- Admin tools (Portainer, Netdata, NPM) - VPN required
|
||||
- They need Headscale VPN for admin access
|
||||
|
||||
## Security Configuration
|
||||
|
||||
### Organizr Authentication
|
||||
|
||||
**Enable auth for public access:**
|
||||
|
||||
Settings → User Management
|
||||
- Create user accounts for family/friends
|
||||
- Configure access levels:
|
||||
- Admin: Full access to all tabs
|
||||
- User: Only media/cloud tabs visible
|
||||
- Guest: Read-only access
|
||||
|
||||
**Restrict admin tabs to admin users only:**
|
||||
- Tab Editor → each admin tab → "Minimum Authentication" → Admin
|
||||
|
||||
### NPM Access Lists (Optional)
|
||||
|
||||
**For extra security on public services:**
|
||||
|
||||
Access Lists → Create "VPN Only"
|
||||
```
|
||||
Name: Headscale VPN Only
|
||||
Allow: 10.99.0.0/16
|
||||
Deny: all
|
||||
```
|
||||
|
||||
Apply to sensitive proxy hosts if needed.
|
||||
|
||||
### Service-Level Authentication
|
||||
|
||||
**Each service maintains its own auth:**
|
||||
- Portainer: Admin password
|
||||
- Jellyfin: User accounts
|
||||
- Nextcloud: User accounts
|
||||
- Uptime Kuma: Admin password
|
||||
|
||||
**This is defense in depth:**
|
||||
1. VPN layer (for admin tools)
|
||||
2. Organizr layer (for organizing access)
|
||||
3. Service layer (individual logins)
|
||||
|
||||
## Connecting Other Devices
|
||||
|
||||
### Laptop/Desktop
|
||||
|
||||
```bash
|
||||
# Install Tailscale
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
|
||||
# Connect to Headscale
|
||||
sudo tailscale up --login-server=http://192.168.86.149:8085 \
|
||||
--authkey=<your-preauth-key> \
|
||||
--hostname=my-laptop
|
||||
|
||||
# Verify mesh access
|
||||
curl http://10.99.0.1:9999
|
||||
# Should load Organizr
|
||||
```
|
||||
|
||||
### Phone (Android/iOS)
|
||||
|
||||
1. Install Tailscale app from store
|
||||
2. In app settings:
|
||||
- Use custom control server
|
||||
- Server URL: http://<your-public-ip>:8085
|
||||
- OR: http://192.168.86.149:8085 (if on local network)
|
||||
3. Authenticate with pre-auth key
|
||||
4. Open browser: http://10.99.0.1:9999
|
||||
|
||||
### Work Computer (Can't Install Software)
|
||||
|
||||
**Use public access only:**
|
||||
- https://home.schweitz.net (Organizr - only non-admin tabs)
|
||||
- https://media.schweitz.net (Jellyfin)
|
||||
- https://cloud.schweitz.net (Nextcloud)
|
||||
|
||||
**Cannot access admin tools without VPN**
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Phase 1: Local Access
|
||||
- [ ] tower-of-joy connected to Headscale
|
||||
- [ ] Mesh IP assigned (10.99.0.x)
|
||||
- [ ] Can access services via mesh IP from tower-of-joy itself
|
||||
|
||||
### Phase 2: VPN Access from Another Device
|
||||
- [ ] Connect laptop/phone to Headscale
|
||||
- [ ] Verify mesh connectivity: `ping 10.99.0.1`
|
||||
- [ ] Access Organizr: http://10.99.0.1:9999
|
||||
- [ ] All tabs load correctly with mesh IPs
|
||||
- [ ] Portainer accessible via mesh
|
||||
- [ ] Netdata accessible via mesh
|
||||
|
||||
### Phase 3: Public Access
|
||||
- [ ] DNS configured correctly
|
||||
- [ ] NPM proxy hosts configured
|
||||
- [ ] SSL certificates generated (green padlock)
|
||||
- [ ] Access from public network (phone on mobile data):
|
||||
- [ ] https://home.schweitz.net loads Organizr
|
||||
- [ ] https://media.schweitz.net loads Jellyfin
|
||||
- [ ] https://cloud.schweitz.net loads Nextcloud
|
||||
- [ ] Admin tabs NOT accessible without VPN
|
||||
|
||||
### Phase 4: Security Validation
|
||||
- [ ] Admin tools (Portainer, Netdata) not accessible from public internet
|
||||
- [ ] Only exposed ports: 80, 443, 8085
|
||||
- [ ] Organizr authentication working
|
||||
- [ ] Service-level authentication working
|
||||
|
||||
## Advantages of This Architecture
|
||||
|
||||
### Mesh IP Benefits
|
||||
✅ **Location independent:** Same IPs whether at home or remote
|
||||
✅ **Secure by default:** Admin tools only via VPN
|
||||
✅ **Simple routing:** No complex proxy rewrites
|
||||
✅ **Flexible access:** Public and private services coexist
|
||||
✅ **Future-proof:** Add devices easily, IPs don't change
|
||||
✅ **No split-brain:** One set of URLs to remember
|
||||
|
||||
### NPM Proxy Benefits (For Public Access)
|
||||
✅ **Centralized SSL:** All Let's Encrypt certs in one place
|
||||
✅ **Unified logging:** All external access logged in NPM audit log
|
||||
✅ **Security headers:** Consistent HSTS, CSP, X-Frame-Options
|
||||
✅ **Access control:** Add rate limiting, IP blocking at proxy level
|
||||
✅ **DDoS protection:** Can add Cloudflare in front of NPM
|
||||
✅ **Port efficiency:** Only 2 ports exposed (80, 443)
|
||||
|
||||
### Compliance & Auditing
|
||||
✅ **Audit trail:** NPM logs all external access attempts
|
||||
✅ **SSL compliance:** Automatic certificate renewal
|
||||
✅ **Security posture:** Single point to review/harden public access
|
||||
✅ **Change management:** Proxy config changes tracked in one place
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Can't connect to mesh IPs
|
||||
**Check:**
|
||||
```bash
|
||||
# Verify Tailscale running
|
||||
sudo systemctl status tailscaled
|
||||
|
||||
# Check mesh status
|
||||
tailscale status
|
||||
|
||||
# Test connectivity
|
||||
ping 10.99.0.1
|
||||
```
|
||||
|
||||
### Organizr tabs not loading
|
||||
**Issue:** Service blocking iframe embedding
|
||||
**Solution:**
|
||||
- Check browser console for errors
|
||||
- Some services need `X-Frame-Options` configured
|
||||
- Use "pseudo tab" mode (opens in new tab instead)
|
||||
|
||||
### Public access not working
|
||||
**Check:**
|
||||
1. DNS resolves to your public IP: `nslookup home.schweitz.net`
|
||||
2. Router port forwarding configured
|
||||
3. NPM proxy host using correct mesh IP (10.99.0.1)
|
||||
4. SSL certificate valid
|
||||
|
||||
### Headscale connection fails
|
||||
**Check:**
|
||||
- Port 8085 accessible from internet
|
||||
- Pre-auth key still valid
|
||||
- Headscale service running: `docker logs headscale`
|
||||
|
||||
## Next Actions
|
||||
|
||||
1. **Connect tower-of-joy to Headscale** (get mesh IP)
|
||||
2. **Deploy Organizr** (`make deploy-organizr`)
|
||||
3. **Configure Organizr tabs** (using mesh IPs)
|
||||
4. **Configure NPM** (public services only)
|
||||
5. **Test VPN access** (from another device)
|
||||
6. **Test public access** (from mobile data)
|
||||
|
||||
---
|
||||
|
||||
**This gives you the best of both worlds:**
|
||||
- Secure admin access via VPN + mesh IPs
|
||||
- Public access for media/files (family/friends)
|
||||
- Single Organizr dashboard for everything
|
||||
- No complex proxy rewrites
|
||||
- Easy to add new devices
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,441 +0,0 @@
|
||||
# AI Orchestrator Phase 1 - Test Results
|
||||
|
||||
**Date:** 2025-11-13
|
||||
**Service:** Core API v1.0.0-phase1
|
||||
**Endpoint:** http://localhost:8083
|
||||
**Status:** ✅ ALL TESTS PASSING - ZERO ISSUES
|
||||
|
||||
## Test Summary
|
||||
|
||||
| Test | Status | Result |
|
||||
|------|--------|--------|
|
||||
| Health Check | ✅ PASS | Service healthy, Ollama connected |
|
||||
| Models List | ✅ PASS | Returns 11 models (4 aliases + 7 local) |
|
||||
| Non-Streaming Chat | ✅ PASS | Correct response format, token usage |
|
||||
| Streaming Chat | ✅ PASS | SSE format, proper chunking |
|
||||
| Model Aliasing | ✅ PASS | All aliases working correctly |
|
||||
| Error Handling | ✅ PASS | Proper validation errors |
|
||||
| Multi-turn Conversation | ✅ PASS | Handles conversation history |
|
||||
| Token Usage | ✅ PASS | Accurate token counting |
|
||||
| Performance | ✅ PASS | 227-284ms average response time |
|
||||
| Model ID Formatting | ✅ PASS | Clean IDs (issue fixed) |
|
||||
|
||||
**Overall Score: 10/10 Tests Passed (100%)**
|
||||
|
||||
---
|
||||
|
||||
## Detailed Test Results
|
||||
|
||||
### Test 1: Health Check ✅
|
||||
**Endpoint:** `GET /health`
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"ollama_connected": true
|
||||
}
|
||||
```
|
||||
|
||||
**Result:** ✅ Service operational, Ollama connectivity confirmed
|
||||
|
||||
---
|
||||
|
||||
### Test 2: Models List ✅
|
||||
**Endpoint:** `GET /v1/models`
|
||||
|
||||
**Models Returned (all with clean IDs):**
|
||||
```json
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{"id": "gpt-3.5-turbo", "object": "model", "owned_by": "local"},
|
||||
{"id": "gpt-4", "object": "model", "owned_by": "local"},
|
||||
{"id": "gpt-4-turbo", "object": "model", "owned_by": "local"},
|
||||
{"id": "gpt-4-code", "object": "model", "owned_by": "local"},
|
||||
{"id": "gemma:2b", "object": "model", "owned_by": "local"},
|
||||
{"id": "gemma:7b", "object": "model", "owned_by": "local"},
|
||||
{"id": "mistral:7b", "object": "model", "owned_by": "local"},
|
||||
{"id": "gemma2:9b", "object": "model", "owned_by": "local"},
|
||||
{"id": "mixtral:8x7b", "object": "model", "owned_by": "local"},
|
||||
{"id": "codestral:latest", "object": "model", "owned_by": "local"},
|
||||
{"id": "codegemma:latest", "object": "model", "owned_by": "local"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Result:** ✅ All 11 models present with properly formatted IDs
|
||||
- ✅ 4 OpenAI aliases (gpt-3.5-turbo, gpt-4, gpt-4-turbo, gpt-4-code)
|
||||
- ✅ 2 lightweight models (gemma:2b, gemma:7b)
|
||||
- ✅ 3 heavy models (mistral:7b, gemma2:9b, mixtral:8x7b)
|
||||
- ✅ 2 code models (codestral:latest, codegemma:latest)
|
||||
- ✅ No extra quotes or formatting issues
|
||||
|
||||
---
|
||||
|
||||
### Test 3: Non-Streaming Chat Completion ✅
|
||||
**Endpoint:** `POST /v1/chat/completions`
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are a helpful assistant. Respond in exactly 10 words."},
|
||||
{"role": "user", "content": "What is the capital of France?"}
|
||||
],
|
||||
"stream": false,
|
||||
"temperature": 0.5,
|
||||
"max_tokens": 30
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": "chatcmpl-1763064184644",
|
||||
"object": "chat.completion",
|
||||
"created": 1763064199,
|
||||
"model": "gpt-3.5-turbo",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "The capital of France is Paris."
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {
|
||||
"prompt_tokens": 51,
|
||||
"completion_tokens": 8,
|
||||
"total_tokens": 59
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Result:** ✅ Perfect OpenAI-compatible response format
|
||||
- ✅ All required fields present
|
||||
- ✅ Token usage tracking working
|
||||
- ✅ Correct finish_reason
|
||||
- ✅ Model name preserved in response
|
||||
|
||||
---
|
||||
|
||||
### Test 4: Streaming Chat Completion ✅
|
||||
**Endpoint:** `POST /v1/chat/completions` (stream=true)
|
||||
**Request:** "Count from 1 to 5"
|
||||
|
||||
**Response Format (SSE):**
|
||||
```
|
||||
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":...,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"role":"assistant","content":null},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":...,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"content":"1"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":...,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}]}
|
||||
|
||||
... [continues with 2, 3, 4, 5]
|
||||
|
||||
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":...,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
|
||||
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
**Result:** ✅ Proper SSE format
|
||||
- ✅ First chunk includes role
|
||||
- ✅ Content chunks stream correctly
|
||||
- ✅ Final chunk with finish_reason
|
||||
- ✅ [DONE] marker sent
|
||||
- ✅ Compatible with OpenAI clients
|
||||
|
||||
---
|
||||
|
||||
### Test 5: Model Aliasing ✅
|
||||
**Test Cases:**
|
||||
|
||||
**5a: gpt-3.5-turbo → gemma:7b**
|
||||
- Request model: `gpt-3.5-turbo`
|
||||
- Log: `Model resolution: gpt-3.5-turbo → gemma:7b`
|
||||
- Response model field: `gpt-3.5-turbo` (preserves alias)
|
||||
- ✅ Working correctly
|
||||
|
||||
**5b: gpt-4 → mistral:7b**
|
||||
- Request model: `gpt-4`
|
||||
- Log: `Model resolution: gpt-4 → mistral:7b`
|
||||
- Response model field: `gpt-4`
|
||||
- ✅ Working correctly
|
||||
|
||||
**5c: Direct model (gemma:7b)**
|
||||
- Request model: `gemma:7b`
|
||||
- No resolution needed
|
||||
- Response model field: `gemma:7b`
|
||||
- ✅ Working correctly
|
||||
|
||||
**Result:** ✅ All alias mappings functional
|
||||
- Model resolution logged correctly
|
||||
- Response preserves requested model name
|
||||
- Direct model names work without aliasing
|
||||
|
||||
---
|
||||
|
||||
### Test 6: Error Handling ✅
|
||||
**Test Cases:**
|
||||
|
||||
**6a: Missing required field**
|
||||
```json
|
||||
{"model": "gpt-3.5-turbo", "stream": false}
|
||||
```
|
||||
Response: HTTP 422, `"msg": "Field required", "loc": ["body", "messages"]`
|
||||
✅ Proper validation error
|
||||
|
||||
**6b: Empty messages array**
|
||||
```json
|
||||
{"model": "gpt-3.5-turbo", "messages": [], "stream": false}
|
||||
```
|
||||
Response: HTTP 422, `"msg": "List should have at least 1 item after validation"`
|
||||
✅ Array length validation working
|
||||
|
||||
**6c: Invalid temperature (5.0, max is 2.0)**
|
||||
Response: HTTP 422, `"msg": "Input should be less than or equal to 2"`
|
||||
✅ Range validation working
|
||||
|
||||
**6d: Invalid JSON**
|
||||
Response: HTTP 422, `"type": "json_invalid"`
|
||||
✅ JSON parsing errors handled
|
||||
|
||||
**Result:** ✅ All edge cases handled with proper Pydantic validation
|
||||
|
||||
---
|
||||
|
||||
### Test 7: Multi-turn Conversation ✅
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are a math tutor."},
|
||||
{"role": "user", "content": "What is 2+2?"},
|
||||
{"role": "assistant", "content": "2+2 equals 4."},
|
||||
{"role": "user", "content": "What about 3+3?"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** "3+3 equals 6. Would you like to ask anything else today?"
|
||||
|
||||
**Result:** ✅ Correctly processes conversation history
|
||||
- System message understood
|
||||
- Previous assistant response incorporated
|
||||
- Context maintained across turns
|
||||
|
||||
---
|
||||
|
||||
### Test 8: Token Usage Reporting ✅
|
||||
**Request:** Simple "Hello" message
|
||||
|
||||
**Token Usage:**
|
||||
- Prompt tokens: 28
|
||||
- Completion tokens: 19
|
||||
- Total tokens: 47
|
||||
|
||||
**Result:** ✅ Accurate token counting from Ollama
|
||||
|
||||
---
|
||||
|
||||
### Test 9: Performance Benchmark ✅
|
||||
**5 consecutive requests (simple "Hi" prompts, max_tokens=5)**
|
||||
|
||||
| Request | Response Time |
|
||||
|---------|--------------|
|
||||
| 1 | 257ms |
|
||||
| 2 | 221ms |
|
||||
| 3 | 239ms |
|
||||
| 4 | 284ms |
|
||||
| 5 | 227ms |
|
||||
|
||||
**Average: 245.6ms**
|
||||
**Min: 221ms**
|
||||
**Max: 284ms**
|
||||
|
||||
**Result:** ✅ Excellent performance
|
||||
- All requests under 300ms
|
||||
- Consistent response times
|
||||
- No degradation with concurrent requests
|
||||
|
||||
---
|
||||
|
||||
### Test 10: Model ID Formatting Fix ✅
|
||||
**Issue:** Model IDs initially had extra quotes (`"gemma:2b"`, `gemma:7b"`)
|
||||
|
||||
**Root Cause:** Parsing methods in `config.py` weren't stripping quote characters
|
||||
|
||||
**Fix Applied:**
|
||||
```python
|
||||
# Before:
|
||||
return [m.strip() for m in self.lightweight_models.split(",") if m.strip()]
|
||||
|
||||
# After:
|
||||
return [m.strip().strip('"').strip("'") for m in self.lightweight_models.split(",") if m.strip()]
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
✓ Total models: 11
|
||||
✓ gpt-3.5-turbo
|
||||
✓ gpt-4
|
||||
✓ gpt-4-turbo
|
||||
✓ gpt-4-code
|
||||
✓ gemma:2b # No quotes!
|
||||
✓ gemma:7b # No quotes!
|
||||
✓ mistral:7b # No quotes!
|
||||
✓ gemma2:9b
|
||||
✓ mixtral:8x7b # No quotes!
|
||||
✓ codestral:latest # No quotes!
|
||||
✓ codegemma:latest # No quotes!
|
||||
```
|
||||
|
||||
**Result:** ✅ Issue completely resolved
|
||||
- All model IDs properly formatted
|
||||
- No quotes or extra characters
|
||||
- Functionality unaffected
|
||||
|
||||
---
|
||||
|
||||
## Container Health
|
||||
|
||||
**Container:** core-api
|
||||
**Status:** Up and healthy
|
||||
**Ports:** 0.0.0.0:8083->8083/tcp
|
||||
**Health Check:** Passing (30s interval)
|
||||
**Uptime:** Stable (restarted once for fix)
|
||||
|
||||
**Recent Activity:**
|
||||
- Successfully processed 30+ chat requests during testing
|
||||
- Zero errors or crashes
|
||||
- Ollama connectivity stable
|
||||
- Hot-reload functioning correctly
|
||||
|
||||
---
|
||||
|
||||
## OpenAI API Compatibility
|
||||
|
||||
**Compatibility Score: 100%**
|
||||
|
||||
✅ **Request Format:**
|
||||
- All OpenAI fields supported (model, messages, temperature, max_tokens, etc.)
|
||||
- Proper Pydantic validation
|
||||
- Streaming boolean works correctly
|
||||
|
||||
✅ **Response Format:**
|
||||
- All required fields present (id, object, created, model, choices, usage)
|
||||
- Choice structure matches OpenAI exactly
|
||||
- Finish reasons correct ("stop")
|
||||
|
||||
✅ **Streaming Format:**
|
||||
- Server-Sent Events (SSE) format
|
||||
- Proper chunk structure
|
||||
- [DONE] marker
|
||||
- Compatible with OpenAI client libraries
|
||||
|
||||
✅ **Model Endpoints:**
|
||||
- /v1/models returns proper format
|
||||
- Model objects match OpenAI structure
|
||||
- Model IDs properly formatted
|
||||
|
||||
---
|
||||
|
||||
## Known Issues
|
||||
|
||||
**None - All issues resolved!** ✅
|
||||
|
||||
### Previously Fixed
|
||||
|
||||
1. **Model ID Formatting** ✅ FIXED
|
||||
- ~~Some model IDs had extra quotes~~
|
||||
- Fixed by updating config.py parsing methods
|
||||
- All model IDs now clean
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (Planned Phases)
|
||||
|
||||
**Phase 2 - Memory Systems:**
|
||||
- [ ] Tier 1: ConversationBufferMemory (in-memory)
|
||||
- [ ] Tier 2: ConversationSummaryMemory (SQLite)
|
||||
- [ ] Tier 3: VectorStoreRetrieverMemory (Qdrant)
|
||||
|
||||
**Phase 3 - Multi-Agent Workflows:**
|
||||
- [ ] Router agent
|
||||
- [ ] Chat agent
|
||||
- [ ] Research agent
|
||||
- [ ] Code agent
|
||||
|
||||
**Phase 4 - Tool Integration:**
|
||||
- [ ] Web search (DuckDuckGo)
|
||||
- [ ] Web scraping (Core API)
|
||||
- [ ] Document search (Qdrant)
|
||||
|
||||
**Phase 5 - RAG & Advanced Memory:**
|
||||
- [ ] Hybrid retrieval
|
||||
- [ ] Document upload
|
||||
- [ ] Re-ranking
|
||||
|
||||
**Phase 6 - Production Hardening:**
|
||||
- [ ] Metrics and monitoring
|
||||
- [ ] Performance optimization
|
||||
- [ ] Load testing
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
**Phase 1 Status: ✅ 100% COMPLETE - PRODUCTION READY**
|
||||
|
||||
All core functionality is working perfectly:
|
||||
- ✅ OpenAI-compatible API endpoints
|
||||
- ✅ Model aliasing system (4 aliases)
|
||||
- ✅ Streaming and non-streaming responses
|
||||
- ✅ Error handling and validation
|
||||
- ✅ Performance within targets (<300ms)
|
||||
- ✅ All formatting issues resolved
|
||||
- ✅ Zero known bugs
|
||||
|
||||
**Ready for:**
|
||||
- ✅ Open WebUI integration (endpoint: http://core-api:8083/v1)
|
||||
- ✅ OpenAI client library usage
|
||||
- ✅ Production deployment
|
||||
- ✅ Phase 2 development (Memory Systems)
|
||||
|
||||
**Phase 1 Achievements:**
|
||||
- 10/10 tests passing
|
||||
- 100% OpenAI compatibility
|
||||
- Sub-300ms response times
|
||||
- Zero regressions
|
||||
- Clean, maintainable code
|
||||
|
||||
---
|
||||
|
||||
**Test Suite Completed: 2025-11-13**
|
||||
**Final Status: All issues resolved, ready for Phase 2**
|
||||
**Next Step: Begin Phase 2 (Memory Systems) implementation**
|
||||
|
||||
---
|
||||
|
||||
## Files Modified During Phase 1
|
||||
|
||||
### New Files Created
|
||||
- `services/core-api/src/api/v1/chat.py` (207 lines)
|
||||
- `services/core-api/src/api/v1/models.py` (35 lines)
|
||||
- `services/core-api/src/api/v1/schemas.py` (133 lines)
|
||||
- `services/core-api/src/models/ollama_client.py` (202 lines)
|
||||
|
||||
### Files Modified
|
||||
- `services/core-api/src/main.py` - Added v1 routes
|
||||
- `services/core-api/src/config.py` - Added model configuration and aliases
|
||||
- `services/core-api/requirements.txt` - Dependencies up to date
|
||||
- `stacks/core-api.yml` - Environment variables for models
|
||||
|
||||
### Documentation Updated
|
||||
- `CONTAINERS.md` - Core API section updated
|
||||
- `STATUS.md` - Phase 1 completion documented
|
||||
- `docs/ai-orchestrator-plan.md` - Phase 1 marked complete
|
||||
- `docs/phase1-test-results.md` - This document
|
||||
|
||||
**Total Lines Added: ~600+ lines of production code**
|
||||
**Total Time: 1 day (2025-11-13)**
|
||||
@@ -1,414 +0,0 @@
|
||||
# Phase 2: Memory Systems Architecture
|
||||
|
||||
**Status:** In Progress
|
||||
**Started:** 2025-11-13
|
||||
**Phase Goal:** Persistent 3-tier conversation memory with automatic consolidation
|
||||
|
||||
## Overview
|
||||
|
||||
The memory system provides persistent, intelligent conversation context using a three-tier architecture:
|
||||
|
||||
1. **Tier 1 (Working Memory):** Fast in-memory buffer for recent turns
|
||||
2. **Tier 2 (Short-term):** SQLite database for summarized conversation history
|
||||
3. **Tier 3 (Long-term):** Qdrant vector store for semantic search across all conversations
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Chat Endpoint (/v1/chat/completions) │
|
||||
│ │
|
||||
│ 1. Accept user message │
|
||||
│ 2. Retrieve relevant memory from all tiers │
|
||||
│ 3. Build context: [Tier 1 + Tier 2 + Tier 3 semantic] │
|
||||
│ 4. Generate response with Ollama │
|
||||
│ 5. Store new turn in Tier 1 │
|
||||
│ 6. Trigger consolidation if needed │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Memory Manager │
|
||||
│ │
|
||||
│ - Coordinates all 3 tiers │
|
||||
│ - Handles memory retrieval │
|
||||
│ - Triggers consolidation │
|
||||
│ - Manages conversation sessions │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────────────┐ ┌─────────────────┐ ┌──────────────────┐
|
||||
│ Tier 1 │ │ Tier 2 │ │ Tier 3 │
|
||||
│ Buffer Memory │ │ SQLite Summary │ │ Qdrant Vectors │
|
||||
│ │ │ │ │ │
|
||||
│ • In-memory dict │ │ • memory.db │ │ • conversation_ │
|
||||
│ • Last 10 turns │ │ • Summaries │ │ memory │
|
||||
│ • < 1ms access │ │ • ~10ms access │ │ • Semantic │
|
||||
│ • Ephemeral │ │ • Persistent │ │ • ~50ms access │
|
||||
│ • ~5KB RAM │ │ • ~500KB/100 │ │ • ~1KB per turn │
|
||||
└──────────────────┘ └─────────────────┘ └──────────────────┘
|
||||
│ │ │
|
||||
└───────────────────┴────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ Memory Consolidation │
|
||||
│ Service │
|
||||
│ │
|
||||
│ Triggers: │
|
||||
│ • Every 10 messages │
|
||||
│ • Token limit (2000) │
|
||||
│ • Conversation end │
|
||||
│ • Explicit save command │
|
||||
│ │
|
||||
│ Actions: │
|
||||
│ • Tier 1 → Tier 2 summary │
|
||||
│ • Tier 2 → Tier 3 embed │
|
||||
│ • Prune old Tier 1 data │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Structures
|
||||
|
||||
### Tier 1: ConversationBufferMemory
|
||||
|
||||
```python
|
||||
{
|
||||
"conversation_id": "conv_123",
|
||||
"turns": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is FastAPI?",
|
||||
"timestamp": "2025-11-13T10:00:00Z",
|
||||
"turn_number": 1
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "FastAPI is a modern Python web framework...",
|
||||
"timestamp": "2025-11-13T10:00:02Z",
|
||||
"turn_number": 2,
|
||||
"tokens": {"prompt": 15, "completion": 120, "total": 135}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"created_at": "2025-11-13T10:00:00Z",
|
||||
"last_updated": "2025-11-13T10:00:02Z",
|
||||
"turn_count": 2,
|
||||
"total_tokens": 135
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Tier 2: SQLite Schema
|
||||
|
||||
```sql
|
||||
-- conversations table
|
||||
CREATE TABLE conversations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
conversation_id TEXT UNIQUE NOT NULL,
|
||||
user_id TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_message_at TIMESTAMP,
|
||||
turn_count INTEGER DEFAULT 0,
|
||||
total_tokens INTEGER DEFAULT 0,
|
||||
summary TEXT,
|
||||
status TEXT DEFAULT 'active' -- active, archived, deleted
|
||||
);
|
||||
|
||||
-- conversation_turns table
|
||||
CREATE TABLE conversation_turns (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
conversation_id TEXT NOT NULL,
|
||||
turn_number INTEGER NOT NULL,
|
||||
role TEXT NOT NULL, -- user, assistant, system
|
||||
content TEXT NOT NULL,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
tokens_prompt INTEGER,
|
||||
tokens_completion INTEGER,
|
||||
tokens_total INTEGER,
|
||||
FOREIGN KEY (conversation_id) REFERENCES conversations(conversation_id),
|
||||
UNIQUE(conversation_id, turn_number)
|
||||
);
|
||||
|
||||
-- conversation_summaries table (for Tier 2 condensed storage)
|
||||
CREATE TABLE conversation_summaries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
conversation_id TEXT NOT NULL,
|
||||
summary_text TEXT NOT NULL,
|
||||
turn_range_start INTEGER NOT NULL,
|
||||
turn_range_end INTEGER NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
token_count INTEGER,
|
||||
FOREIGN KEY (conversation_id) REFERENCES conversations(conversation_id)
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX idx_conversation_id ON conversation_turns(conversation_id);
|
||||
CREATE INDEX idx_timestamp ON conversation_turns(timestamp);
|
||||
CREATE INDEX idx_summary_conv ON conversation_summaries(conversation_id);
|
||||
```
|
||||
|
||||
### Tier 3: Qdrant Collection Schema
|
||||
|
||||
```python
|
||||
# Collection: conversation_memory
|
||||
{
|
||||
"collection_name": "conversation_memory",
|
||||
"vectors": {
|
||||
"size": 384, # all-MiniLM-L6-v2 embedding dimension
|
||||
"distance": "Cosine"
|
||||
},
|
||||
"payload_schema": {
|
||||
"conversation_id": "string",
|
||||
"turn_number": "integer",
|
||||
"role": "string",
|
||||
"content": "text",
|
||||
"timestamp": "datetime",
|
||||
"tokens": "integer",
|
||||
"summary": "text", # Optional condensed version
|
||||
"tags": ["string"] # e.g., ["question", "code", "technical"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Memory Retrieval Flow
|
||||
|
||||
### Query: "What did we discuss about FastAPI?"
|
||||
|
||||
```python
|
||||
# 1. Tier 1: Check recent buffer (last 10 turns)
|
||||
tier1_results = buffer_memory.get_recent_turns(limit=10)
|
||||
# Returns last 10 turns if they exist
|
||||
|
||||
# 2. Tier 2: Check SQLite summaries
|
||||
tier2_results = sqlite_memory.search_summaries(
|
||||
conversation_id="conv_123",
|
||||
query="FastAPI discussion"
|
||||
)
|
||||
# Returns summaries containing "FastAPI"
|
||||
|
||||
# 3. Tier 3: Semantic search in Qdrant
|
||||
tier3_results = qdrant_memory.similarity_search(
|
||||
query="FastAPI discussion",
|
||||
limit=5,
|
||||
filter={"conversation_id": "conv_123"}
|
||||
)
|
||||
# Returns 5 most semantically similar turns
|
||||
|
||||
# 4. Merge and deduplicate
|
||||
context = merge_memory_results(tier1_results, tier2_results, tier3_results)
|
||||
|
||||
# 5. Build prompt with context
|
||||
prompt = build_prompt_with_memory(
|
||||
system_message="You are a helpful assistant",
|
||||
memory_context=context,
|
||||
user_message="What did we discuss about FastAPI?"
|
||||
)
|
||||
```
|
||||
|
||||
## Memory Consolidation Logic
|
||||
|
||||
### Trigger Conditions
|
||||
|
||||
```python
|
||||
class ConsolidationTrigger:
|
||||
MESSAGE_COUNT = 10 # Every 10 messages
|
||||
TOKEN_LIMIT = 2000 # When context > 2000 tokens
|
||||
CONVERSATION_END = True # End of conversation
|
||||
EXPLICIT_SAVE = True # User command: "remember this"
|
||||
TIME_ELAPSED = 3600 # 1 hour idle
|
||||
```
|
||||
|
||||
### Consolidation Process
|
||||
|
||||
```python
|
||||
async def consolidate_memory(conversation_id: str):
|
||||
"""
|
||||
Consolidate memory from Tier 1 → Tier 2 → Tier 3
|
||||
"""
|
||||
# 1. Get Tier 1 buffer
|
||||
buffer = tier1_memory.get_buffer(conversation_id)
|
||||
|
||||
if len(buffer.turns) >= 10:
|
||||
# 2. Summarize buffer using lightweight model
|
||||
summary = await summarize_conversation(
|
||||
turns=buffer.turns,
|
||||
model="gemma:7b"
|
||||
)
|
||||
|
||||
# 3. Store summary in Tier 2 (SQLite)
|
||||
tier2_memory.add_summary(
|
||||
conversation_id=conversation_id,
|
||||
summary=summary,
|
||||
turn_range=(buffer.turns[0].turn_number, buffer.turns[-1].turn_number)
|
||||
)
|
||||
|
||||
# 4. Embed individual turns to Tier 3 (Qdrant)
|
||||
for turn in buffer.turns:
|
||||
embedding = await embed_text(turn.content)
|
||||
tier3_memory.add_turn(
|
||||
conversation_id=conversation_id,
|
||||
turn=turn,
|
||||
embedding=embedding
|
||||
)
|
||||
|
||||
# 5. Prune Tier 1 buffer (keep only last 5 turns)
|
||||
tier1_memory.prune(conversation_id, keep_last=5)
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
services/core-api/src/
|
||||
├── memory/
|
||||
│ ├── __init__.py
|
||||
│ ├── base.py # Base memory classes
|
||||
│ ├── tier1_buffer.py # ConversationBufferMemory
|
||||
│ ├── tier2_sqlite.py # ConversationSummaryMemory
|
||||
│ ├── tier3_qdrant.py # VectorStoreRetrieverMemory
|
||||
│ ├── manager.py # MemoryManager (coordinates all tiers)
|
||||
│ ├── consolidation.py # Consolidation service
|
||||
│ └── schemas.py # Pydantic models
|
||||
├── api/
|
||||
│ └── v1/
|
||||
│ ├── chat.py # Updated with memory integration
|
||||
│ ├── memory.py # NEW: Memory API endpoints
|
||||
│ └── schemas.py # Updated with memory schemas
|
||||
├── models/
|
||||
│ ├── ollama_client.py # Existing
|
||||
│ └── embeddings.py # NEW: Embedding model client
|
||||
└── utils/
|
||||
└── database.py # NEW: SQLite utilities
|
||||
```
|
||||
|
||||
## API Endpoints (New)
|
||||
|
||||
### GET /v1/conversations
|
||||
List all conversations
|
||||
|
||||
### GET /v1/conversations/{conversation_id}
|
||||
Get conversation details and history
|
||||
|
||||
### GET /v1/conversations/{conversation_id}/turns
|
||||
Get all turns in a conversation
|
||||
|
||||
### POST /v1/conversations/{conversation_id}/search
|
||||
Semantic search within a conversation
|
||||
|
||||
### DELETE /v1/conversations/{conversation_id}
|
||||
Delete/archive a conversation
|
||||
|
||||
### POST /v1/conversations/{conversation_id}/consolidate
|
||||
Manually trigger memory consolidation
|
||||
|
||||
## Configuration Updates
|
||||
|
||||
```python
|
||||
# config.py additions
|
||||
class Settings(BaseSettings):
|
||||
# ... existing ...
|
||||
|
||||
# Memory Configuration
|
||||
memory_tier1_max_turns: int = 10
|
||||
memory_tier2_summary_threshold: int = 10
|
||||
memory_tier3_enabled: bool = True
|
||||
|
||||
# SQLite
|
||||
sqlite_database_path: str = "/app/data/memory.db"
|
||||
|
||||
# Qdrant
|
||||
qdrant_host: str = "qdrant"
|
||||
qdrant_port: int = 6333
|
||||
qdrant_collection_conversations: str = "conversation_memory"
|
||||
qdrant_collection_documents: str = "documents"
|
||||
qdrant_collection_user_facts: str = "user_facts"
|
||||
|
||||
# Embeddings
|
||||
embedding_model: str = "sentence-transformers/all-MiniLM-L6-v2"
|
||||
embedding_dimension: int = 384
|
||||
```
|
||||
|
||||
## Dependencies to Add
|
||||
|
||||
```txt
|
||||
# requirements.txt additions
|
||||
sqlalchemy==2.0.23 # SQLite ORM
|
||||
qdrant-client==1.7.0 # Qdrant Python client
|
||||
sentence-transformers==2.2.2 # Embedding models
|
||||
torch==2.1.0 # PyTorch (for embeddings)
|
||||
```
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 2.1: Tier 1 (Day 1)
|
||||
- ✅ Create base memory classes
|
||||
- ✅ Implement ConversationBufferMemory
|
||||
- ✅ Add basic memory schemas
|
||||
- ✅ Test in-memory storage and retrieval
|
||||
|
||||
### Phase 2.2: Tier 2 (Day 2)
|
||||
- ✅ Setup SQLite database
|
||||
- ✅ Create schema and migrations
|
||||
- ✅ Implement ConversationSummaryMemory
|
||||
- ✅ Add summarization using Ollama
|
||||
- ✅ Test persistence across restarts
|
||||
|
||||
### Phase 2.3: Tier 3 (Day 3)
|
||||
- ✅ Setup Qdrant collections
|
||||
- ✅ Implement embedding pipeline
|
||||
- ✅ Implement VectorStoreRetrieverMemory
|
||||
- ✅ Test semantic search
|
||||
- ✅ Test Qdrant connectivity
|
||||
|
||||
### Phase 2.4: Integration (Day 4)
|
||||
- ✅ Create MemoryManager
|
||||
- ✅ Implement consolidation service
|
||||
- ✅ Update /v1/chat/completions to use memory
|
||||
- ✅ Add memory API endpoints
|
||||
- ✅ Test end-to-end flow
|
||||
|
||||
### Phase 2.5: Testing & Polish (Day 5)
|
||||
- ✅ Comprehensive testing
|
||||
- ✅ Performance optimization
|
||||
- ✅ Memory leak checks
|
||||
- ✅ Documentation updates
|
||||
- ✅ Integration with Open WebUI
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- **Tier 1 Performance:** < 1ms access time
|
||||
- **Tier 2 Performance:** < 10ms query time
|
||||
- **Tier 3 Performance:** < 50ms semantic search
|
||||
- **Memory Persistence:** 100% across container restarts
|
||||
- **Context Relevance:** Semantic search returns appropriate results
|
||||
- **Memory Growth:** Bounded growth with automatic pruning
|
||||
- **Container Restart:** Conversations resume with full context
|
||||
|
||||
## Testing Plan
|
||||
|
||||
1. **Unit Tests:**
|
||||
- Each tier independently
|
||||
- Consolidation logic
|
||||
- Memory retrieval
|
||||
|
||||
2. **Integration Tests:**
|
||||
- Full memory flow
|
||||
- Container restart persistence
|
||||
- Multi-conversation handling
|
||||
|
||||
3. **Performance Tests:**
|
||||
- 100 conversations
|
||||
- 1000 turns total
|
||||
- Memory usage monitoring
|
||||
- Query performance benchmarks
|
||||
|
||||
4. **User Acceptance:**
|
||||
- Start conversation
|
||||
- Restart container
|
||||
- Resume conversation with context
|
||||
- Ask about past discussions
|
||||
- Verify relevant recall
|
||||
|
||||
---
|
||||
|
||||
**Next Step:** Implement Tier 1 (ConversationBufferMemory)
|
||||
@@ -0,0 +1,609 @@
|
||||
# Container Reference Guide
|
||||
|
||||
> Documentation of all deployed containers in the tower-of-joy infrastructure
|
||||
>
|
||||
> Last Updated: 2025-11-16
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure Layer
|
||||
|
||||
### Portainer
|
||||
|
||||
Portainer provides the web-based container management interface for the entire stack, offering visual control over Docker containers, stacks, images, volumes, and networks. It serves as the primary management tool for deploying and monitoring all other services, with GPU device management enabled for allocation to ML and transcoding workloads. The interface replaces the need for manual Docker CLI operations and provides real-time container logs, stats, and control.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `portainer/portainer-ce:latest` |
|
||||
| **Container Name** | `portainer` |
|
||||
| **Access URL** | http://192.168.86.149:8001 |
|
||||
| **External Access** | LAN only (behind firewall) |
|
||||
| **Port Mapping** | 8001:9000 (HTTP), 8443:9443 (HTTPS) |
|
||||
| **Network Mode** | Host |
|
||||
| **Restart Policy** | `always` |
|
||||
| **Volume Mounts** | `portainer_data:/data`, `/var/run/docker.sock` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None (base service) |
|
||||
|
||||
---
|
||||
|
||||
### PostgreSQL Shared
|
||||
|
||||
PostgreSQL Shared is a centralized PostgreSQL 17 database server providing isolated database instances for multiple applications across the infrastructure, including Authentik (SSO), Gitea (Git hosting), and future services requiring relational database storage. It implements a shared infrastructure pattern where each application gets its own database and user credentials while sharing the same PostgreSQL instance for resource efficiency. The service stores all database data on the HDD with automated backups scheduled to the backups directory, providing persistent storage with volume-based data retention across container updates.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `postgres:17` |
|
||||
| **Container Name** | `postgres-shared` |
|
||||
| **Access URL** | N/A (internal database server) |
|
||||
| **External Access** | No (docker-dataplane network only) |
|
||||
| **Port Mapping** | 5432:5432 (PostgreSQL) |
|
||||
| **Network Mode** | Bridge (docker-dataplane) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `/home/jpmschweitzer/docker-data/postgres-shared/data:/var/lib/postgresql/data`, `/home/jpmschweitzer/docker-data/postgres-shared/backups:/backups` |
|
||||
| **Environment** | `POSTGRES_PASSWORD=<secure-password>`, `POSTGRES_DB=postgres`, `TZ=Europe/Amsterdam`, `PGDATA=/var/lib/postgresql/data/pgdata` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | docker-dataplane network |
|
||||
| **Databases** | `authentik` (Authentik SSO), `gitea` (Git hosting), `postgres` (default/admin) |
|
||||
| **Database Users** | `authentik_user`, `gitea_user`, `postgres` (superuser) |
|
||||
| **Health Check** | `pg_isready -U postgres` (30s interval) |
|
||||
| **Backup Strategy** | `/backups` volume for pg_dump exports |
|
||||
|
||||
**Initialization**: Databases and users are created manually after first deployment:
|
||||
```bash
|
||||
docker exec -i postgres-shared psql -U postgres <<'EOF'
|
||||
CREATE DATABASE authentik;
|
||||
CREATE USER authentik_user WITH PASSWORD '<password>';
|
||||
GRANT ALL PRIVILEGES ON DATABASE authentik TO authentik_user;
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Redis Shared
|
||||
|
||||
Redis Shared is a centralized Redis 7 key-value store providing cache, session storage, and message queue capabilities for multiple applications, with logical database isolation (DB 0-15) allowing each service to maintain separate keyspaces within the same Redis instance. It implements a shared infrastructure pattern where applications like Authentik use DB 0 for sessions/cache while future services can use DB 1-15, eliminating the need for separate Redis containers per application. The service stores data on the HDD for persistence across restarts, with AOF (Append-Only File) enabled for durability and optional RDB snapshots for backup points.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `redis:7-alpine` |
|
||||
| **Container Name** | `redis-shared` |
|
||||
| **Access URL** | N/A (internal cache server) |
|
||||
| **External Access** | No (docker-dataplane network only) |
|
||||
| **Port Mapping** | 6379:6379 (Redis) |
|
||||
| **Network Mode** | Bridge (docker-dataplane) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `/home/jpmschweitzer/docker-data/redis-shared/data:/data` |
|
||||
| **Command** | `redis-server --appendonly yes --dir /data` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | docker-dataplane network |
|
||||
| **Database Allocation** | DB 0: Authentik, DB 1-15: Available for future services |
|
||||
| **Persistence** | AOF (Append-Only File) enabled for durability |
|
||||
| **Health Check** | `redis-cli ping` returns PONG (30s interval) |
|
||||
| **Connection String** | `redis://redis-shared:6379/0` (DB 0), `redis://redis-shared:6379/1` (DB 1), etc. |
|
||||
|
||||
---
|
||||
|
||||
### Nginx Proxy Manager
|
||||
|
||||
Nginx Proxy Manager serves as the unified reverse proxy and SSL certificate manager, providing a web-based interface for routing HTTP/HTTPS traffic to backend services with automatic Let's Encrypt certificate provisioning. It consolidates access to all web services through a single entry point with path-based or subdomain routing, eliminating the need to remember individual service ports. The service handles SSL termination, proxy host configuration, and access list management through an intuitive dashboard.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `jc21/nginx-proxy-manager:latest` |
|
||||
| **Container Name** | `nginx-proxy-manager` |
|
||||
| **Access URL** | http://192.168.86.149:81 |
|
||||
| **External Access** | LAN + Internet (ports 80/443 forwarded) |
|
||||
| **Port Mapping** | 81:81 (Admin), 80:80 (HTTP), 443:443 (HTTPS) |
|
||||
| **Network Mode** | Host |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/nginx-proxy-manager/data:/data`, `~/docker-data/nginx-proxy-manager/letsencrypt:/etc/letsencrypt` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None (reverse proxy for other services) |
|
||||
|
||||
---
|
||||
|
||||
### Ollama
|
||||
|
||||
Ollama is a GPU-accelerated large language model server that provides a REST API for running local LLM inference with models up to 13B parameters, leveraging the RTX 2080 Ti's 11GB VRAM for fast on-device AI capabilities. It manages model downloads, quantization, and serving through a simple API compatible with OpenAI's format, supporting use cases like code generation, chat applications, and text processing without cloud dependencies. The service stores models on the SSD for quick loading times and maintains persistent model storage across container restarts.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `ollama/ollama:latest` |
|
||||
| **Container Name** | `ollama` |
|
||||
| **Access URL** | http://192.168.86.149:11434 |
|
||||
| **External Access** | LAN only (API endpoint) |
|
||||
| **Port Mapping** | 11434:11434 (API) |
|
||||
| **Network Mode** | Bridge (custom network) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/ollama/models:/root/.ollama` |
|
||||
| **Resource Limits** | Memory: 8GB, GPU: 11GB VRAM |
|
||||
| **GPU Required** | Yes (NVIDIA RTX 2080 Ti) |
|
||||
| **GPU Configuration** | `NVIDIA_VISIBLE_DEVICES=all`, `NVIDIA_DRIVER_CAPABILITIES=compute,utility` |
|
||||
| **Dependencies** | NVIDIA Container Toolkit |
|
||||
| **Typical Models** | llama3.2:3b (~2GB), mistral:7b (~4GB), codellama:7b (~4GB) |
|
||||
|
||||
---
|
||||
|
||||
### Code-Server
|
||||
|
||||
Code-Server provides a browser-based Visual Studio Code IDE running directly on the host system, offering a persistent development environment with full access to host-level configurations, filesystems, and systemd services without the limitations of containerization. It replaces traditional SSH access by providing a rich IDE experience that survives network disconnections through session persistence, with integrated terminal access, file explorer, git integration, and extension support. The service runs as a systemd service on the host, listening on localhost and exposed externally through Nginx Proxy Manager with SSL encryption and multi-layer authentication for secure remote development access.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Deployment Type** | Host-based systemd service (NOT containerized) |
|
||||
| **Binary Location** | `/usr/bin/code-server` |
|
||||
| **Service Name** | `code-server.service` |
|
||||
| **Access URL (LAN)** | http://127.0.0.1:8084 (localhost only) |
|
||||
| **Access URL (Public)** | https://code.schweitz.net |
|
||||
| **External Access** | Yes (via NPM reverse proxy with SSL) |
|
||||
| **Port Binding** | 127.0.0.1:8084 (not exposed to network) |
|
||||
| **User/Group** | `jpmschweitzer:jpmschweitzer` |
|
||||
| **Restart Policy** | `always` (systemd) |
|
||||
| **Config Location** | `~/.config/code-server/config.yaml` |
|
||||
| **Data Storage (SSD)** | `~/docker-data/code-server/user-data/` (settings, workspace), `~/docker-data/code-server/extensions/` (extensions) |
|
||||
| **Resource Limits** | None (native host process) |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | NPM (reverse proxy), systemd |
|
||||
| **Authentication** | Triple-layer: NPM access list, code-server password, SSL certificate |
|
||||
| **WebSocket Support** | Required (enabled via NPM proxy) |
|
||||
| **Typical Memory** | ~200-500MB (depends on workspace size) |
|
||||
| **Setup Guide** | `docs/code-server-setup.md` |
|
||||
|
||||
---
|
||||
|
||||
## Networking Layer
|
||||
|
||||
### Headscale
|
||||
|
||||
Headscale is a self-hosted control server for Tailscale's mesh VPN protocol, creating a private software-defined network across all connected devices with end-to-end encryption and zero-configuration NAT traversal. It enables secure remote access to all homelab services from anywhere without exposing ports to the internet, using a custom 10.99.0.0/16 IP range for the mesh network. The service manages device registration, authentication, and mesh routing while maintaining full data sovereignty compared to the hosted Tailscale control plane.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `headscale/headscale:latest` |
|
||||
| **Container Name** | `headscale` |
|
||||
| **Access URL** | http://192.168.86.149:8085 |
|
||||
| **External Access** | LAN only (control server) |
|
||||
| **Port Mapping** | 8085:8080 (Web/API), 9090:9090 (Metrics) |
|
||||
| **Network Mode** | Bridge (custom network) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/headscale/config:/etc/headscale`, `~/docker-data/headscale/data:/var/lib/headscale` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None |
|
||||
| **Network Range** | 10.99.0.0/16 (mesh IPs) |
|
||||
| **Pre-Auth Keys** | 24-hour expiration |
|
||||
|
||||
---
|
||||
|
||||
## Monitoring Layer
|
||||
|
||||
### Uptime Kuma
|
||||
|
||||
Uptime Kuma monitors the availability and response times of all infrastructure and application services, providing real-time status dashboards with historical uptime tracking, incident detection, and notification capabilities. It performs HTTP, TCP, and ICMP checks at configurable intervals against each service endpoint, alerting on downtime events through multiple notification channels including email, Discord, and Slack. The service maintains a SQLite database of uptime history and response time metrics accessible through a clean web interface.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `louislam/uptime-kuma:latest` |
|
||||
| **Container Name** | `uptime-kuma` |
|
||||
| **Access URL** | http://192.168.86.149:3001 |
|
||||
| **External Access** | LAN only (monitoring dashboard) |
|
||||
| **Port Mapping** | 3001:3001 (Web UI) |
|
||||
| **Network Mode** | Bridge |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/uptime-kuma:/app/data` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None (monitors other services) |
|
||||
| **Database** | SQLite (persistent in volume) |
|
||||
| **Check Intervals** | 60 seconds (configurable) |
|
||||
|
||||
---
|
||||
|
||||
### Netdata
|
||||
|
||||
Netdata provides comprehensive real-time system performance monitoring with per-second metric collection for CPU, RAM, disk I/O, network traffic, and Docker container resource usage, displaying everything through interactive web dashboards with zero configuration required. It collects thousands of metrics automatically with minimal overhead, offering drill-down capabilities from system-wide views to per-container and per-process analysis. The service maintains short-term metric history in RAM and can stream data to long-term storage backends for historical analysis.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `netdata/netdata:latest` |
|
||||
| **Container Name** | `netdata` |
|
||||
| **Access URL** | http://192.168.86.149:19999 |
|
||||
| **External Access** | LAN only (metrics dashboard) |
|
||||
| **Port Mapping** | 19999:19999 (Web UI) |
|
||||
| **Network Mode** | Host (for full system visibility) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `/proc:/host/proc:ro`, `/sys:/host/sys:ro`, `/var/run/docker.sock:/var/run/docker.sock:ro` |
|
||||
| **Capabilities** | `SYS_PTRACE`, `apparmor:unconfined` |
|
||||
| **Resource Limits** | None (monitoring overhead ~1-3% CPU) |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | Docker socket (read-only) |
|
||||
| **Metric Retention** | ~1 hour (RAM-based) |
|
||||
|
||||
---
|
||||
|
||||
### Organizr
|
||||
|
||||
Organizr serves as a comprehensive unified dashboard that consolidates all homelab services into a single tabbed interface with integrated homepage widgets showing real-time statistics from Jellyfin streams, Netdata metrics, Uptime Kuma status checks, and download client activity. It provides customizable authentication per-tab with support for SSO integration, user management with group-based access control, and a mobile-responsive interface for managing the entire infrastructure from anywhere. The service acts as a central hub replacing the need for multiple bookmarks or remembering service ports, offering both quick-access tabs and homepage cards with live data feeds from connected services.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `organizr/organizr:latest` |
|
||||
| **Container Name** | `organizr` |
|
||||
| **Access URL (LAN)** | http://192.168.86.149:9999 |
|
||||
| **Access URL (Public)** | https://home.schweitz.net |
|
||||
| **External Access** | Yes (via NPM reverse proxy with SSL) |
|
||||
| **Port Mapping** | 9999:80 (HTTP), 443:443 (HTTPS) |
|
||||
| **Network Mode** | Bridge |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/organizr:/config` |
|
||||
| **Environment** | Built-in (no custom env vars) |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None (integrates with other services via API) |
|
||||
| **Database** | SQLite at `/config/db/organizrDashboardDb.db` |
|
||||
| **Database Size** | ~5-10MB (typical) |
|
||||
| **Integrated Services** | Jellyfin, Netdata, Uptime Kuma |
|
||||
| **Authentication** | Internal (supports SSO, Plex OAuth, LDAP) |
|
||||
|
||||
---
|
||||
|
||||
## Optimization Layer
|
||||
|
||||
### Watchtower
|
||||
|
||||
Watchtower automatically monitors all running containers for updated images and performs rolling updates on a configurable schedule, ensuring the infrastructure stays current with security patches and feature releases without manual intervention. It checks Docker Hub and configured registries daily at 4 AM, pulls new images when available, gracefully stops containers, deploys updated versions, and cleans up old images to prevent disk bloat. The service logs all update activities and can send notifications through various channels when updates occur.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `containrrr/watchtower:latest` |
|
||||
| **Container Name** | `watchtower` |
|
||||
| **Access URL** | N/A (background service) |
|
||||
| **External Access** | N/A |
|
||||
| **Port Mapping** | None (no exposed ports) |
|
||||
| **Network Mode** | Bridge |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `/var/run/docker.sock:/var/run/docker.sock` |
|
||||
| **Environment** | `WATCHTOWER_CLEANUP=true`, `WATCHTOWER_SCHEDULE=0 0 4 * * *` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | Docker socket (read-write for updates) |
|
||||
| **Schedule** | Daily at 4:00 AM |
|
||||
| **Cleanup** | Automatic (removes old images) |
|
||||
|
||||
---
|
||||
|
||||
### Maintenance Container
|
||||
|
||||
The maintenance container runs scheduled automation tasks including nightly Docker configuration backups with 30-day retention, disk space monitoring, log cleanup, and future expansion for health checks and system maintenance scripts. It executes cron-based jobs at 3 AM daily to archive all Docker Compose configurations, container settings, and persistent data to the backup directory on the HDD with timestamped snapshots. The container provides a centralized location for all homelab automation without cluttering the host system with multiple cron entries.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `alpine:latest` |
|
||||
| **Container Name** | `maintenance` |
|
||||
| **Access URL** | N/A (background service) |
|
||||
| **External Access** | N/A |
|
||||
| **Port Mapping** | None (no exposed ports) |
|
||||
| **Network Mode** | Bridge |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data:/source:ro`, `/mnt/media/backups:/backups` |
|
||||
| **Command** | Runs crond with custom crontab |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None |
|
||||
| **Schedule** | Daily at 3:00 AM (backups) |
|
||||
| **Backup Retention** | 30 days |
|
||||
| **Backup Size** | ~94MB per snapshot |
|
||||
|
||||
---
|
||||
|
||||
## Application Layer
|
||||
|
||||
### Open WebUI
|
||||
|
||||
Open WebUI is a feature-rich, self-hosted web interface for interacting with large language models via Ollama, providing a ChatGPT-like experience with support for multiple models, conversation history, RAG (Retrieval-Augmented Generation), web search integration, and user authentication. It offers a modern chat interface with streaming responses, markdown rendering, code syntax highlighting, and conversation management, enabling seamless switching between different LLM models and maintaining persistent chat histories in a local database. The service integrates directly with the local Ollama instance for GPU-accelerated inference without cloud dependencies, supporting features like web search via DuckDuckGo, document upload for context, and multi-user access with authentication. It is documented at: https://docs.openwebui.com/
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `ghcr.io/open-webui/open-webui:main` |
|
||||
| **Container Name** | `open-webui` |
|
||||
| **Access URL** | http://192.168.86.149:82 |
|
||||
| **External Access** | LAN only (not yet proxied) |
|
||||
| **Port Mapping** | 82:8080 (HTTP) |
|
||||
| **Network Mode** | Bridge (custom network: ai-network) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/open-webui:/app/backend/data` |
|
||||
| **Environment** | `OLLAMA_BASE_URL=http://192.168.86.149:11434`, `DEFAULT_MODELS=llama3.2:3b`, `ENABLE_RAG_WEB_SEARCH=true`, `ENABLE_OLLAMA_API=true`, `WEBUI_AUTH=true`, `RAG_WEB_SEARCH_ENGINE=duckduckgo`, `AUDIO_STT_ENGINE=openai`, `AUDIO_TTS_ENGINE=openai` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No (uses Ollama for GPU inference) |
|
||||
| **Dependencies** | Ollama (ML inference backend) |
|
||||
| **Database** | SQLite (persistent in volume) |
|
||||
| **Authentication** | Built-in user management |
|
||||
| **Integrated Services** | Ollama, DuckDuckGo (web search) |
|
||||
|
||||
---
|
||||
|
||||
### Core API
|
||||
|
||||
Core API provides OpenAI-compatible HTTP functions for Open WebUI, extending LLM capabilities with AI orchestration, web scraping, and content processing services in a hot-reload development environment. The service implements **Phase 1 of the AI Orchestrator** plan, providing `/v1/chat/completions` and `/v1/models` endpoints with full OpenAI API compatibility, model aliasing (gpt-3.5-turbo → gemma:7b), and streaming support via Server-Sent Events. It uses Trafilatura for intelligent content extraction with BeautifulSoup fallback, offering configurable content length limits and optional link extraction optimized for feeding webpage content to language models. The service runs on Python 3.12 with mounted source code for instant updates, maintaining a persistent venv in docker-data for fast container restarts and development agility.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `python:3.12` |
|
||||
| **Container Name** | `core-api` |
|
||||
| **Access URL** | http://192.168.86.149:8083 |
|
||||
| **API Documentation** | http://192.168.86.149:8083/docs (Swagger UI) |
|
||||
| **External Access** | LAN only (internal API) |
|
||||
| **Port Mapping** | 8083:8083 (HTTP) |
|
||||
| **Network Mode** | Bridge (custom network: ai-dataplane) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `/home/jpmschweitzer/Projects/portainer-core/services/core-api:/app` (source code), `~/docker-data/core-api/venv:/venv` (dependencies), `~/docker-data/core-api/logs:/app/logs` (logs) |
|
||||
| **Environment** | `APP_NAME=Core API`, `APP_VERSION=1.0.0-phase1`, `DEBUG=true`, `PORT=8083`, `LOG_LEVEL=INFO`, `PYTHONPATH=/app`, Model aliases: `ALIAS_GPT35=gemma:7b`, `ALIAS_GPT4=mistral:7b` |
|
||||
| **Command** | Hot-reload with uvicorn: `--reload --reload-dir /app/src` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No (proxies requests to Ollama which uses GPU) |
|
||||
| **Dependencies** | Ollama (model inference), Open WebUI (consumes this API), ai-dataplane network |
|
||||
| **Framework** | FastAPI 0.115.0, Uvicorn 0.32.0, Pydantic 2.10.4, httpx 0.28.1 |
|
||||
| **Key Features** | OpenAI-compatible API (`/v1/chat/completions`, `/v1/models`), Model aliasing (OpenAI → local models), Streaming & non-streaming responses, Web scraping (Trafilatura, BeautifulSoup), Hot-reload development, OpenAPI spec, Infrastructure management (Portainer, Uptime Kuma) |
|
||||
| **AI Orchestrator** | **Phase 1 Complete** - OpenAI API wrapper with model routing. Phase 2+ will add memory systems, multi-agent workflows, and tool integration. |
|
||||
| **Health Check** | `GET /health` (30s interval) - checks API status and Ollama connectivity |
|
||||
| **Monitoring API** | Full CRUD for Uptime Kuma monitors via Socket.IO: `GET /infrastructure/monitors`, `POST /infrastructure/monitors`, `GET /infrastructure/monitors/{id}`, `PUT /infrastructure/monitors/{id}`, `DELETE /infrastructure/monitors/{id}` |
|
||||
|
||||
**Creating Monitors via API**:
|
||||
```bash
|
||||
# Create a TCP port monitor for Redis
|
||||
curl -X POST http://192.168.86.149:8083/infrastructure/monitors \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "port",
|
||||
"name": "Redis Shared - Port Check",
|
||||
"hostname": "redis-shared",
|
||||
"port": 6379,
|
||||
"interval": 60,
|
||||
"retryInterval": 60,
|
||||
"maxretries": 3,
|
||||
"notificationIDList": [],
|
||||
"accepted_statuscodes": ["200-299"]
|
||||
}'
|
||||
|
||||
# Create a PostgreSQL database monitor (note URL-encoded password)
|
||||
curl -X POST http://192.168.86.149:8083/infrastructure/monitors \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "postgres",
|
||||
"name": "PostgreSQL Shared",
|
||||
"interval": 60,
|
||||
"retryInterval": 60,
|
||||
"maxretries": 3,
|
||||
"notificationIDList": [],
|
||||
"accepted_statuscodes": ["200-299"],
|
||||
"databaseConnectionString": "postgres://user:password@postgres-shared:5432/postgres"
|
||||
}'
|
||||
```
|
||||
|
||||
**Important**: When creating database monitors with passwords containing special characters (`/`, `=`, `+`, etc.), URL-encode them in the connection string (e.g., `/` → `%2F`, `=` → `%3D`).
|
||||
|
||||
---
|
||||
|
||||
### Jellyfin
|
||||
|
||||
Jellyfin is a GPU-accelerated media server that organizes, streams, and transcodes video, music, and photo libraries with hardware encoding via NVIDIA NVENC, enabling smooth 4K playback across multiple simultaneous clients without taxing the CPU. It provides a Netflix-like interface accessible through web browsers, mobile apps, and smart TV clients, with automatic metadata fetching, subtitle support, and user management for family sharing. The service stores configuration and cache on the SSD for responsive browsing while accessing massive media libraries on the 3.7TB HDD, supporting direct play when possible and GPU-accelerated transcoding when format conversion is needed.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `jellyfin/jellyfin:latest` |
|
||||
| **Container Name** | `jellyfin` |
|
||||
| **Access URL** | http://192.168.86.149:8096, https://media.schweitz.net |
|
||||
| **External Access** | LAN + Internet (via NPM reverse proxy) |
|
||||
| **Port Mapping** | 8096:8096 (HTTP), 8920:8920 (HTTPS), 7359:7359/udp (Discovery), 1900:1900/udp (DLNA) |
|
||||
| **Network Mode** | Bridge |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/jellyfin/config:/config`, `~/docker-data/jellyfin/cache:/cache`, `/mnt/media/jellyfin:/media:ro` |
|
||||
| **Environment** | `NVIDIA_VISIBLE_DEVICES=all`, `NVIDIA_DRIVER_CAPABILITIES=all` |
|
||||
| **User** | `1000:1000` (UID:GID) |
|
||||
| **Resource Limits** | Memory: 4GB, GPU: 11GB VRAM (shared) |
|
||||
| **GPU Required** | Yes (NVIDIA RTX 2080 Ti) |
|
||||
| **GPU Configuration** | NVENC hardware encoding, NVDEC hardware decoding |
|
||||
| **Dependencies** | NVIDIA Container Toolkit, NPM (for external access) |
|
||||
| **Media Storage** | /mnt/media/jellyfin (HDD) |
|
||||
| **Transcoding** | Hardware-accelerated (H.264/H.265) |
|
||||
|
||||
---
|
||||
|
||||
### Nextcloud
|
||||
|
||||
Nextcloud is a self-hosted cloud storage and collaboration platform providing file sync, sharing, calendar, contacts, and collaborative document editing with a web interface and mobile apps, replacing cloud services like Dropbox or Google Drive while maintaining full data sovereignty. It runs as a multi-container stack with a MariaDB database for metadata, Redis for caching and file locking, and the main PHP application container, with the application configuration stored on SSD for responsiveness while user data resides on the HDD for capacity. The service integrates behind Nginx Proxy Manager with SSL at https://cloud.schweitz.net, offering external access for file synchronization from anywhere while maintaining automated background job execution through the maintenance container's cron system.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `nextcloud:stable` |
|
||||
| **Container Name** | `nextcloud` |
|
||||
| **Access URL (LAN)** | http://192.168.86.149:8082 |
|
||||
| **Access URL (Public)** | https://cloud.schweitz.net |
|
||||
| **External Access** | Yes (via NPM reverse proxy with SSL) |
|
||||
| **Port Mapping** | 8082:80 (HTTP) |
|
||||
| **Network Mode** | Bridge (custom network: nextcloud_nextcloud-network) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/nextcloud/config:/var/www/html` (SSD), `/mnt/media/nextcloud/data:/var/www/html/data` (HDD) |
|
||||
| **Environment** | `MYSQL_HOST=nextcloud-db`, `MYSQL_DATABASE=nextcloud`, `MYSQL_USER=nextcloud`, `REDIS_HOST=nextcloud-redis`, `TZ=Europe/Amsterdam` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | MariaDB 10.11 (nextcloud-db), Redis Alpine (nextcloud-redis), NPM (reverse proxy), Maintenance container (cron jobs) |
|
||||
| **Database** | MariaDB on SSD (~100MB) |
|
||||
| **Cron Jobs** | Background tasks every 5 minutes (via maintenance container) |
|
||||
| **Storage Split** | Config/apps on SSD, user data on HDD |
|
||||
| **Features** | File sync, calendar, contacts, document editing, photo gallery, mobile apps |
|
||||
|
||||
---
|
||||
|
||||
### Samba
|
||||
|
||||
Samba provides SMB/CIFS network file sharing for seamless access to homelab storage from Windows, macOS, Linux, and mobile devices, exposing curated shares for media libraries, downloads, and backups with configurable read-only and read-write permissions. It runs as a single container on the samba_default network, serving three shares: Media (read-write access to Jellyfin content), Downloads (read-write for torrent clients), and Backups (read-only for safe data recovery). The service uses password authentication for the user jpmschweitzer and stores its minimal configuration on the SSD while directly mounting HDD paths for zero-copy file access with native performance.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `dperson/samba` |
|
||||
| **Container Name** | `samba` |
|
||||
| **Access URL** | \\\\192.168.86.149 or \\\\tower-of-joy |
|
||||
| **External Access** | LAN only (ports firewalled) |
|
||||
| **Port Mapping** | 139:139 (NetBIOS), 445:445 (SMB) |
|
||||
| **Network Mode** | Bridge (custom network: samba_default) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/samba:/share/config` (SSD config), `/mnt/media/jellyfin:/share/media` (Media share), `/mnt/media/downloads:/share/downloads` (Downloads share), `/mnt/media/backups:/share/backups:ro` (Backups read-only) |
|
||||
| **Environment** | `TZ=Europe/Amsterdam`, `USERID=1000`, `GROUPID=1000` |
|
||||
| **Command** | Share configs: Media (browseable, guest access), Downloads (no guest), Backups (read-only, browseable) |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | UFW firewall rules (ports 139, 445), Host Samba service disabled |
|
||||
| **Shares** | 3 total: Media (R/W), Downloads (R/W), Backups (R/O) |
|
||||
| **Authentication** | Username/password (jpmschweitzer) |
|
||||
| **Client Compatibility** | Windows, macOS, Linux, iOS, Android |
|
||||
|
||||
---
|
||||
|
||||
### Gitea
|
||||
|
||||
Gitea is a lightweight, self-hosted Git service providing repository hosting, issue tracking, pull requests, code review, and CI/CD integration through a clean web interface accessible via both HTTPS and SSH. It runs as a multi-container stack with a PostgreSQL database for metadata storage, offering GitHub-like functionality including organizations, teams, wikis, webhooks, and automated Actions workflows while maintaining complete data sovereignty and minimal resource overhead. The service stores all Git repositories and configuration on the SSD for fast access, integrates behind Nginx Proxy Manager with SSL at https://git.schweitz.net for web access, and exposes SSH on port 2222 for standard Git operations without conflicting with the host's SSH service.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `gitea/gitea:latest` |
|
||||
| **Container Name** | `gitea` |
|
||||
| **Access URL (LAN)** | http://192.168.86.149:3002 |
|
||||
| **Access URL (Public)** | https://git.schweitz.net |
|
||||
| **External Access** | Yes (via NPM reverse proxy with SSL) |
|
||||
| **Port Mapping** | 3002:3000 (HTTP), 2222:22 (SSH) |
|
||||
| **Network Mode** | Bridge (custom network: gitea_gitea-network) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | `~/docker-data/gitea/data:/data` (SSD - repos, config) |
|
||||
| **Environment** | `USER_UID=1000`, `USER_GID=1000`, `GITEA__database__*` (PostgreSQL connection), `TZ=Europe/Amsterdam` |
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | PostgreSQL 14 (gitea-db), NPM (reverse proxy) |
|
||||
| **Database** | PostgreSQL on SSD (~50MB) |
|
||||
| **SSH Access** | Port 2222 - `git clone ssh://git@git.schweitz.net:2222/user/repo.git` |
|
||||
| **Features** | Git hosting, Organizations/teams, Issues/PRs, Code review, Wikis, Webhooks, Gitea Actions (CI/CD), GitHub/GitLab migration |
|
||||
| **SSH Config Tip** | Add to `~/.ssh/config`: `Host git.schweitz.net` / `Port 2222` / `User git` for seamless cloning |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Tables
|
||||
|
||||
### Service Access Matrix
|
||||
|
||||
| Service | LAN URL | Internet Access | Primary Function |
|
||||
|---------|---------|-----------------|------------------|
|
||||
| **Portainer** | http://192.168.86.149:8001 | No | Container management |
|
||||
| **PostgreSQL Shared** | postgres-shared:5432 | No (internal) | Shared database server |
|
||||
| **Redis Shared** | redis-shared:6379 | No (internal) | Shared cache/session store |
|
||||
| **NPM** | http://192.168.86.149:81 | Yes (admin) | Reverse proxy admin |
|
||||
| **Code-Server** | https://code.schweitz.net | Yes | Browser-based IDE |
|
||||
| **Ollama** | http://192.168.86.149:11434 | No | ML model API |
|
||||
| **Headscale** | http://192.168.86.149:8085 | No | VPN control server |
|
||||
| **Uptime Kuma** | http://192.168.86.149:3001 | No | Uptime monitoring |
|
||||
| **Netdata** | http://192.168.86.149:19999 | No | System metrics |
|
||||
| **Heimdall** | http://192.168.86.149:8888 | No | Service dashboard |
|
||||
| **Organizr** | https://home.schweitz.net | Yes | Unified dashboard |
|
||||
| **Open WebUI** | http://192.168.86.149:82 | No | LLM chat interface |
|
||||
| **Core API** | http://192.168.86.149:8083 | No | API functions & infrastructure mgmt |
|
||||
| **Jellyfin** | https://media.schweitz.net | Yes | Media streaming |
|
||||
| **Nextcloud** | https://cloud.schweitz.net | Yes | Cloud storage & sync |
|
||||
| **Gitea** | https://git.schweitz.net | Yes | Git repository hosting |
|
||||
| **Samba** | \\\\192.168.86.149 | No | Network file shares |
|
||||
| **Watchtower** | N/A (background) | N/A | Auto-updates |
|
||||
| **Maintenance** | N/A (background) | N/A | Automated tasks |
|
||||
|
||||
---
|
||||
|
||||
### GPU-Enabled Services
|
||||
|
||||
| Service | GPU Usage | VRAM Requirements | Purpose |
|
||||
|---------|-----------|-------------------|---------|
|
||||
| **Ollama** | Compute, Utility | 2-10GB (model dependent) | LLM inference |
|
||||
| **Jellyfin** | Video Encode/Decode | ~1-2GB (during transcode) | Media transcoding |
|
||||
|
||||
**Total VRAM Available:** 11GB (RTX 2080 Ti)
|
||||
|
||||
---
|
||||
|
||||
### Storage Distribution
|
||||
|
||||
| Service | Config Location (SSD) | Data Location (HDD) | Typical Size |
|
||||
|---------|----------------------|---------------------|--------------|
|
||||
| **Portainer** | Docker volume: `portainer_data` | N/A | ~100MB |
|
||||
| **PostgreSQL Shared** | N/A | `~/docker-data/postgres-shared/data/` | Data: 100MB-5GB (depends on databases), Backups: variable |
|
||||
| **Redis Shared** | N/A | `~/docker-data/redis-shared/data/` | ~10-100MB (AOF + RDB snapshots) |
|
||||
| **NPM** | `~/docker-data/nginx-proxy-manager/` | N/A | ~50MB |
|
||||
| **Code-Server** | `~/.config/code-server/`, `~/docker-data/code-server/` | N/A | Config: ~5MB, Extensions: ~50-200MB, User data: ~50MB |
|
||||
| **Ollama** | `~/docker-data/ollama/models/` | Alt: `/mnt/media/ollama/` | 2-15GB per model |
|
||||
| **Headscale** | `~/docker-data/headscale/` | N/A | ~10MB |
|
||||
| **Uptime Kuma** | `~/docker-data/uptime-kuma/` | N/A | ~50MB |
|
||||
| **Netdata** | RAM-based (ephemeral) | N/A | ~200MB RAM |
|
||||
| **Heimdall** | `~/docker-data/heimdall/` | N/A | ~20MB |
|
||||
| **Organizr** | `~/docker-data/organizr/` | N/A | ~50MB |
|
||||
| **Open WebUI** | `~/docker-data/open-webui/` | N/A | ~100MB |
|
||||
| **Core API** | `~/docker-data/core-api/`, `/home/jpmschweitzer/Projects/portainer-core/services/core-api/` (source) | N/A | Venv: ~200MB, Logs: ~10MB |
|
||||
| **Jellyfin** | `~/docker-data/jellyfin/` | `/mnt/media/jellyfin/` | Config: ~500MB, Media: ~2TB |
|
||||
| **Nextcloud** | `~/docker-data/nextcloud/` | `/mnt/media/nextcloud/data/` | Config: ~200MB, DB: ~100MB, User data: variable |
|
||||
| **Gitea** | `~/docker-data/gitea/` | N/A | Data: ~100MB, DB: ~50MB, Repos: variable |
|
||||
| **Samba** | `~/docker-data/samba/` | Mounts: `/mnt/media/` (shares) | Config: ~5MB |
|
||||
| **Maintenance** | N/A | `/mnt/media/backups/` | ~94MB per backup |
|
||||
|
||||
**SSD Usage (docker-data):** ~6-11GB (configs, caches, databases)
|
||||
**HDD Usage (/mnt/media):** ~2.1TB / 3.6TB (58% used)
|
||||
|
||||
---
|
||||
|
||||
### Network Architecture
|
||||
|
||||
**As of 2025-11-15**, all services have been consolidated to the `docker-dataplane` bridge network for simplified service discovery and inter-container communication. This consolidation replaced 12+ legacy networks with a single unified network, enabling all services to communicate using container names as DNS hostnames.
|
||||
|
||||
| Network Name | Containers | Purpose |
|
||||
|--------------|------------|---------|
|
||||
| **docker-dataplane** | Ollama, Open WebUI, Core API, Qdrant, Uptime Kuma, PostgreSQL Shared, Redis Shared, Headscale, Nextcloud, Gitea, Samba, Watchtower, Maintenance, Organizr, Netdata | Unified service mesh for all containerized applications |
|
||||
| **host** | Portainer, NPM | Direct host port access for infrastructure management |
|
||||
|
||||
**Benefits of Consolidation**:
|
||||
- **Service Discovery**: All services reachable via `http://container-name:port` (e.g., `http://postgres-shared:5432`)
|
||||
- **Simplified Monitoring**: Uptime Kuma can monitor all services on docker-dataplane
|
||||
- **Shared Infrastructure**: postgres-shared and redis-shared accessible to all applications
|
||||
- **Network Cleanup**: Removed 7 obsolete networks (stacks_default, ai-dataplane, various stack-specific networks)
|
||||
|
||||
**Container Name Resolution Examples**:
|
||||
```bash
|
||||
# From any container on docker-dataplane
|
||||
curl http://uptime-kuma:3001 # Uptime Kuma API
|
||||
curl http://ollama:11434 # Ollama LLM API
|
||||
psql -h postgres-shared -U postgres # PostgreSQL connection
|
||||
redis-cli -h redis-shared # Redis connection
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Restart Policies
|
||||
|
||||
| Policy | Containers | Behavior |
|
||||
|--------|------------|----------|
|
||||
| **always** | Portainer | Restart on failure, on Docker daemon restart |
|
||||
| **unless-stopped** | All others | Restart on failure, but not after manual stop |
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Schedule
|
||||
|
||||
| Service | Task | Frequency | Time |
|
||||
|---------|------|-----------|------|
|
||||
| **Watchtower** | Container updates | Daily | 4:00 AM |
|
||||
| **Maintenance** | Config backups | Daily | 3:00 AM |
|
||||
| **Maintenance** | Nextcloud background jobs | Every 5 minutes | Continuous |
|
||||
| **Maintenance** | Log cleanup | Weekly | Sunday 3:30 AM |
|
||||
| **Docker** | Image pruning | Monthly | 1st of month |
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: 2025-11-16*
|
||||
*System: tower-of-joy (tower-of-joy v0.5.0-optimization)*
|
||||
@@ -0,0 +1,266 @@
|
||||
# SYSTEM.md
|
||||
|
||||
**System documentation for LLM coding agents** - This file describes the computer system where this project resides, including hardware, OS, installed software, and environment details.
|
||||
|
||||
> Last updated: 2025-11-11
|
||||
|
||||
## System Overview
|
||||
|
||||
- **Hostname**: tower-of-joy
|
||||
- **User**: jpmschweitzer
|
||||
- **Home Directory**: /home/jpmschweitzer
|
||||
- **Project Location**: /home/jpmschweitzer/Projects/portainer-core
|
||||
|
||||
## Operating System
|
||||
|
||||
### Distribution
|
||||
- **OS**: Zorin OS 16.3
|
||||
- **Based on**: Ubuntu 20.04 (Focal Fossa)
|
||||
- **Kernel**: Linux 5.4.0-216-generic
|
||||
- **Architecture**: x86_64 (64-bit)
|
||||
|
||||
### Desktop Environment
|
||||
- **Display Server**: X11 (GDM)
|
||||
- **Desktop**: GNOME Shell (Zorin session mode)
|
||||
- **Session Manager**: gnome-session
|
||||
|
||||
### Locale & Timezone
|
||||
- **Language**: en_US.UTF-8
|
||||
- **Numeric/Time Format**: nl_NL.UTF-8
|
||||
- **Timezone**: Europe/Amsterdam (CET, +0100)
|
||||
|
||||
## Hardware Specifications
|
||||
|
||||
### CPU
|
||||
- **Model**: Intel Core i7-6700 @ 3.40GHz (6th Gen Skylake)
|
||||
- **Cores**: 4 physical cores, 8 threads (2 threads per core)
|
||||
- **Architecture**: x86_64
|
||||
- **Frequency**: 800 MHz - 4000 MHz (currently ~3666 MHz)
|
||||
- **Cache**:
|
||||
- L1d: 128 KiB
|
||||
- L1i: 128 KiB
|
||||
- L2: 1 MiB
|
||||
- L3: 8 MiB
|
||||
- **Virtualization**: VT-x supported
|
||||
- **Notable Flags**: AVX, AVX2, AES-NI, SSE4.1, SSE4.2, FMA
|
||||
|
||||
### Memory
|
||||
- **Total RAM**: 16 GiB
|
||||
- **Available**: ~9.6 GiB (typical)
|
||||
- **Swap**: 2.0 GiB
|
||||
|
||||
### Storage
|
||||
|
||||
**System has 2 disks with total capacity of 4.2TB:**
|
||||
|
||||
#### Disk 1: System SSD (/dev/sda)
|
||||
- **Model**: Crucial CT525MX300SSD1 (525GB SSD)
|
||||
- **Partition**: /dev/sda1
|
||||
- **Filesystem**: ext4
|
||||
- **Total Size**: 489 GB
|
||||
- **Used**: 92 GB (21%)
|
||||
- **Available**: 365 GB
|
||||
- **Mount Point**: `/` (root)
|
||||
- **Purpose**: Operating system, Docker containers, application data
|
||||
|
||||
#### Disk 2: Media HDD (/dev/sdb)
|
||||
- **Model**: Seagate IronWolf NE ST4000NE001 (4TB NAS-grade HDD)
|
||||
- **Total Size**: 3.7 TB
|
||||
- **Filesystem**: ext4
|
||||
- **Label**: "media"
|
||||
- **UUID**: f4300e91-3f51-45a0-b038-03335c5bd792
|
||||
- **Mount Status**: ⚠️ **Currently unmounted** (not in /etc/fstab)
|
||||
- **Purpose**: Media storage for Jellyfin, Nextcloud data, backups
|
||||
- **Drive Type**: NAS-optimized (24/7 operation, multi-user workloads)
|
||||
|
||||
**Total Storage Capacity**: 4.2 TB
|
||||
|
||||
### Graphics
|
||||
- **GPU**: NVIDIA GeForce RTX 2080 Ti (TU102, Rev. A)
|
||||
- **VRAM**: 11 GB (11018 MiB)
|
||||
- **Driver**: NVIDIA 470.256.02
|
||||
- **CUDA Version**: 11.4
|
||||
- **Bus**: PCIe 0a:00.0
|
||||
- **Current Usage**: ~390 MiB VRAM (mostly X11/GNOME)
|
||||
- **Power**: 260W TDP
|
||||
|
||||
**Note**: NVCC (CUDA compiler) is not currently in PATH, but CUDA drivers are installed.
|
||||
|
||||
## Development Tools & Languages
|
||||
|
||||
### Programming Languages
|
||||
|
||||
#### Python
|
||||
- **Version**: 3.8.10 (system default)
|
||||
- **pip**: 25.3 (Python 3.10 in user site-packages)
|
||||
- **Location**: /usr/bin/python3
|
||||
- **Python 2.x**: Not installed
|
||||
- **Virtual Environments**:
|
||||
- virtualenv: Not installed
|
||||
- Conda: Not installed
|
||||
- venv module: Available (built-in)
|
||||
|
||||
#### Node.js & JavaScript
|
||||
- **Node.js**: v24.11.0
|
||||
- **npm**: 11.6.1
|
||||
- **Version Manager**: NVM installed at /home/jpmschweitzer/.nvm
|
||||
|
||||
#### Java
|
||||
- **Version**: Java 21.0.4 LTS (Oracle JDK)
|
||||
- **Runtime**: Java(TM) SE Runtime Environment (build 21.0.4+8-LTS-274)
|
||||
- **VM**: Java HotSpot 64-Bit Server VM
|
||||
|
||||
#### C/C++
|
||||
- **GCC**: 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)
|
||||
- **Make**: GNU Make 4.2.1
|
||||
- **CMake**: Not installed
|
||||
|
||||
#### Other Languages
|
||||
- **Go**: Not installed
|
||||
- **Rust**: Not installed
|
||||
|
||||
### Version Control
|
||||
- **Git**: 2.25.1
|
||||
|
||||
### Containerization & Virtualization
|
||||
- **Docker**: 28.1.1, build 4eba377
|
||||
|
||||
### Editors & IDEs
|
||||
- **Vim**: 8.1 (2018 May 18)
|
||||
- **VS Code**: Not installed
|
||||
|
||||
### Command Line Tools
|
||||
- **Shell**: Bash 5.0.17
|
||||
- **curl**: 7.68.0
|
||||
- **wget**: 1.20.3
|
||||
- **SSH**: OpenSSH 8.2p1 Ubuntu-4ubuntu0.13
|
||||
|
||||
## GPU & CUDA Information
|
||||
|
||||
### NVIDIA GPU Details
|
||||
The system has an NVIDIA RTX 2080 Ti with CUDA support, suitable for:
|
||||
- Machine learning and deep learning workloads
|
||||
- CUDA-accelerated computing
|
||||
- GPU rendering and compute tasks
|
||||
- Parallel processing
|
||||
|
||||
### CUDA Configuration
|
||||
- **Driver Version**: 470.256.02
|
||||
- **CUDA Toolkit Version**: 11.4 (driver supports)
|
||||
- **Compute Capability**: 7.5 (Turing architecture)
|
||||
- **NVCC**: Not in PATH (may need manual setup)
|
||||
|
||||
### GPU Usage Considerations
|
||||
When working with GPU-accelerated code:
|
||||
- Ensure CUDA toolkit is properly installed if needed
|
||||
- Use appropriate CUDA version compatibility (11.4 or compatible)
|
||||
- PyTorch/TensorFlow should use CUDA 11.x compatible builds
|
||||
- Monitor VRAM usage (11 GB total, ~10.6 GB available for compute)
|
||||
|
||||
## System Capabilities & Recommendations
|
||||
|
||||
### Suitable For
|
||||
- **Web Development**: Node.js, npm available
|
||||
- **Python Development**: Python 3.8 with pip
|
||||
- **Java Development**: Java 21 LTS
|
||||
- **Machine Learning**: CUDA-capable GPU with 11GB VRAM
|
||||
- **Containerized Development**: Docker available
|
||||
- **Compiled Languages**: GCC toolchain available
|
||||
- **Media Server**: 3.7TB NAS-grade storage for Jellyfin/Plex
|
||||
- **NAS/File Server**: Seagate IronWolf drive optimized for 24/7 operation
|
||||
- **Cloud Storage**: Ample space for Nextcloud deployments
|
||||
- **Home Server**: Suitable for comprehensive home lab setup
|
||||
|
||||
### Limitations
|
||||
- No Rust toolchain (needs installation)
|
||||
- No Go compiler (needs installation)
|
||||
- CMake not installed (needed for some C/C++ projects)
|
||||
- VS Code not installed (Vim available as alternative)
|
||||
- CUDA compiler not in PATH
|
||||
|
||||
### Environment Notes
|
||||
- NVM is available for Node.js version management
|
||||
- Python 3.8 is the system default (older, consider pyenv for newer versions)
|
||||
- pip is installed in user site-packages (Python 3.10 version)
|
||||
- Docker is available for containerized workflows
|
||||
|
||||
## Package Management
|
||||
|
||||
### System Package Manager
|
||||
- **APT**: Available (Ubuntu/Debian package manager)
|
||||
- Use `sudo apt install <package>` for system packages
|
||||
|
||||
### Language-Specific Package Managers
|
||||
- **Python**: pip3 (25.3)
|
||||
- **Node.js**: npm (11.6.1), managed via NVM
|
||||
- **Java**: Maven/Gradle likely needed (not verified)
|
||||
|
||||
## Network Information
|
||||
- SSH client available (OpenSSH 8.2p1)
|
||||
- Standard network tools available (curl, wget)
|
||||
|
||||
## Usage Notes for LLM Agents
|
||||
|
||||
### Before Installing New Software
|
||||
1. Check if the tool is already installed using `which <command>`
|
||||
2. Check available disk space:
|
||||
- System SSD: 365 GB available (for OS and containers)
|
||||
- Media HDD: 3.7 TB available (currently unmounted - needs mounting)
|
||||
3. Use appropriate package manager (apt, pip, npm, etc.)
|
||||
4. Consider using Docker for isolated environments
|
||||
5. **Mount the 4TB media drive** before deploying data-intensive services:
|
||||
- Recommended mount point: `/mnt/media` or `/media/storage`
|
||||
- Add to `/etc/fstab` for automatic mounting on boot
|
||||
- UUID: `f4300e91-3f51-45a0-b038-03335c5bd792`
|
||||
|
||||
### GPU Development
|
||||
1. Verify CUDA toolkit path if developing GPU code
|
||||
2. Check GPU memory availability with `nvidia-smi`
|
||||
3. Use CUDA 11.x compatible libraries
|
||||
4. Monitor GPU utilization to avoid OOM errors
|
||||
|
||||
### Python Development
|
||||
1. System Python is 3.8.10 (older version)
|
||||
2. Consider using venv for project isolation
|
||||
3. pip is available but points to Python 3.10 libs in user space
|
||||
4. May need to install python3-venv: `sudo apt install python3-venv`
|
||||
|
||||
### Node.js Development
|
||||
1. NVM is installed for version management
|
||||
2. Current Node.js is v24.11.0 (latest as of 2024)
|
||||
3. npm 11.6.1 is available
|
||||
|
||||
### Docker Usage
|
||||
1. Docker 28.1.1 is installed
|
||||
2. Useful for consistent development environments
|
||||
3. Can isolate dependencies and avoid system conflicts
|
||||
|
||||
### Storage Management (Dual-Disk Setup)
|
||||
1. **System SSD (/dev/sda)**: Use for:
|
||||
- Operating system
|
||||
- Docker images and container configs
|
||||
- Application databases (small, performance-critical)
|
||||
- Cache directories
|
||||
|
||||
2. **Media HDD (/dev/sdb)**: Use for:
|
||||
- Jellyfin/Plex media libraries
|
||||
- Nextcloud user data
|
||||
- Backups and archives
|
||||
- Large file storage
|
||||
- Any data-intensive workloads
|
||||
|
||||
3. **Best Practices**:
|
||||
- Keep Docker container configs on SSD for performance
|
||||
- Store media files on HDD (they're sequential access, HDD is fine)
|
||||
- Use bind mounts to map HDD storage into containers
|
||||
- Example: `-v /mnt/media/jellyfin:/media:ro` in Docker
|
||||
|
||||
4. **Before First Use**:
|
||||
- Mount the media drive (see step 5 in "Before Installing New Software")
|
||||
- Verify mount with `df -h /mnt/media`
|
||||
- Set appropriate permissions: `sudo chown -R $USER:$USER /mnt/media`
|
||||
|
||||
---
|
||||
|
||||
*Generated automatically on 2025-11-11, updated with storage configuration*
|
||||
*For project-specific guidelines, see [AGENTS.md](./AGENTS.md)*
|
||||
@@ -0,0 +1,200 @@
|
||||
# Maintenance Scripts Reference
|
||||
|
||||
Shell scripts for common maintenance tasks located in `/scripts/`.
|
||||
|
||||
## Available Scripts
|
||||
|
||||
| Script | Description | Usage |
|
||||
|--------|-------------|-------|
|
||||
| `gpu-check.sh` | Verify GPU passthrough in containers | `./scripts/gpu-check.sh` |
|
||||
| `health-check.sh` | Check all services and report status | `./scripts/health-check.sh` |
|
||||
| `setup-kuma-monitors.sh` | Manual guide for configuring Uptime Kuma monitors | `./scripts/setup-kuma-monitors.sh` |
|
||||
| `setup-kuma-monitors.py` | **Automated** Uptime Kuma monitor setup via API | `source .venv/bin/activate && python3 scripts/setup-kuma-monitors.py` |
|
||||
| `backup-configs.sh` | Backup all Docker configs | `./scripts/backup-configs.sh` |
|
||||
| `disk-usage.sh` | Report disk usage for SSD and HDD | `./scripts/disk-usage.sh` |
|
||||
| `update-stacks.sh` | Pull latest images and update containers | `./scripts/update-stacks.sh <stack-name>` |
|
||||
| `cleanup.sh` | Clean up unused Docker resources | `./scripts/cleanup.sh` |
|
||||
|
||||
## Making Scripts Executable
|
||||
|
||||
```bash
|
||||
# Make all scripts executable
|
||||
chmod +x scripts/*.sh
|
||||
|
||||
# Or individually
|
||||
chmod +x scripts/health-check.sh
|
||||
```
|
||||
|
||||
## Scheduling with Cron
|
||||
|
||||
Add to crontab for automated maintenance:
|
||||
|
||||
```bash
|
||||
# Edit crontab
|
||||
crontab -e
|
||||
|
||||
# Examples:
|
||||
# Daily health check at 8 AM
|
||||
0 8 * * * /home/jpmschweitzer/Projects/portainer-core/scripts/health-check.sh >> /var/log/portainer-core-health.log 2>&1
|
||||
|
||||
# Weekly cleanup on Sunday at 3 AM
|
||||
0 3 * * 0 /home/jpmschweitzer/Projects/portainer-core/scripts/cleanup.sh
|
||||
|
||||
# Daily backup at 2 AM
|
||||
0 2 * * * /home/jpmschweitzer/Projects/portainer-core/scripts/backup-configs.sh
|
||||
```
|
||||
|
||||
## Script Details
|
||||
|
||||
### GPU Check (`gpu-check.sh`)
|
||||
|
||||
Verifies GPU passthrough is working in GPU-enabled containers (Ollama, Jellyfin).
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/gpu-check.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
- Lists all running containers with GPU access
|
||||
- Runs `nvidia-smi` inside each container
|
||||
- Reports any containers that fail GPU detection
|
||||
|
||||
### Health Check (`health-check.sh`)
|
||||
|
||||
Checks status of all deployed services and generates a health report.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/health-check.sh
|
||||
```
|
||||
|
||||
**Checks:**
|
||||
- Container running status
|
||||
- Container health status (if health check defined)
|
||||
- Port accessibility
|
||||
- Basic connectivity tests
|
||||
|
||||
### Uptime Kuma Monitor Setup
|
||||
|
||||
Two versions available:
|
||||
|
||||
**Manual Script (`setup-kuma-monitors.sh`):**
|
||||
- Interactive guide for adding monitors
|
||||
- Shows recommended settings for each service
|
||||
- Good for understanding monitor configuration
|
||||
|
||||
**Automated Script (`setup-kuma-monitors.py`):**
|
||||
- Python script using Uptime Kuma API
|
||||
- Automatically creates monitors for all services
|
||||
- Requires Uptime Kuma API key
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Automated setup
|
||||
source .venv/bin/activate
|
||||
python3 scripts/setup-kuma-monitors.py
|
||||
```
|
||||
|
||||
### Backup Configs (`backup-configs.sh`)
|
||||
|
||||
Backs up Docker container configurations and important data.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/backup-configs.sh
|
||||
```
|
||||
|
||||
**What it backs up:**
|
||||
- Docker Compose files from `/stacks/`
|
||||
- Container configs from `/home/jpmschweitzer/docker-data/`
|
||||
- Project documentation
|
||||
- Excludes large media files (those are backed up separately)
|
||||
|
||||
**Backup location:**
|
||||
- `/mnt/media/backups/portainer-core/`
|
||||
|
||||
See [Backup Procedures](../guides/backup-procedures.md) for comprehensive backup strategy.
|
||||
|
||||
### Disk Usage (`disk-usage.sh`)
|
||||
|
||||
Reports disk usage breakdown for SSD and HDD storage.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/disk-usage.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
- Total SSD usage (`/home/jpmschweitzer/docker-data/`)
|
||||
- Total HDD usage (`/mnt/media/`)
|
||||
- Per-service breakdown
|
||||
- Available space warnings
|
||||
|
||||
### Update Stacks (`update-stacks.sh`)
|
||||
|
||||
Pulls latest images and updates a specific stack.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/update-stacks.sh <stack-name>
|
||||
|
||||
# Examples:
|
||||
./scripts/update-stacks.sh jellyfin
|
||||
./scripts/update-stacks.sh core-api
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
1. Pulls latest images for the stack
|
||||
2. Stops containers gracefully
|
||||
3. Recreates containers with new images
|
||||
4. Removes old images
|
||||
5. Verifies containers started successfully
|
||||
|
||||
**Note:** Watchtower handles this automatically for most services. Use this script for manual updates or services excluded from Watchtower.
|
||||
|
||||
### Cleanup (`cleanup.sh`)
|
||||
|
||||
Cleans up unused Docker resources to free disk space.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/cleanup.sh
|
||||
```
|
||||
|
||||
**What it removes:**
|
||||
- Stopped containers
|
||||
- Unused images
|
||||
- Dangling build cache
|
||||
- Unused volumes (with confirmation prompt)
|
||||
- Unused networks
|
||||
|
||||
**Warning:** Always review what will be removed before confirming volume deletion.
|
||||
|
||||
## Script Guidelines
|
||||
|
||||
All scripts follow these conventions:
|
||||
|
||||
- Include error handling and exit codes
|
||||
- Use absolute paths for reliability
|
||||
- Log output for debugging
|
||||
- Exit with status codes (0 = success, non-zero = failure)
|
||||
- Include help text with `-h` or `--help` flags
|
||||
- Non-destructive by default (ask before deleting)
|
||||
|
||||
## Creating New Scripts
|
||||
|
||||
When adding new maintenance scripts:
|
||||
|
||||
1. Place in `/scripts/` directory
|
||||
2. Use `.sh` extension for shell scripts
|
||||
3. Make executable: `chmod +x scripts/your-script.sh`
|
||||
4. Add to this documentation
|
||||
5. Include help text and error handling
|
||||
6. Test thoroughly before scheduling with cron
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Backup Procedures](../guides/backup-procedures.md) - Comprehensive backup strategy
|
||||
- [Stacks Reference](stacks.md) - Stack deployment and management
|
||||
- [Automation Reference](AUTOMATION.md) - Portainer REST API automation
|
||||
@@ -0,0 +1,185 @@
|
||||
# Docker Compose Stacks Reference
|
||||
|
||||
Complete reference for all Docker Compose stacks in the portainer-core infrastructure.
|
||||
|
||||
## Deployment
|
||||
|
||||
See the [core-api OpenAPI documentation](http://localhost:8083/docs) for infrastructure management REST endpoints.
|
||||
|
||||
All stacks are located in the `/stacks/` directory and version-controlled.
|
||||
|
||||
## Stack Inventory
|
||||
|
||||
### Phase 1: Foundation
|
||||
|
||||
| Stack | File | Ports | GPU | Description |
|
||||
|-------|------|-------|-----|-------------|
|
||||
| **Portainer** | `portainer.yml` | 8080, 8443 | No | Container management UI |
|
||||
| **Nginx Proxy Manager** | `nginx-proxy-manager.yml` | 8000, 80, 443 | No | Reverse proxy and unified web interface |
|
||||
| **Ollama** | `ollama.yml` | 11434 | **Yes** | ML model serving with GPU acceleration |
|
||||
|
||||
### Phase 2: Networking
|
||||
|
||||
| Stack | File | Ports | GPU | Description |
|
||||
|-------|------|-------|-----|-------------|
|
||||
| **Headscale** | `headscale.yml` | 8085, 9090 | No | Self-hosted Tailscale control server |
|
||||
|
||||
### Phase 3: Monitoring
|
||||
|
||||
| Stack | File | Ports | GPU | Description |
|
||||
|-------|------|-------|-----|-------------|
|
||||
| **Uptime Kuma** | `uptime-kuma.yml` | 3001 | No | Service availability monitoring |
|
||||
| **Netdata** | `netdata.yml` | 19999 | No | Real-time system performance monitoring |
|
||||
| **Heimdall** | `heimdall.yml` | 8888, 8889 | No | Application dashboard |
|
||||
|
||||
### Phase 4: Optimization
|
||||
|
||||
| Stack | File | Ports | GPU | Description |
|
||||
|-------|------|-------|-----|-------------|
|
||||
| **Watchtower** | `watchtower.yml` | - | No | Automatic container updates |
|
||||
| **Duplicati** | `duplicati.yml` | 8200 | No | Backup solution |
|
||||
|
||||
### Applications
|
||||
|
||||
| Stack | File | Ports | GPU | Description |
|
||||
|-------|------|-------|-----|-------------|
|
||||
| **Jellyfin** | `jellyfin.yml` | 8096, 8920, 7359, 1900 | **Yes** | Media server with GPU transcoding |
|
||||
| **Nextcloud** | `nextcloud.yml` | 8082 | No | Cloud storage (uses shared PostgreSQL and Redis) |
|
||||
| **Gitea** | `gitea.yml` | 3002, 2222 | No | Git repository hosting (includes PostgreSQL) |
|
||||
| **Samba** | `samba.yml` | 139, 445 | No | Network file sharing |
|
||||
| **Open WebUI** | `open-webui.yml` | 8081 | No | AI chat interface with Ollama integration |
|
||||
| **Core API** | `core-api.yml` | 8083 | No | Infrastructure management and AI orchestration |
|
||||
| **Qdrant** | `qdrant.yml` | 6333, 6334 | No | Vector database for embeddings |
|
||||
| **Organizr** | `organizr.yml` | 8084 | No | Unified dashboard |
|
||||
|
||||
### Shared Infrastructure
|
||||
|
||||
| Stack | File | Ports | GPU | Description |
|
||||
|-------|------|-------|-----|-------------|
|
||||
| **PostgreSQL Shared** | `postgres-shared.yml` | 5432 | No | Shared database for Nextcloud |
|
||||
| **Redis Shared** | `redis-shared.yml` | 6379 | No | Shared cache for Nextcloud |
|
||||
|
||||
## Port Allocation
|
||||
|
||||
### Infrastructure Services (8000-8099)
|
||||
- 8000: Nginx Proxy Manager (unified web interface)
|
||||
- 8080: Portainer
|
||||
- 8081: Open WebUI
|
||||
- 8082: Nextcloud
|
||||
- 8083: Core API
|
||||
- 8084: Organizr
|
||||
- 8085: Headscale
|
||||
- 8096: Jellyfin
|
||||
|
||||
### Git & Development Services
|
||||
- 2222: Gitea SSH
|
||||
- 3002: Gitea HTTP
|
||||
|
||||
### Monitoring Services (3000-3999, 19000-19999)
|
||||
- 3001: Uptime Kuma
|
||||
- 8200: Duplicati
|
||||
- 8888: Heimdall
|
||||
- 19999: Netdata
|
||||
|
||||
### ML/API Services (11000+)
|
||||
- 11434: Ollama
|
||||
- 6333: Qdrant HTTP
|
||||
- 6334: Qdrant gRPC
|
||||
|
||||
### Database Services
|
||||
- 5432: PostgreSQL (shared)
|
||||
- 6379: Redis (shared)
|
||||
|
||||
### Network Services
|
||||
- 80: HTTP (NPM reverse proxy)
|
||||
- 443: HTTPS (NPM reverse proxy)
|
||||
- 139, 445: Samba/SMB
|
||||
- 9090: Headscale metrics
|
||||
|
||||
## Storage Convention
|
||||
|
||||
All stacks follow the dual-disk strategy:
|
||||
|
||||
**SSD (Performance):**
|
||||
- Configs: `/home/jpmschweitzer/docker-data/<service>/config`
|
||||
- Cache: `/home/jpmschweitzer/docker-data/<service>/cache`
|
||||
- Databases: `/home/jpmschweitzer/docker-data/<service>/db`
|
||||
|
||||
**HDD (Capacity):**
|
||||
- User content: `/mnt/media/<service>/data`
|
||||
- Media files: `/mnt/media/<service>/media`
|
||||
- Backups: `/mnt/media/backups/<service>`
|
||||
|
||||
See [Shared Infrastructure Architecture](../architecture/SHARED_INFRASTRUCTURE_ARCHITECTURE.md) for database and cache sharing details.
|
||||
|
||||
## GPU Services
|
||||
|
||||
Stacks requiring GPU access (marked with **Yes** above):
|
||||
- `ollama.yml` - ML model inference
|
||||
- `jellyfin.yml` - Hardware transcoding
|
||||
|
||||
**Prerequisites:**
|
||||
- NVIDIA Container Toolkit installed
|
||||
- GPU verified: `docker run --rm --gpus all nvidia/cuda:11.4.0-base-ubuntu20.04 nvidia-smi`
|
||||
|
||||
See [GPU Docker Configuration](../guides/gpu-docker-config.md) for setup details.
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
### Before Deploying
|
||||
|
||||
1. **Review environment variables** - Change default passwords!
|
||||
2. **Create directories** - Ensure volume paths exist
|
||||
3. **Check ports** - Verify no conflicts with existing services
|
||||
4. **GPU services** - Confirm NVIDIA toolkit installed
|
||||
5. **Update STATUS.md** - Plan the deployment
|
||||
|
||||
### After Deploying
|
||||
|
||||
1. **Test service** - Access web UI or API endpoint
|
||||
2. **Check logs** - `docker logs <container-name>`
|
||||
3. **Verify GPU** - `docker exec <container> nvidia-smi` (if applicable)
|
||||
4. **Update documentation** - Add to STATUS.md and CHANGELOG.md
|
||||
5. **Configure backup** - Add to Duplicati backup job
|
||||
6. **Add monitoring** - Configure Uptime Kuma checks
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Update a Stack
|
||||
|
||||
```bash
|
||||
# Pull latest images
|
||||
docker compose -f stacks/<stack-name>.yml pull
|
||||
|
||||
# Recreate containers with new images
|
||||
docker compose -f stacks/<stack-name>.yml up -d
|
||||
|
||||
# Or let Watchtower handle it automatically
|
||||
```
|
||||
|
||||
### Backup Stack Configuration
|
||||
|
||||
Stacks are version-controlled in the `/stacks/` directory. Backup container data separately using the backup procedures.
|
||||
|
||||
See [Backup Procedures](../guides/backup-procedures.md) for details.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- Container won't start: `docker logs <container-name>`
|
||||
- Port conflicts: `sudo netstat -tulpn | grep <port>`
|
||||
- Permission issues: Check volume path ownership
|
||||
- GPU not detected: Verify NVIDIA toolkit and restart Docker
|
||||
|
||||
## Automation
|
||||
|
||||
The project includes automation scripts for stack management:
|
||||
|
||||
- `update-stack.sh` - Pull and update specific stack
|
||||
- See [Automation Reference](AUTOMATION.md) for Portainer REST API usage
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Container Reference](CONTAINERS.md) - Complete container profiles
|
||||
- [System Specifications](SYSTEM.md) - Hardware and software specs
|
||||
- [Shared Infrastructure Architecture](../architecture/SHARED_INFRASTRUCTURE_ARCHITECTURE.md) - Database/cache sharing
|
||||
- [Maintenance Scripts](scripts.md) - Automated maintenance tasks
|
||||
@@ -1,578 +0,0 @@
|
||||
# Home Server Container Platform Research
|
||||
|
||||
> Research Date: 2025-11-11
|
||||
> System: tower-of-joy (Zorin OS 16.3, Intel i7-6700, 16GB RAM, RTX 2080 Ti)
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document contains comprehensive research on open-source home server solutions for containerizing applications, web servers, file servers, Jellyfin media server, and cloud services like Nextcloud. The research evaluates platforms based on our specific hardware constraints and requirements.
|
||||
|
||||
### System Context
|
||||
|
||||
**Current Configuration:**
|
||||
- **OS**: Zorin OS 16.3 (Ubuntu 20.04 based)
|
||||
- **CPU**: Intel i7-6700 (4 cores, 8 threads, 3.40GHz)
|
||||
- **RAM**: 16 GB
|
||||
- **Storage**: 481 GB (365 GB available) - **LIMITED**
|
||||
- **GPU**: NVIDIA RTX 2080 Ti (11GB VRAM) - **EXCELLENT for transcoding**
|
||||
- **Docker**: 28.1.1 (already installed)
|
||||
- **User**: jpmschweitzer
|
||||
- **Hostname**: tower-of-joy
|
||||
|
||||
**Critical Constraints:**
|
||||
1. Limited storage (481GB) - Rules out storage-intensive solutions
|
||||
2. Existing OS installation - Prefer solutions that don't require fresh install
|
||||
3. RTX 2080 Ti excellent for Jellyfin hardware transcoding
|
||||
4. Docker already installed - Should leverage existing infrastructure
|
||||
|
||||
### Requirements
|
||||
|
||||
1. **Container orchestration** for running:
|
||||
- Jellyfin media server (with GPU hardware transcoding)
|
||||
- Nextcloud (cloud storage with external access)
|
||||
- File servers
|
||||
- Web servers
|
||||
- Various other containerized applications
|
||||
|
||||
2. **Web-based management interface** for container/service management
|
||||
|
||||
3. **NAS capabilities** (file storage and sharing)
|
||||
|
||||
4. **Software-defined networking** - Specifically Tailscale's OSS version (Headscale) or similar
|
||||
|
||||
5. **External access capabilities** (secure remote access)
|
||||
|
||||
6. **Easy extensibility** for adding more services
|
||||
|
||||
7. **GPU passthrough support** for Jellyfin hardware transcoding
|
||||
|
||||
---
|
||||
|
||||
## Solutions Evaluated
|
||||
|
||||
### 1. Portainer + Docker Compose ⭐ **RECOMMENDED**
|
||||
|
||||
**Overview:**
|
||||
Portainer provides a web-based management interface for Docker, allowing you to manage containers, stacks, images, and volumes through an intuitive UI. Combined with Docker Compose for multi-container orchestration.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ✅ **WORKS ON EXISTING UBUNTU/ZORIN OS**
|
||||
- No fresh install required
|
||||
- Installs as a Docker container itself
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐⭐ (9.6/10) | Intuitive dashboard, visual management, real-time monitoring |
|
||||
| Container/Docker Support | ⭐⭐⭐⭐⭐ | Native Docker integration, full Compose support, stack management |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐ (4/5) | Full NVIDIA support via Container Toolkit, GPU toggle in UI |
|
||||
| NAS/File Sharing | ⭐⭐⭐ (3/5) | Not built-in, easily added via Samba/NFS containers |
|
||||
| Headscale Integration | ⭐⭐⭐⭐⭐ | Excellent - both available as Docker containers |
|
||||
| Hardware Requirements | ⭐⭐⭐⭐⭐ | Minimal - perfect for 481GB storage constraint |
|
||||
| Learning Curve | ⭐⭐⭐⭐⭐ (EASY) | Rated 9.6/10 for ease of use, visual interface |
|
||||
| Community & Ecosystem | ⭐⭐⭐⭐⭐ | Massive Docker ecosystem, active community |
|
||||
| Extensibility | ⭐⭐⭐⭐⭐ | Add any Docker container via UI, custom stacks |
|
||||
|
||||
#### GPU Configuration Example
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
jellyfin:
|
||||
image: jellyfin/jellyfin:latest
|
||||
environment:
|
||||
- NVIDIA_VISIBLE_DEVICES=all
|
||||
- NVIDIA_DRIVER_CAPABILITIES=all
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
```
|
||||
|
||||
#### Pros & Cons
|
||||
|
||||
**PROS:**
|
||||
- ✅ Works on existing OS (no reinstall)
|
||||
- ✅ Minimal resource footprint (~200MB disk, <100MB RAM for Portainer)
|
||||
- ✅ Extremely easy to use (9.6/10 rating)
|
||||
- ✅ Full GPU support for Jellyfin
|
||||
- ✅ Already have Docker installed
|
||||
- ✅ Huge ecosystem of containers
|
||||
- ✅ Perfect for limited storage (481GB)
|
||||
- ✅ Quick setup (15-30 minutes)
|
||||
- ✅ Free and open source
|
||||
- ✅ Excellent for Jellyfin + Nextcloud + file servers
|
||||
|
||||
**CONS:**
|
||||
- ❌ NAS features require separate containers (not integrated)
|
||||
- ❌ No built-in RAID or advanced storage management
|
||||
- ❌ Less comprehensive than full NAS solutions
|
||||
- ❌ File sharing requires additional configuration
|
||||
|
||||
#### Expected Challenges
|
||||
|
||||
1. Setting up NVIDIA Container Toolkit (one-time setup)
|
||||
2. Configuring proper GPU permissions
|
||||
3. Learning Docker Compose syntax (minimal if using UI)
|
||||
4. Setting up reverse proxy for external access (Nginx/Caddy)
|
||||
|
||||
---
|
||||
|
||||
### 2. CasaOS - **BEST ALTERNATIVE**
|
||||
|
||||
**Overview:**
|
||||
CasaOS is a beautiful, app-store-like home server operating system that runs on top of existing Linux installations. Designed specifically for home users who want simplicity.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ✅ **INSTALLS ON EXISTING UBUNTU/ZORIN OS**
|
||||
- Single curl command: `curl -fsSL https://get.casaos.io | bash`
|
||||
- Auto-installs Docker if not present
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐⭐ (5/5) | Most elegant UI, app store paradigm, built-in file manager |
|
||||
| Container/Docker Support | ⭐⭐⭐⭐⭐ | Built on Docker, app store, recognizes existing containers |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐ (4/5) | NVIDIA support via environment variables |
|
||||
| NAS/File Sharing | ⭐⭐⭐⭐ (4/5) | Built-in file manager, easy network sharing |
|
||||
| Headscale Integration | ⭐⭐⭐⭐⭐ | Can install via Docker containers |
|
||||
| Hardware Requirements | ⭐⭐⭐⭐⭐ | Very light (~500MB for CasaOS) |
|
||||
| Learning Curve | ⭐⭐⭐⭐⭐ (EASIEST) | Absolute easiest solution, "click and go" |
|
||||
| Community & Ecosystem | ⭐⭐⭐⭐ (4/5) | Growing community, Docker ecosystem access |
|
||||
| Extensibility | ⭐⭐⭐⭐⭐ | Full Docker ecosystem, custom app import |
|
||||
|
||||
#### Pros & Cons
|
||||
|
||||
**PROS:**
|
||||
- ✅ Installs on existing OS
|
||||
- ✅ Absolutely beautiful UI
|
||||
- ✅ Easiest to use (perfect for beginners)
|
||||
- ✅ App store paradigm
|
||||
- ✅ Built-in file management
|
||||
- ✅ GPU support for Jellyfin
|
||||
- ✅ Minimal resources
|
||||
- ✅ One-command install
|
||||
- ✅ Can combine with Portainer
|
||||
|
||||
**CONS:**
|
||||
- ❌ Less granular control than Portainer
|
||||
- ❌ Newer/smaller community
|
||||
- ❌ May abstract away some Docker details
|
||||
- ❌ Advanced features require custom Docker configs
|
||||
|
||||
---
|
||||
|
||||
### 3. Cockpit + Podman
|
||||
|
||||
**Overview:**
|
||||
Cockpit is a web-based Linux server management tool with a Podman extension for container management. Podman is a daemonless Docker alternative.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ✅ Works on existing Ubuntu
|
||||
- Installs via apt package manager
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐ (4/5) | Clean, functional, less polished than alternatives |
|
||||
| Container/Docker Support | ⭐⭐⭐ (3/5) | Uses Podman (not Docker), compatibility issues |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐ (4/5) | NVIDIA support with Podman |
|
||||
| NAS/File Sharing | ⭐⭐ (2/5) | No built-in features |
|
||||
| Headscale Integration | ⭐⭐⭐⭐ | Available as Podman containers |
|
||||
| Hardware Requirements | ⭐⭐⭐⭐⭐ | Very lightweight |
|
||||
| Learning Curve | ⭐⭐⭐ (3/5 - MODERATE) | Requires learning Podman differences |
|
||||
| Community & Ecosystem | ⭐⭐⭐ (3/5) | Growing, smaller than Docker |
|
||||
| Extensibility | ⭐⭐⭐ (3/5) | Limited compared to Docker |
|
||||
|
||||
**Why Not Recommended:**
|
||||
- Not compatible with existing Docker setup
|
||||
- Smaller container ecosystem
|
||||
- Would require migration from Docker to Podman
|
||||
- Less intuitive than alternatives
|
||||
|
||||
---
|
||||
|
||||
### 4. K3s / MicroK8s (Lightweight Kubernetes)
|
||||
|
||||
**Overview:**
|
||||
Lightweight Kubernetes distributions designed for edge computing and resource-constrained environments.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ✅ Works on existing Ubuntu
|
||||
- k3s: Single binary installation
|
||||
- MicroK8s: Snap package (Ubuntu native)
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐ (3/5) | Less intuitive than Portainer |
|
||||
| Container/Docker Support | ⭐⭐⭐⭐ (4/5) | Uses containerd, complex deployment |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐⭐ | Excellent GPU support, NVIDIA operator |
|
||||
| NAS/File Sharing | ⭐⭐ (2/5) | No built-in features |
|
||||
| Headscale Integration | ⭐⭐⭐⭐ | Can run as pods |
|
||||
| Hardware Requirements | ⭐⭐⭐⭐ | 150-600MB RAM depending on distro |
|
||||
| Learning Curve | ⭐ (1/5 - STEEP) | Very steep, Kubernetes concepts required |
|
||||
| Community & Ecosystem | ⭐⭐⭐⭐⭐ | Massive Kubernetes ecosystem |
|
||||
| Extensibility | ⭐⭐⭐⭐⭐ | Unlimited, enterprise-grade |
|
||||
|
||||
**Why Not Recommended:**
|
||||
- Massive overkill for home server
|
||||
- Steep learning curve (weeks to months)
|
||||
- Complex for simple tasks
|
||||
- Use case doesn't need Kubernetes orchestration
|
||||
- More resource overhead than needed
|
||||
|
||||
---
|
||||
|
||||
### 5. TrueNAS Scale
|
||||
|
||||
**Overview:**
|
||||
Enterprise-grade NAS operating system based on Debian with built-in Kubernetes (K3s) for app deployment.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ❌ **REQUIRES FRESH INSTALL**
|
||||
- Not dual-boot friendly
|
||||
- Requires entire disk
|
||||
- Minimum 2 disks for storage functionality
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐⭐ | Excellent, comprehensive |
|
||||
| Container/Docker Support | ⭐⭐⭐ (3/5) | Uses K3s, more complex than Docker |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐ (4/5) | NVIDIA support in 24.10+, some RTX issues reported |
|
||||
| NAS/File Sharing | ⭐⭐⭐⭐⭐ | Best-in-class, ZFS, snapshots, replication |
|
||||
| Headscale Integration | ⭐⭐⭐ | Can deploy as K3s apps |
|
||||
| Hardware Requirements | ⭐⭐ (2/5) | Requires 2+ disks, storage-intensive |
|
||||
| Learning Curve | ⭐⭐⭐ (3/5 - MODERATE) | Storage concepts to learn |
|
||||
| Community & Ecosystem | ⭐⭐⭐⭐⭐ | Large community, enterprise backing |
|
||||
| Extensibility | ⭐⭐⭐⭐ | App catalog, K3s apps |
|
||||
|
||||
**Why Not Recommended:**
|
||||
- ❌ **REQUIRES FRESH INSTALL** (major dealbreaker)
|
||||
- ❌ Needs 2+ disks (we have 1)
|
||||
- ❌ 481GB too small for NAS + apps
|
||||
- ❌ Overkill for our needs
|
||||
- ❌ Would lose existing Zorin OS setup
|
||||
- ❌ Not suitable for our hardware configuration
|
||||
|
||||
---
|
||||
|
||||
### 6. Unraid
|
||||
|
||||
**Overview:**
|
||||
Popular NAS-focused OS with excellent Docker support and user-friendly interface. Known for flexible storage and parity protection.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ❌ **REQUIRES FRESH INSTALL**
|
||||
- Boots from USB drive
|
||||
- Takes over entire system
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐⭐ | Excellent, polished |
|
||||
| Container/Docker Support | ⭐⭐⭐⭐⭐ | Native Docker, Community Applications |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐⭐ | Excellent NVIDIA/AMD support |
|
||||
| NAS/File Sharing | ⭐⭐⭐⭐⭐ | Excellent, flexible array, parity protection |
|
||||
| Headscale Integration | ⭐⭐⭐⭐⭐ | Community containers, well-documented |
|
||||
| Hardware Requirements | ⭐⭐⭐ (3/5) | Works with single disk, benefits from multiple |
|
||||
| Learning Curve | ⭐⭐⭐⭐ (4/5 - EASY) | Very user-friendly |
|
||||
| Community & Ecosystem | ⭐⭐⭐⭐⭐ | Massive community, active forums |
|
||||
| Extensibility | ⭐⭐⭐⭐⭐ | Docker, VMs, plugins |
|
||||
|
||||
**Why Not Recommended (Currently):**
|
||||
- ❌ **REQUIRES FRESH INSTALL** (dealbreaker)
|
||||
- ❌ **NOT FREE** ($59-$129 license)
|
||||
- ❌ Would lose existing setup
|
||||
- ❌ Limited by 481GB storage
|
||||
- ❌ Boots from USB (uses a port)
|
||||
|
||||
**Note:** Best all-in-one solution if starting fresh with more storage. Consider for future rebuild.
|
||||
|
||||
---
|
||||
|
||||
### 7. Proxmox VE
|
||||
|
||||
**Overview:**
|
||||
Enterprise virtualization platform supporting VMs and LXC containers. Industry-standard for homelabs.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ❌ **REQUIRES FRESH INSTALL** (typically)
|
||||
- Can migrate existing Ubuntu to VM (complex)
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐⭐ | Professional, comprehensive |
|
||||
| Container/Docker Support | ⭐⭐⭐ (3/5) | LXC containers, not Docker directly |
|
||||
| GPU Passthrough | ⭐⭐⭐⭐⭐ | Excellent, well-documented |
|
||||
| NAS/File Sharing | ⭐⭐ (2/5) | No built-in, deploy as VM |
|
||||
| Headscale Integration | ⭐⭐⭐ | Can run in containers/VMs |
|
||||
| Hardware Requirements | ⭐⭐⭐ (3/5) | Virtualization overhead, 481GB limiting |
|
||||
| Learning Curve | ⭐⭐ (2/5 - STEEP) | Virtualization concepts required |
|
||||
| Community & Ecosystem | ⭐⭐⭐⭐⭐ | Huge community, enterprise support |
|
||||
| Extensibility | ⭐⭐⭐⭐⭐ | Maximum flexibility |
|
||||
|
||||
**Why Not Recommended:**
|
||||
- ❌ Requires fresh install
|
||||
- ❌ Overkill for our needs
|
||||
- ❌ Virtualization overhead
|
||||
- ❌ More complex than needed
|
||||
- ❌ Limited by 481GB storage
|
||||
- ❌ Not optimized for Docker
|
||||
|
||||
---
|
||||
|
||||
### 8. YunoHost
|
||||
|
||||
**Overview:**
|
||||
Debian-based server OS focused on simplifying self-hosting with pre-packaged applications.
|
||||
|
||||
**Installation Compatibility:**
|
||||
- ⚠️ Prefers fresh install
|
||||
- Can work on existing Debian/Ubuntu (risky)
|
||||
- May conflict with existing setup
|
||||
|
||||
#### Ratings
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|----------|--------|-------|
|
||||
| Web UI Quality | ⭐⭐⭐⭐ | Good application-focused UI |
|
||||
| Container/Docker Support | ⭐⭐ (2/5) | Docker support experimental/unofficial |
|
||||
| GPU Passthrough | ⭐ (1/5) | No specific support |
|
||||
| NAS/File Sharing | ⭐⭐⭐ | Basic file sharing |
|
||||
| Headscale Integration | ⭐⭐ | Would require manual setup |
|
||||
| Hardware Requirements | ⭐⭐⭐⭐ | Lightweight |
|
||||
| Learning Curve | ⭐⭐⭐⭐ | Easy for app installation |
|
||||
| Community & Ecosystem | ⭐⭐⭐ | Active, limited app catalog |
|
||||
| Extensibility | ⭐⭐ | Limited to YunoHost apps |
|
||||
|
||||
**Why Not Recommended:**
|
||||
- ❌ Poor Docker support
|
||||
- ❌ No GPU support
|
||||
- ❌ Not suitable for Jellyfin + Docker setup
|
||||
- ❌ Limited extensibility
|
||||
- ❌ Prefers fresh install
|
||||
|
||||
---
|
||||
|
||||
## Software-Defined Networking Solutions
|
||||
|
||||
### Headscale ⭐ **RECOMMENDED**
|
||||
|
||||
**Overview:**
|
||||
Open-source, self-hosted implementation of Tailscale control server. Fully compatible with Tailscale clients.
|
||||
|
||||
**Key Features:**
|
||||
- Self-hosted control plane
|
||||
- Use official Tailscale clients
|
||||
- ACL support
|
||||
- Pre-authenticated keys
|
||||
- Docker container available (`headscale/headscale`)
|
||||
|
||||
**Integration:**
|
||||
- ✅ Excellent Docker integration
|
||||
- Docker Compose deployment
|
||||
- Can share network to other containers
|
||||
- Well-documented setup
|
||||
|
||||
**PROS:**
|
||||
- ✅ Fully self-hosted
|
||||
- ✅ No external dependencies
|
||||
- ✅ Uses Tailscale clients
|
||||
- ✅ Free and open source
|
||||
- ✅ Active development
|
||||
- ✅ Easy Docker deployment
|
||||
|
||||
**CONS:**
|
||||
- ❌ Requires initial setup
|
||||
- ❌ Less polished than Tailscale SaaS
|
||||
- ❌ Self-managed (no cloud coordination)
|
||||
|
||||
---
|
||||
|
||||
### Tailscale (Official) - **SIMPLE ALTERNATIVE**
|
||||
|
||||
**Overview:**
|
||||
Commercial mesh VPN service with generous free tier (up to 100 devices, 3 users).
|
||||
|
||||
**PROS:**
|
||||
- ✅ Zero configuration
|
||||
- ✅ Excellent reliability
|
||||
- ✅ Free tier sufficient for home use
|
||||
- ✅ Better NAT traversal out of the box
|
||||
- ✅ Managed service
|
||||
|
||||
**CONS:**
|
||||
- ❌ Relies on external service
|
||||
- ❌ Privacy considerations (external control plane)
|
||||
- ❌ Free tier limits
|
||||
|
||||
---
|
||||
|
||||
### Nebula
|
||||
|
||||
**Overview:**
|
||||
Slack's open-source overlay network with built-in firewall capabilities.
|
||||
|
||||
**Key Differences:**
|
||||
- Certificate-based authentication
|
||||
- Built-in firewall (ACLs)
|
||||
- Lighthouse coordination servers
|
||||
- AES-256-GCM encryption
|
||||
|
||||
**Why Not Recommended:**
|
||||
- More complex setup
|
||||
- Smaller community than Tailscale/WireGuard
|
||||
- Less polished tooling
|
||||
- Steeper learning curve
|
||||
|
||||
---
|
||||
|
||||
### WireGuard
|
||||
|
||||
**Overview:**
|
||||
Modern, lightweight VPN protocol built into Linux kernel.
|
||||
|
||||
**PROS:**
|
||||
- ✅ Excellent performance (kernel-level)
|
||||
- ✅ Simple protocol
|
||||
- ✅ Widely supported
|
||||
- ✅ Very secure
|
||||
|
||||
**CONS:**
|
||||
- ❌ Point-to-point (not mesh)
|
||||
- ❌ Manual configuration for mesh networking
|
||||
- ❌ No built-in coordination
|
||||
- ❌ More setup required for home use
|
||||
|
||||
---
|
||||
|
||||
## Comparison Matrix
|
||||
|
||||
| Solution | Existing OS | Web UI | Docker | GPU | NAS | Learning Curve | Storage | Best For |
|
||||
|----------|------------|--------|--------|-----|-----|----------------|---------|----------|
|
||||
| **Portainer + Docker** | ✅ YES | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | **EASY** | Minimal | **Best Overall** |
|
||||
| **CasaOS** | ✅ YES | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | **EASIEST** | Minimal | Beginners |
|
||||
| **Cockpit + Podman** | ✅ YES | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | Moderate | Minimal | Linux admins |
|
||||
| **k3s/MicroK8s** | ✅ YES | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | **STEEP** | Low | Learning K8s |
|
||||
| **TrueNAS Scale** | ❌ NO | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Moderate | **HIGH** | NAS primary |
|
||||
| **Unraid** | ❌ NO | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Easy | Medium | Fresh install |
|
||||
| **Proxmox VE** | ❌ NO | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | **STEEP** | Medium | Virtualization |
|
||||
| **YunoHost** | ⚠️ Risky | ⭐⭐⭐⭐ | ⭐⭐ | ⭐ | ⭐⭐⭐ | Easy | Low | Not recommended |
|
||||
|
||||
---
|
||||
|
||||
## Final Recommendation: Portainer + Docker Compose
|
||||
|
||||
### Decision Factors
|
||||
|
||||
**Why Portainer Wins:**
|
||||
|
||||
1. ✅ **No OS Reinstall** - Works on existing Zorin OS
|
||||
2. ✅ **Leverages Existing Docker** - Already have Docker 28.1.1 installed
|
||||
3. ✅ **Minimal Storage Footprint** - Perfect for 481GB constraint
|
||||
4. ✅ **Full RTX 2080 Ti Support** - Excellent for Jellyfin hardware transcoding
|
||||
5. ✅ **Easy Learning Curve** - Rated 9.6/10 for ease of use
|
||||
6. ✅ **Massive Ecosystem** - Thousands of pre-built containers
|
||||
7. ✅ **Free and Open Source** - No licensing costs
|
||||
8. ✅ **Quick Setup** - 15-30 minutes to get running
|
||||
9. ✅ **Perfect for 16GB RAM / 481GB storage** - Minimal overhead
|
||||
10. ✅ **Excellent Headscale Integration** - Simple Docker deployment
|
||||
11. ✅ **Meets All Requirements** - Jellyfin, Nextcloud, file servers, web servers
|
||||
12. ✅ **Active Community** - Extensive support and documentation
|
||||
13. ✅ **Easy Extensibility** - Add services via web UI
|
||||
14. ✅ **Web UI for Everything** - No command-line required for basic tasks
|
||||
|
||||
### When This Might Not Be Right
|
||||
|
||||
- If you need enterprise NAS features (ZFS snapshots, replication)
|
||||
- If you want one-click app installation without any configuration (choose CasaOS)
|
||||
- If you need advanced RAID configurations
|
||||
- If you're planning major storage expansion (consider TrueNAS later)
|
||||
|
||||
### Alternative Consideration: CasaOS
|
||||
|
||||
**Choose CasaOS instead if:**
|
||||
- You want the absolute easiest experience
|
||||
- You prioritize beautiful UI over control
|
||||
- You're completely new to self-hosting
|
||||
- You want app-store simplicity
|
||||
- You can sacrifice some control for ease-of-use
|
||||
|
||||
**Note:** You can also run both - CasaOS will recognize existing Docker containers managed by Portainer.
|
||||
|
||||
---
|
||||
|
||||
## Networking Recommendation
|
||||
|
||||
**Primary Choice: Headscale**
|
||||
- Self-hosted Tailscale control server
|
||||
- Full privacy and control
|
||||
- Uses official Tailscale clients
|
||||
- Docker container deployment
|
||||
- No external dependencies
|
||||
|
||||
**Alternative: Tailscale Free Tier**
|
||||
- Zero configuration
|
||||
- Excellent reliability
|
||||
- Free for personal use (100 devices, 3 users)
|
||||
- Better NAT traversal out of the box
|
||||
- Managed service (less maintenance)
|
||||
|
||||
**Recommendation:** Start with Headscale for full control, fall back to Tailscale if setup is too complex.
|
||||
|
||||
---
|
||||
|
||||
## Resource Links
|
||||
|
||||
### Portainer + Docker Compose
|
||||
- Official Docs: https://docs.portainer.io/
|
||||
- GPU Configuration: Search "Portainer GPU passthrough Docker Compose"
|
||||
- Stack Templates: https://github.com/portainer/templates
|
||||
|
||||
### CasaOS
|
||||
- Official Site: https://casaos.io/
|
||||
- GitHub: https://github.com/IceWhaleTech/CasaOS
|
||||
- Community: https://community.zimaspace.com/
|
||||
|
||||
### Headscale
|
||||
- Official Docs: https://headscale.net/
|
||||
- GitHub: https://github.com/juanfont/headscale
|
||||
- Docker Setup: Check official documentation
|
||||
|
||||
### Jellyfin Hardware Transcoding
|
||||
- Official Docs: https://jellyfin.org/docs/general/administration/hardware-acceleration/
|
||||
- NVIDIA Guide: Jellyfin docs for NVIDIA-specific configuration
|
||||
- RTX 2080 Ti: Fully supported, handles multiple 4K transcodes
|
||||
|
||||
### NVIDIA Container Toolkit
|
||||
- Official Docs: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/
|
||||
- Ubuntu Setup: Follow NVIDIA's Ubuntu installation guide
|
||||
- Testing: Use nvidia-smi in containers to verify
|
||||
|
||||
### Docker Compose Examples
|
||||
- Awesome Docker: https://github.com/veggiemonk/awesome-docker
|
||||
- Compose Examples: https://github.com/docker/awesome-compose
|
||||
- Media Server Stacks: Search GitHub for "jellyfin nextcloud docker-compose"
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
Proceed to `implementation-plan.md` for detailed step-by-step implementation instructions with phases, tests, and validation checks.
|
||||
|
||||
---
|
||||
|
||||
*Research compiled from: TrueNAS community forums, Portainer documentation, CasaOS project, Jellyfin docs, NVIDIA Container Toolkit guides, Headscale documentation, Reddit homelab communities, and various technical blogs specializing in home server deployments (2024-2025)*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,290 @@
|
||||
# Core API Service
|
||||
|
||||
OpenAPI-compatible functions for Open WebUI and infrastructure management, providing web scraping, AI orchestration, and Portainer automation capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
### Web Scraper
|
||||
- Intelligent content extraction using Trafilatura
|
||||
- BeautifulSoup fallback for complex pages
|
||||
- Configurable content length limits
|
||||
- Optional link extraction
|
||||
- Perfect for feeding webpage content to LLMs
|
||||
|
||||
### Infrastructure Management
|
||||
- Portainer stack control (start/stop services)
|
||||
- Service status monitoring
|
||||
- Container health checks
|
||||
- Service group management
|
||||
- Read/write REST API
|
||||
|
||||
### AI Orchestration
|
||||
- OpenAI-compatible API endpoints
|
||||
- Model routing and management
|
||||
- Streaming responses
|
||||
- Function calling support
|
||||
- Multi-phase enhancement roadmap
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/
|
||||
├── config.py # Global application settings
|
||||
├── logging_config.py # Logging configuration
|
||||
├── base_schema.py # Base Pydantic models
|
||||
├── main.py # FastAPI application entry point
|
||||
└── modules/
|
||||
├── web_scraper/ # Web scraper module
|
||||
│ ├── config.py
|
||||
│ ├── schemas.py
|
||||
│ ├── service.py
|
||||
│ ├── router.py
|
||||
│ └── exceptions.py
|
||||
└── infrastructure/ # Infrastructure management
|
||||
├── config.py
|
||||
├── schemas.py
|
||||
├── service.py
|
||||
└── router.py
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Portainer Stack
|
||||
|
||||
1. Navigate to Portainer UI
|
||||
2. Go to **Stacks** → **Add Stack**
|
||||
3. Name: `core-api`
|
||||
4. Upload `stacks/core-api.yml` or paste contents
|
||||
5. Deploy
|
||||
|
||||
### Environment Variables
|
||||
|
||||
See `.env.example` in the service directory for all available configuration options.
|
||||
|
||||
Key variables:
|
||||
- `PORTAINER_URL` - Portainer API endpoint
|
||||
- `PORTAINER_API_KEY` - API key for Portainer authentication
|
||||
- `LOG_LEVEL` - Logging verbosity (DEBUG, INFO, WARNING, ERROR)
|
||||
- `CORS_ORIGINS` - Allowed CORS origins
|
||||
|
||||
## API Documentation
|
||||
|
||||
Once deployed, access documentation at:
|
||||
- **Swagger UI**: http://localhost:8083/docs
|
||||
- **ReDoc**: http://localhost:8083/redoc
|
||||
- **OpenAPI Spec**: http://localhost:8083/openapi.json
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Web Scraper
|
||||
|
||||
**POST /web-scraper/scrape**
|
||||
|
||||
Scrape and extract content from a website.
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"url": "https://example.com/article",
|
||||
"extract_main_content": true,
|
||||
"include_links": false,
|
||||
"max_length": 10000
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"url": "https://example.com/article",
|
||||
"title": "Article Title",
|
||||
"content": "Extracted article content...",
|
||||
"extracted_at": "2025-11-12T19:30:00Z",
|
||||
"content_length": 5432,
|
||||
"links": null
|
||||
}
|
||||
```
|
||||
|
||||
### Infrastructure Management
|
||||
|
||||
**GET /infrastructure/services**
|
||||
|
||||
List all Portainer stacks with status.
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "jellyfin",
|
||||
"status": "running",
|
||||
"containers": 1,
|
||||
"running_containers": 1
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**POST /infrastructure/services/{name}/start**
|
||||
|
||||
Start a service stack.
|
||||
|
||||
**POST /infrastructure/services/{name}/stop**
|
||||
|
||||
Stop a service stack.
|
||||
|
||||
**GET /infrastructure/service-groups**
|
||||
|
||||
Get service groupings and always-on services.
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"service_groups": {
|
||||
"jellyfin": ["jellyfin"],
|
||||
"nextcloud": ["nextcloud"],
|
||||
"ai-stack": ["open-webui", "ollama", "qdrant"]
|
||||
},
|
||||
"always_on": ["portainer", "nginx-proxy-manager", "core-api"]
|
||||
}
|
||||
```
|
||||
|
||||
### Health Check
|
||||
|
||||
**GET /health**
|
||||
|
||||
Service health check endpoint.
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"status": "healthy"
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Open WebUI
|
||||
|
||||
### Method 1: Functions (OpenAPI Import)
|
||||
1. In Open WebUI, navigate to Functions
|
||||
2. Import from OpenAPI spec: `http://localhost:8083/openapi.json`
|
||||
3. Use functions directly in chat
|
||||
|
||||
### Method 2: Pipelines
|
||||
1. Create a pipeline that calls Core API endpoints
|
||||
2. Use as data source for LLM workflows
|
||||
|
||||
### Method 3: Direct API Calls
|
||||
```python
|
||||
import httpx
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
"http://localhost:8083/web-scraper/scrape",
|
||||
json={
|
||||
"url": "https://example.com",
|
||||
"extract_main_content": True
|
||||
}
|
||||
)
|
||||
data = response.json()
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Requirements
|
||||
- Python 3.12+
|
||||
- Docker (for containerized deployment)
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Run locally
|
||||
uvicorn src.main:app --reload --host 0.0.0.0 --port 8083
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
```bash
|
||||
# Build image
|
||||
docker build -t core-api:latest .
|
||||
|
||||
# Run container
|
||||
docker run -p 8083:8083 core-api:latest
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
Logs are written to:
|
||||
- **Console**: stdout (captured by Docker)
|
||||
- **File**: `/app/logs/app.log` (persisted via volume mount)
|
||||
|
||||
Log format:
|
||||
```
|
||||
2025-11-12 19:30:00 | INFO | src.web_scraper.service:scrape_url:45 | Starting scrape for URL: https://example.com
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
- Runs as non-root user (uid 1000)
|
||||
- No authentication required (internal network only)
|
||||
- CORS configured for same-network access
|
||||
- Rate limiting: Not implemented (internal use only)
|
||||
- **Always-on service** - Cannot be stopped via infrastructure management
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
See [AI Orchestrator Plan](../../plans/active/ai-orchestrator-plan.md) for upcoming features:
|
||||
|
||||
### Phase 2: Memory Systems (In Progress)
|
||||
- Ephemeral, short-term, and long-term memory
|
||||
- Vector embeddings with Qdrant
|
||||
- Memory search and retrieval
|
||||
|
||||
### Phase 3: Multi-Model Management
|
||||
- Dynamic model routing
|
||||
- Cost optimization
|
||||
- Fallback strategies
|
||||
|
||||
### Phase 4: Reasoning & Chain-of-Thought
|
||||
- Structured reasoning
|
||||
- Multi-step problem solving
|
||||
- Verification and validation
|
||||
|
||||
### Phase 5: Agentic Workflows
|
||||
- Tool integration
|
||||
- Multi-agent orchestration
|
||||
- Autonomous task execution
|
||||
|
||||
### Phase 6: Production Optimization
|
||||
- Caching strategies
|
||||
- Performance tuning
|
||||
- Monitoring and metrics
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container won't start
|
||||
```bash
|
||||
docker logs core-api
|
||||
```
|
||||
|
||||
### API not responding
|
||||
```bash
|
||||
curl http://localhost:8083/health
|
||||
```
|
||||
|
||||
### Check OpenAPI spec
|
||||
```bash
|
||||
curl http://localhost:8083/openapi.json | jq
|
||||
```
|
||||
|
||||
### Portainer connection issues
|
||||
1. Verify `PORTAINER_URL` is correct
|
||||
2. Check `PORTAINER_API_KEY` is valid
|
||||
3. Ensure Portainer is accessible from core-api container
|
||||
4. Check Docker network connectivity
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Stacks Reference](../reference/stacks.md) - All Docker Compose stacks
|
||||
- [Automation Reference](../reference/AUTOMATION.md) - Portainer REST API details
|
||||
- [AI Orchestrator Plan](../../plans/active/ai-orchestrator-plan.md) - Feature roadmap
|
||||
- [Organizr Widget](organizr-widgets.md) - Service control UI integration
|
||||
@@ -0,0 +1,215 @@
|
||||
# Organizr Service Control Widget
|
||||
|
||||
A beautiful, responsive widget for managing on-demand services from your Organizr dashboard.
|
||||
|
||||
## Features
|
||||
|
||||
- ✨ **Real-time Status** - Live service status with container counts
|
||||
- 🎮 **One-Click Control** - Start/Stop services with a single click
|
||||
- 🔒 **Safety First** - Always-on services are protected and clearly marked
|
||||
- 🎨 **Beautiful UI** - Dark theme that matches Organizr
|
||||
- ⚡ **Auto-Refresh** - Updates every 10 seconds
|
||||
- 📱 **Responsive** - Works on desktop, tablet, and mobile
|
||||
|
||||
## Installation
|
||||
|
||||
### Method 1: Organizr Custom Homepage Item (Recommended)
|
||||
|
||||
1. **Copy the widget file** to a web-accessible location:
|
||||
```bash
|
||||
# If you have a web server serving files from /var/www/html:
|
||||
sudo cp organizr-widgets/service-control.html /var/www/html/widgets/
|
||||
|
||||
# Or use Organizr's public directory:
|
||||
cp organizr-widgets/service-control.html /path/to/organizr/plugins/widgets/
|
||||
```
|
||||
|
||||
2. **Add to Organizr Homepage**:
|
||||
- Open Organizr
|
||||
- Go to **Settings** → **Customize** → **Homepage Items**
|
||||
- Click **Add New Item**
|
||||
- Configure:
|
||||
- **Name**: "Service Control"
|
||||
- **Category**: Custom
|
||||
- **Type**: iFrame
|
||||
- **URL**: `http://localhost/widgets/service-control.html` (adjust path)
|
||||
- **Minimum Authentication**: User
|
||||
- **Enabled**: Yes
|
||||
- Save
|
||||
|
||||
3. **Add to Homepage**:
|
||||
- Go to **Settings** → **Customize** → **Appearance**
|
||||
- Edit your homepage layout
|
||||
- Add the "Service Control" item to desired location
|
||||
- Save
|
||||
|
||||
### Method 2: Organizr Custom HTML Tab
|
||||
|
||||
1. **Open Organizr Settings**:
|
||||
- Settings → **Tab Editor**
|
||||
|
||||
2. **Add New Tab**:
|
||||
- Click **Add Tab**
|
||||
- Configure:
|
||||
- **Tab Name**: "Services"
|
||||
- **Tab URL**: Leave empty
|
||||
- **Category**: Custom
|
||||
- **Type**: iFrame
|
||||
- **Image**: `images/tabs/services.png` (or your choice)
|
||||
|
||||
3. **Add Custom HTML**:
|
||||
- In the same tab configuration, find **Custom HTML** section
|
||||
- Copy and paste the entire contents of `service-control.html`
|
||||
- Save
|
||||
|
||||
4. **Access the Tab**:
|
||||
- The "Services" tab will now appear in your Organizr sidebar
|
||||
|
||||
### Method 3: Nginx Reverse Proxy Integration
|
||||
|
||||
If you want to serve the widget through Nginx Proxy Manager:
|
||||
|
||||
1. **Create a location** in your Organizr proxy host:
|
||||
```nginx
|
||||
location /widgets/ {
|
||||
alias /path/to/portainer-core/organizr-widgets/;
|
||||
autoindex off;
|
||||
}
|
||||
```
|
||||
|
||||
2. **Access via**: `https://your-organizr-domain.com/widgets/service-control.html`
|
||||
|
||||
## Configuration
|
||||
|
||||
### Changing API Endpoint
|
||||
|
||||
If your core-api is not on `localhost:8083`, edit the widget file:
|
||||
|
||||
```javascript
|
||||
const API_BASE = 'http://your-server:8083'; // Change this line
|
||||
```
|
||||
|
||||
### Adjusting Auto-Refresh Interval
|
||||
|
||||
Default is 10 seconds. To change:
|
||||
|
||||
```javascript
|
||||
setInterval(fetchServices, 10000); // Change 10000 to desired milliseconds
|
||||
```
|
||||
|
||||
### Customizing Displayed Services
|
||||
|
||||
By default, the widget shows all stoppable services (excludes always-on infrastructure).
|
||||
|
||||
To filter specific services, modify the `renderServices()` function:
|
||||
|
||||
```javascript
|
||||
const stoppableServices = services.filter(s =>
|
||||
!isAlwaysOn(s.name) &&
|
||||
['jellyfin', 'nextcloud', 'gitea', 'ai-stack'].includes(s.name) // Add this line
|
||||
);
|
||||
```
|
||||
|
||||
## Service Groups
|
||||
|
||||
The following service groups are defined (stopping one stops all in group):
|
||||
|
||||
- **jellyfin**: jellyfin
|
||||
- **nextcloud**: nextcloud (uses shared postgres-shared + redis-shared)
|
||||
- **gitea**: gitea, gitea-db
|
||||
- **ai-stack**: open-webui, ollama, qdrant
|
||||
- **samba**: samba
|
||||
|
||||
## Always-On Services (Cannot be stopped)
|
||||
|
||||
These infrastructure services are protected:
|
||||
- portainer
|
||||
- nginx-proxy-manager
|
||||
- core-api
|
||||
- uptime-kuma
|
||||
- organizr
|
||||
- headscale
|
||||
- watchtower
|
||||
- netdata
|
||||
- maintenance
|
||||
- postgres-shared (shared database infrastructure)
|
||||
- redis-shared (shared cache infrastructure)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Failed to connect to API"
|
||||
|
||||
**Problem**: Widget shows red error message
|
||||
|
||||
**Solutions**:
|
||||
1. Verify core-api is running: `docker ps | grep core-api`
|
||||
2. Check core-api URL is correct (localhost vs IP address)
|
||||
3. If accessing from remote, change `API_BASE` to full URL
|
||||
4. Check browser console for CORS errors
|
||||
|
||||
### CORS Issues
|
||||
|
||||
If accessing widget from a different domain than core-api:
|
||||
|
||||
**Option 1**: Update core-api CORS settings in `services/core-api/src/config.py`:
|
||||
```python
|
||||
cors_origins: list[str] = ["http://your-organizr-domain.com"]
|
||||
```
|
||||
|
||||
**Option 2**: Proxy the API through same domain using Nginx
|
||||
|
||||
### Services Not Appearing
|
||||
|
||||
**Check**:
|
||||
1. Services are deployed as Portainer stacks
|
||||
2. Services have proper labels: `com.docker.compose.project`
|
||||
3. Core-API can connect to Portainer
|
||||
4. Check browser console for errors
|
||||
|
||||
### Buttons Disabled
|
||||
|
||||
**Expected Behavior**:
|
||||
- Start button disabled when service is running
|
||||
- Stop button disabled when service is stopped
|
||||
- All buttons disabled for always-on services
|
||||
|
||||
## API Endpoints Used
|
||||
|
||||
The widget consumes these core-api endpoints:
|
||||
|
||||
- `GET /infrastructure/services` - Fetch service list with status
|
||||
- `GET /infrastructure/service-groups` - Fetch service groups and always-on list
|
||||
- `POST /infrastructure/services/{name}/start` - Start a service
|
||||
- `POST /infrastructure/services/{name}/stop` - Stop a service
|
||||
|
||||
See [Core API Documentation](core-api.md) for full API reference.
|
||||
|
||||
## Advanced Customization
|
||||
|
||||
### Colors
|
||||
|
||||
Edit the CSS variables in the `<style>` section:
|
||||
|
||||
```css
|
||||
.status-running {
|
||||
background: rgba(72, 187, 120, 0.2); /* Green background */
|
||||
color: #48bb78; /* Green text */
|
||||
}
|
||||
```
|
||||
|
||||
### Card Size
|
||||
|
||||
Adjust grid columns:
|
||||
|
||||
```css
|
||||
.service-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
/* Change 300px to make cards wider/narrower */
|
||||
}
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Core API Service](core-api.md) - Infrastructure management API
|
||||
- [Stacks Reference](../reference/stacks.md) - All deployed services
|
||||
- [Automation Reference](../reference/AUTOMATION.md) - Portainer REST API
|
||||
@@ -1,241 +0,0 @@
|
||||
# Unified Dashboard & External Access Strategy
|
||||
|
||||
> "One page to rule them all" - Unified interface for tower-of-joy services
|
||||
> Created: 2025-11-11
|
||||
|
||||
## Overview
|
||||
|
||||
This document defines the strategy for creating a unified web interface that provides access to all tower-of-joy services through a single page with tabbed navigation.
|
||||
|
||||
## Solution: Organizr + Nginx Proxy Manager
|
||||
|
||||
**Organizr** provides the unified tabbed interface
|
||||
**NPM** provides secure external access with SSL
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet
|
||||
↓
|
||||
[DNS: home.schweitz.net]
|
||||
↓
|
||||
[Router: Port Forward 443 → 192.168.86.149:443]
|
||||
↓
|
||||
[Nginx Proxy Manager: 443]
|
||||
↓
|
||||
[Organizr: 9999] ←→ [Service Tabs via iframe]
|
||||
├── Portainer (8001)
|
||||
├── Uptime Kuma (3001)
|
||||
├── Netdata (19999)
|
||||
├── Heimdall (8888)
|
||||
└── More services...
|
||||
```
|
||||
|
||||
## URL Pattern: Single Domain Approach
|
||||
|
||||
**Recommended Pattern:**
|
||||
```
|
||||
https://home.schweitz.net → Organizr unified interface
|
||||
```
|
||||
|
||||
**All services accessed through Organizr tabs:**
|
||||
- Click "Portainer" tab → loads in iframe
|
||||
- Click "Netdata" tab → loads in iframe
|
||||
- Click "Uptime Kuma" tab → loads in iframe
|
||||
|
||||
**Why this pattern?**
|
||||
- ✅ True "one page" experience
|
||||
- ✅ Single SSL certificate
|
||||
- ✅ Single URL to remember
|
||||
- ✅ Centralized authentication
|
||||
- ✅ Simple to maintain
|
||||
|
||||
## Alternative: Hybrid Subdomain Pattern
|
||||
|
||||
If some services need direct access (bypassing Organizr):
|
||||
|
||||
```
|
||||
https://home.schweitz.net → Organizr (main interface)
|
||||
https://portainer.home.schweitz.net → Direct Portainer access
|
||||
https://netdata.home.schweitz.net → Direct Netdata access
|
||||
```
|
||||
|
||||
**Requires:**
|
||||
- Wildcard DNS: `*.home.schweitz.net → 192.168.86.149`
|
||||
- Wildcard SSL cert OR individual certs per subdomain
|
||||
|
||||
## Service Configuration in Organizr
|
||||
|
||||
### Infrastructure Services (Primary Tabs)
|
||||
| Service | Internal URL | Tab Name | Notes |
|
||||
|---------|-------------|----------|-------|
|
||||
| **Portainer** | http://192.168.86.149:8001 | Portainer | Container management |
|
||||
| **Uptime Kuma** | http://192.168.86.149:3001 | Uptime | Service monitoring |
|
||||
| **Netdata** | http://192.168.86.149:19999 | Metrics | System metrics |
|
||||
| **Heimdall** | http://192.168.86.149:8888 | Dashboard | Alternative launcher |
|
||||
|
||||
### Optional Services (Additional Tabs)
|
||||
| Service | Internal URL | Tab Name | Expose? |
|
||||
|---------|-------------|----------|---------|
|
||||
| **NPM Admin** | http://192.168.86.149:81 | NPM | Admin only - local access |
|
||||
| **Headscale** | http://192.168.86.149:8085 | VPN | Admin only |
|
||||
| **Ollama** | http://192.168.86.149:11434 | AI | API only, no UI |
|
||||
|
||||
### Future Application Services
|
||||
| Service | Internal URL | Tab Name | Notes |
|
||||
|---------|-------------|----------|-------|
|
||||
| **Jellyfin** | http://192.168.86.149:8096 | Media | GPU transcoding |
|
||||
| **Nextcloud** | http://192.168.86.149:8082 | Cloud | File storage |
|
||||
|
||||
## Iframe Embedding Challenges
|
||||
|
||||
### Known Issues
|
||||
|
||||
Some services block iframe embedding via `X-Frame-Options` header:
|
||||
- **Netdata**: Can be configured to allow embedding
|
||||
- **Portainer**: May require configuration
|
||||
- **Uptime Kuma**: Generally works fine
|
||||
|
||||
### Solutions
|
||||
|
||||
**Option 1: Configure services to allow embedding**
|
||||
Add to docker-compose environment:
|
||||
```yaml
|
||||
environment:
|
||||
- X_FRAME_OPTIONS=SAMEORIGIN # Allow same-origin iframes
|
||||
```
|
||||
|
||||
**Option 2: NPM header manipulation**
|
||||
Configure NPM to strip/modify headers for internal access
|
||||
|
||||
**Option 3: Organizr "direct link" mode**
|
||||
Services that don't work in iframes can open in new tab
|
||||
|
||||
## Security Layers
|
||||
|
||||
### Level 1: External Access (NPM)
|
||||
- HTTPS with Let's Encrypt SSL
|
||||
- External port 443 only
|
||||
- DDoS protection via Cloudflare (optional)
|
||||
|
||||
### Level 2: Application Authentication (Organizr)
|
||||
- User authentication in Organizr
|
||||
- Role-based access control
|
||||
- SSO integration (optional)
|
||||
|
||||
### Level 3: Service-Level Authentication
|
||||
- Each service keeps its own auth
|
||||
- Organizr can pass auth tokens (for supported services)
|
||||
|
||||
### Level 4: Network Security (Headscale)
|
||||
- VPN access for sensitive admin tools
|
||||
- Public: Jellyfin, Nextcloud
|
||||
- Private (VPN only): Portainer, NPM, Netdata
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Deploy Organizr
|
||||
```bash
|
||||
make deploy-organizr
|
||||
```
|
||||
|
||||
### Phase 2: Configure Organizr
|
||||
1. Access http://192.168.86.149:9999
|
||||
2. Complete setup wizard
|
||||
3. Create admin user
|
||||
4. Add tabs for each service
|
||||
|
||||
### Phase 3: Configure NPM for External Access
|
||||
1. Access NPM admin: http://192.168.86.149:81
|
||||
2. Add proxy host:
|
||||
- Domain: `home.schweitz.net`
|
||||
- Forward to: `192.168.86.149:9999`
|
||||
- Enable SSL with Let's Encrypt
|
||||
- Force HTTPS redirect
|
||||
|
||||
### Phase 4: Configure Router Port Forwarding
|
||||
```
|
||||
External Port 443 → Internal 192.168.86.149:443 (NPM HTTPS)
|
||||
External Port 80 → Internal 192.168.86.149:80 (NPM HTTP redirect)
|
||||
```
|
||||
|
||||
### Phase 5: DNS Configuration
|
||||
Point `home.schweitz.net` to your public IP
|
||||
|
||||
### Phase 6: Test & Secure
|
||||
- Test external access: https://home.schweitz.net
|
||||
- Verify SSL certificate
|
||||
- Test all service tabs
|
||||
- Configure Organizr authentication
|
||||
- Review security settings
|
||||
|
||||
## Service Tab Recommendations
|
||||
|
||||
### Homepage Tab
|
||||
- Quick status dashboard
|
||||
- Links to most-used services
|
||||
- System health indicators
|
||||
|
||||
### Essential Tabs (Always Visible)
|
||||
- Portainer (container management)
|
||||
- Uptime Kuma (monitoring)
|
||||
- Netdata (metrics)
|
||||
|
||||
### Application Tabs (After deployment)
|
||||
- Jellyfin (media)
|
||||
- Nextcloud (files)
|
||||
|
||||
### Admin Tabs (Restricted)
|
||||
- NPM (reverse proxy config)
|
||||
- Headscale (VPN management)
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Adding New Services
|
||||
1. Deploy service via Portainer/Docker Compose
|
||||
2. Add tab in Organizr settings
|
||||
3. Test iframe embedding
|
||||
4. Update this documentation
|
||||
|
||||
### SSL Certificate Renewal
|
||||
- Automatic via Let's Encrypt (NPM handles this)
|
||||
- Check NPM dashboard for expiry dates
|
||||
|
||||
### Security Updates
|
||||
- Watchtower auto-updates containers (Phase 4)
|
||||
- Review Organizr user access monthly
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Service won't load in iframe
|
||||
**Problem:** `X-Frame-Options` header blocking
|
||||
**Solution:** Configure service to allow embedding, or use "open in new tab" mode
|
||||
|
||||
### External access not working
|
||||
**Check:**
|
||||
1. Router port forwarding configured (443 → 192.168.86.149:443)
|
||||
2. DNS pointing to correct public IP
|
||||
3. NPM proxy host configured correctly
|
||||
4. SSL certificate generated successfully
|
||||
|
||||
### Authentication issues
|
||||
**Check:**
|
||||
1. Organizr user permissions
|
||||
2. Service-specific authentication (each service has own login)
|
||||
3. Consider implementing SSO for seamless experience
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Potential Upgrades
|
||||
- **Authelia**: Centralized authentication with 2FA
|
||||
- **Cloudflare Tunnel**: Avoid port forwarding entirely
|
||||
- **Custom Theme**: Brand Organizr to match preferences
|
||||
- **API Integration**: Show live stats in Organizr homepage
|
||||
|
||||
---
|
||||
|
||||
**Next Steps:**
|
||||
1. Deploy Organizr: `make deploy-organizr`
|
||||
2. Configure tabs for existing services
|
||||
3. Set up NPM proxy for external access
|
||||
4. Test the unified interface
|
||||
Reference in New Issue
Block a user