This commit adds extensive documentation covering all aspects of homelab setup,
configuration, and troubleshooting.
## Documentation Structure
### Main Documentation
- **docs/README.md**: Documentation hub with table of contents
- **docs/getting-started.md**: Complete setup guide from scratch
- **docs/quick-reference.md**: Fast reference for common tasks and commands
### Configuration Guides (docs/guides/)
- **secrets-management.md**: Environment variables and secrets configuration
- How to generate secure secrets
- Service-specific configuration
- Automated secret generation scripts
- Security best practices
- Common mistakes to avoid
- **gpu-setup.md**: NVIDIA GTX 1070 GPU acceleration setup
- Specific to Proxmox 9 on Debian 13
- Complete passthrough configuration
- Jellyfin hardware transcoding setup
- Immich ML inference acceleration
- Performance tuning and benchmarks
- Troubleshooting GPU issues
### Troubleshooting (docs/troubleshooting/)
- **faq.md**: Frequently asked questions (60+ Q&A)
- General questions about the homelab
- Setup and configuration questions
- SSL/TLS and SSO questions
- Service-specific questions
- Security and backup questions
- Performance optimization
- **common-issues.md**: Common problems and solutions
- Service startup failures
- SSL certificate errors
- SSO authentication issues
- Access problems
- Performance issues
- Database errors
- Network issues
- GPU problems
### Services (docs/services/)
- **README.md**: Complete service overview
- All 20 services with descriptions
- Use cases for each service
- Resource requirements
- Deployment checklists
- Service dependencies
- Minimum viable setups
## Key Features
### Environment-Specific
All GPU documentation is specific to:
- **Platform**: Proxmox 9 (PVE)
- **OS**: Debian 13
- **GPU**: NVIDIA GTX 1070 (Pascal)
- Includes Proxmox-specific GPU passthrough
- VM guest setup on Debian 13
- NVIDIA Container Toolkit configuration
### Comprehensive Coverage
- 60+ FAQs answered
- 50+ common issues documented
- 100+ command examples
- Step-by-step procedures
- Troubleshooting decision trees
- Quick reference tables
### Practical Examples
- Actual command outputs
- Real-world scenarios
- Copy-paste ready commands
- Configuration file examples
- Debugging procedures
## Documentation Highlights
### Getting Started Guide
- Prerequisites checklist
- Docker installation
- Media directory setup
- DNS configuration
- Environment variable setup
- Service deployment order
- Initial service configuration
- Verification procedures
### Secrets Management
- Secret type identification
- Generation commands for each type
- Service-specific requirements
- Automated generation script
- Password manager integration
- Backup procedures
- Security best practices
- Common mistakes
### GPU Setup (Proxmox/Debian/GTX 1070)
- IOMMU enablement
- VFIO configuration
- PCI passthrough to VM
- NVIDIA driver installation on Debian 13
- Container toolkit setup
- Jellyfin NVENC configuration
- Immich CUDA acceleration
- Performance benchmarks
- NVENC stream limit unlock
- Monitoring and tuning
### Quick Reference
- All service URLs
- Common Docker Compose commands
- System check commands
- Secret generation commands
- Troubleshooting steps
- File locations
- Port reference
- Emergency procedures
### FAQ
Covers questions about:
- Hardware requirements
- Domain requirements
- Cost estimates
- Setup procedures
- Configuration details
- SSL certificates
- SSO authentication
- Service-specific issues
- Backup strategies
- Performance optimization
- Security considerations
### Common Issues
Solutions for:
- Container startup failures
- Environment variable errors
- Port conflicts
- Permission issues
- SSL certificate problems
- DNS issues
- SSO login failures
- Database connections
- Network connectivity
- GPU detection
- Resource constraints
### Services Overview
- Detailed description of all 20 services
- Use cases and features
- Required vs optional services
- Resource requirements by tier
- Service dependencies diagram
- Deployment checklists
- "When to use" guidance
## File Structure
```
docs/
├── README.md # Documentation hub
├── getting-started.md # Setup walkthrough
├── quick-reference.md # Command reference
├── guides/
│ ├── secrets-management.md # Secrets configuration
│ └── gpu-setup.md # GPU acceleration (GTX 1070)
├── troubleshooting/
│ ├── faq.md # 60+ FAQs
│ └── common-issues.md # Problem solving
└── services/
└── README.md # Service overview
```
## Benefits
### For New Users
- Clear setup path from zero to running services
- Explains "why" not just "how"
- Common pitfalls documented and avoided
- Example configurations provided
### For Experienced Users
- Quick reference for commands
- Troubleshooting decision trees
- Performance tuning guides
- Advanced configurations
### For Maintenance
- Update procedures
- Backup and restore
- Monitoring guidelines
- Security hardening
## Documentation Standards
- Clear, concise writing
- Code blocks with syntax highlighting
- Examples with expected output
- Warning and tip callouts
- Cross-references between docs
- Tested commands and procedures
## Next Steps
Users should:
1. Start with getting-started.md
2. Configure secrets using secrets-management.md
3. Enable GPU if available (gpu-setup.md)
4. Use quick-reference.md for daily operations
5. Refer to faq.md and common-issues.md when stuck
---
**This documentation makes the homelab accessible to users of all skill levels!**
13 KiB
Secrets and Environment Variables Management
This guide explains how to properly configure and manage secrets in your homelab.
Overview
Every service uses environment variables stored in .env files for configuration. This approach:
- ✅ Keeps secrets out of version control
- ✅ Makes configuration changes easy
- ✅ Follows Docker Compose best practices
- ✅ Provides clear examples of what each secret should look like
Finding What Needs Configuration
Search for Placeholder Values
All secrets that need changing are marked with changeme_:
# Find all files with placeholder secrets
grep -r "changeme_" ~/homelab/compose
# Output shows exactly what needs updating:
compose/core/lldap/.env:LLDAP_LDAP_USER_PASS=changeme_please_set_secure_password
compose/core/lldap/.env:LLDAP_JWT_SECRET=changeme_please_set_random_secret
compose/core/tinyauth/.env:LDAP_BIND_PASSWORD=changeme_please_set_secure_password
...
Count What's Left to Configure
# Count how many secrets still need updating
grep -r "changeme_" ~/homelab/compose | wc -l
# Goal: 0
Generating Secrets
Each .env file includes comments showing:
- What the secret is for
- How to generate it
- What format it should be in
Common Secret Types
1. JWT Secrets (64 characters)
Used by: LLDAP, Vikunja, NextAuth
Generate:
openssl rand -hex 32
Example output:
a1b2c3d4e5f67890abcdef1234567890a1b2c3d4e5f67890abcdef1234567890
Where to use:
LLDAP_JWT_SECRETVIKUNJA_SERVICE_JWTSECRETNEXTAUTH_SECRETSESSION_SECRET
2. Database Passwords (32 alphanumeric)
Used by: Postgres, Immich, Vikunja, Linkwarden
Generate:
openssl rand -base64 32 | tr -d /=+ | cut -c1-32
Example output:
aB3dEf7HiJ9kLmN2oPqR5sTuV8wXyZ1
Where to use:
DB_PASSWORD(Immich)POSTGRES_PASSWORD(Vikunja, Linkwarden)VIKUNJA_DATABASE_PASSWORD
3. Strong Passwords (16+ characters, mixed)
Used by: LLDAP admin, service admin accounts
Generate:
# Option 1: Using pwgen (install: apt install pwgen)
pwgen -s 20 1
# Option 2: Using openssl
openssl rand -base64 20 | tr -d /=+
# Option 3: Manual (recommended for main admin password)
# Create something memorable but strong
# Example format: MyS3cur3P@ssw0rd!2024#HomeL@b
Where to use:
LLDAP_LDAP_USER_PASSLDAP_BIND_PASSWORD(must match LLDAP_LDAP_USER_PASS!)
4. API Keys / Master Keys (32 characters)
Used by: Meilisearch, various APIs
Generate:
openssl rand -hex 16
Example output:
f6g7h8i901234abcdef567890a1b2c3d
Where to use:
MEILI_MASTER_KEY
Service-Specific Configuration
Core Services
LLDAP (compose/core/lldap/.env)
# Edit the file
cd ~/homelab/compose/core/lldap
nano .env
Required secrets:
# Admin password - use a STRONG password you'll remember
# Example: MyS3cur3P@ssw0rd!2024#HomeL@b
LLDAP_LDAP_USER_PASS=changeme_please_set_secure_password
# JWT secret - generate with: openssl rand -hex 32
# Example: a1b2c3d4e5f67890abcdef1234567890a1b2c3d4e5f67890abcdef1234567890
LLDAP_JWT_SECRET=changeme_please_set_random_secret
Generate and update:
# Generate JWT secret
echo "LLDAP_JWT_SECRET=$(openssl rand -hex 32)"
# Choose a strong password for LLDAP_LDAP_USER_PASS
# Write it down - you'll need it for Tinyauth too!
Tinyauth (compose/core/tinyauth/.env)
cd ~/homelab/compose/core/tinyauth
nano .env
Required secrets:
# MUST match LLDAP_LDAP_USER_PASS from lldap/.env
LDAP_BIND_PASSWORD=changeme_please_set_secure_password
# Session secret - generate with: openssl rand -hex 32
SESSION_SECRET=changeme_please_set_random_session_secret
⚠️ CRITICAL: LDAP_BIND_PASSWORD must exactly match LLDAP_LDAP_USER_PASS!
# Generate session secret
echo "SESSION_SECRET=$(openssl rand -hex 32)"
Media Services
Immich (compose/media/frontend/immich/.env)
cd ~/homelab/compose/media/frontend/immich
nano .env
Required secrets:
# Database password - generate with: openssl rand -base64 32 | tr -d /=+ | cut -c1-32
DB_PASSWORD=changeme_please_set_secure_password
# Generate
echo "DB_PASSWORD=$(openssl rand -base64 32 | tr -d /=+ | cut -c1-32)"
Utility Services
Linkwarden (compose/services/linkwarden/.env)
cd ~/homelab/compose/services/linkwarden
nano .env
Required secrets:
# NextAuth secret - generate with: openssl rand -hex 32
NEXTAUTH_SECRET=changeme_please_set_random_secret_key
# Postgres password - generate with: openssl rand -base64 32 | tr -d /=+ | cut -c1-32
POSTGRES_PASSWORD=changeme_please_set_secure_postgres_password
# Meilisearch master key - generate with: openssl rand -hex 16
MEILI_MASTER_KEY=changeme_please_set_meili_master_key
# Generate all three
echo "NEXTAUTH_SECRET=$(openssl rand -hex 32)"
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -d /=+ | cut -c1-32)"
echo "MEILI_MASTER_KEY=$(openssl rand -hex 16)"
Vikunja (compose/services/vikunja/.env)
cd ~/homelab/compose/services/vikunja
nano .env
Required secrets:
# Database password (used in two places - must match!)
VIKUNJA_DATABASE_PASSWORD=changeme_please_set_secure_password
POSTGRES_PASSWORD=changeme_please_set_secure_password # Same value!
# JWT secret - generate with: openssl rand -hex 32
VIKUNJA_SERVICE_JWTSECRET=changeme_please_set_random_jwt_secret
⚠️ CRITICAL: Both password fields must match!
# Generate
DB_PASS=$(openssl rand -base64 32 | tr -d /=+ | cut -c1-32)
echo "VIKUNJA_DATABASE_PASSWORD=$DB_PASS"
echo "POSTGRES_PASSWORD=$DB_PASS"
echo "VIKUNJA_SERVICE_JWTSECRET=$(openssl rand -hex 32)"
Automated Configuration Script
Create a script to generate all secrets at once:
#!/bin/bash
# save as: ~/homelab/generate-secrets.sh
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${YELLOW}Homelab Secrets Generator${NC}\n"
echo "This script will help you generate secure secrets for your homelab."
echo "You'll need to manually copy these values into the respective .env files."
echo ""
# LLDAP
echo -e "${GREEN}=== LLDAP (compose/core/lldap/.env) ===${NC}"
echo "LLDAP_JWT_SECRET=$(openssl rand -hex 32)"
echo "LLDAP_LDAP_USER_PASS=<choose-a-strong-password-manually>"
echo ""
# Tinyauth
echo -e "${GREEN}=== Tinyauth (compose/core/tinyauth/.env) ===${NC}"
echo "LDAP_BIND_PASSWORD=<same-as-LLDAP_LDAP_USER_PASS-above>"
echo "SESSION_SECRET=$(openssl rand -hex 32)"
echo ""
# Immich
echo -e "${GREEN}=== Immich (compose/media/frontend/immich/.env) ===${NC}"
echo "DB_PASSWORD=$(openssl rand -base64 32 | tr -d /=+ | cut -c1-32)"
echo ""
# Linkwarden
echo -e "${GREEN}=== Linkwarden (compose/services/linkwarden/.env) ===${NC}"
echo "NEXTAUTH_SECRET=$(openssl rand -hex 32)"
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -d /=+ | cut -c1-32)"
echo "MEILI_MASTER_KEY=$(openssl rand -hex 16)"
echo ""
# Vikunja
VIKUNJA_PASS=$(openssl rand -base64 32 | tr -d /=+ | cut -c1-32)
echo -e "${GREEN}=== Vikunja (compose/services/vikunja/.env) ===${NC}"
echo "VIKUNJA_DATABASE_PASSWORD=$VIKUNJA_PASS"
echo "POSTGRES_PASSWORD=$VIKUNJA_PASS # Must match above!"
echo "VIKUNJA_SERVICE_JWTSECRET=$(openssl rand -hex 32)"
echo ""
echo -e "${YELLOW}Done! Copy these values into your .env files.${NC}"
echo ""
echo "Don't forget to:"
echo "1. Choose a strong LLDAP_LDAP_USER_PASS manually"
echo "2. Use the same password for LDAP_BIND_PASSWORD in tinyauth"
echo "3. Save all secrets in a password manager"
Usage:
chmod +x ~/homelab/generate-secrets.sh
~/homelab/generate-secrets.sh > secrets.txt
# Review and copy secrets
cat secrets.txt
# Keep this file safe or delete after copying to .env files
Security Best Practices
1. Use a Password Manager
Store all secrets in a password manager:
- 1Password: Great for teams
- Bitwarden: Self-hostable option
- KeePassXC: Offline, open-source
Create an entry for each service with:
- Service name
- URL
- All secrets from
.envfile - Admin credentials
2. Never Commit Secrets
The repository .gitignore already excludes .env files, but double-check:
# Verify .env files are ignored
git status
# Should NOT show any .env files
3. Backup Your Secrets
# Create encrypted backup of all .env files
cd ~/homelab
tar czf env-backup-$(date +%Y%m%d).tar.gz $(find compose -name ".env")
# Encrypt with GPG
gpg -c env-backup-$(date +%Y%m%d).tar.gz
# Store encrypted file safely
mv env-backup-*.tar.gz.gpg ~/backups/
# Delete unencrypted tar
rm env-backup-*.tar.gz
4. Rotate Secrets Regularly
Change critical secrets periodically:
- Admin passwords: Every 90 days
- JWT secrets: Every 180 days
- Database passwords: When personnel changes
5. Limit Secret Access
- Don't share raw secrets over email/chat
- Use password manager's sharing features
- Delete shared secrets when no longer needed
Verification
Check All Secrets Are Set
# Should return 0 (no changeme_ values left)
grep -r "changeme_" ~/homelab/compose | wc -l
Test Service Startup
# Start a service and check for password errors
cd ~/homelab/compose/core/lldap
docker compose up -d
docker compose logs
# Should NOT see:
# - "invalid password"
# - "authentication failed"
# - "secret not set"
Verify SSO Works
- Start LLDAP and Tinyauth
- Access protected service (e.g., https://tasks.fig.systems)
- Should redirect to auth.fig.systems
- Login with LLDAP credentials
- Should redirect back to service
If this works, your LLDAP ↔ Tinyauth passwords match! ✅
Common Mistakes
❌ Using Weak Passwords
Don't:
LLDAP_LDAP_USER_PASS=password123
Do:
LLDAP_LDAP_USER_PASS=MyS3cur3P@ssw0rd!2024#HomeL@b
❌ Mismatched Passwords
Don't:
# In lldap/.env
LLDAP_LDAP_USER_PASS=password1
# In tinyauth/.env
LDAP_BIND_PASSWORD=password2 # Different!
Do:
# In lldap/.env
LLDAP_LDAP_USER_PASS=MyS3cur3P@ssw0rd!2024#HomeL@b
# In tinyauth/.env
LDAP_BIND_PASSWORD=MyS3cur3P@ssw0rd!2024#HomeL@b # Same!
❌ Using Same Secret Everywhere
Don't:
# Same secret in multiple places
LLDAP_JWT_SECRET=abc123
NEXTAUTH_SECRET=abc123
SESSION_SECRET=abc123
Do:
# Unique secret for each
LLDAP_JWT_SECRET=a1b2c3d4e5f67890...
NEXTAUTH_SECRET=f6g7h8i9j0k1l2m3...
SESSION_SECRET=x9y8z7w6v5u4t3s2...
❌ Forgetting to Update Both Password Fields
In Vikunja .env, both must match:
# Both must be the same!
VIKUNJA_DATABASE_PASSWORD=aB3dEf7HiJ9kLmN2oPqR5sTuV8wXyZ1
POSTGRES_PASSWORD=aB3dEf7HiJ9kLmN2oPqR5sTuV8wXyZ1
Troubleshooting
"Authentication failed" in Tinyauth
Cause: LDAP_BIND_PASSWORD doesn't match LLDAP_LDAP_USER_PASS
Fix:
# Check LLDAP password
grep LLDAP_LDAP_USER_PASS ~/homelab/compose/core/lldap/.env
# Check Tinyauth password
grep LDAP_BIND_PASSWORD ~/homelab/compose/core/tinyauth/.env
# They should be identical!
"Invalid JWT" errors
Cause: JWT_SECRET is too short or invalid format
Fix:
# Regenerate with proper length
openssl rand -hex 32
# Update in .env file
"Database connection failed"
Cause: Database password mismatch
Fix:
# Check both password fields match
grep -E "(POSTGRES_PASSWORD|DATABASE_PASSWORD)" compose/services/vikunja/.env
# Both should be identical
Next Steps
Once all secrets are configured:
- ✅ Deploy services
- ✅ Configure SSO
- ✅ Set up backups
- ✅ Store secrets in password manager
- ✅ Create encrypted backup of .env files
Reference
Quick Command Reference
# Generate 64-char hex
openssl rand -hex 32
# Generate 32-char password
openssl rand -base64 32 | tr -d /=+ | cut -c1-32
# Generate 32-char hex
openssl rand -hex 16
# Find all changeme_ values
grep -r "changeme_" compose/
# Count remaining secrets to configure
grep -r "changeme_" compose/ | wc -l
# Backup all .env files (encrypted)
tar czf env-files.tar.gz $(find compose -name ".env")
gpg -c env-files.tar.gz
Secret Types Quick Reference
| Secret Type | Command | Example Length | Used By |
|---|---|---|---|
| JWT Secret | openssl rand -hex 32 |
64 chars | LLDAP, Vikunja, NextAuth |
| Session Secret | openssl rand -hex 32 |
64 chars | Tinyauth |
| DB Password | openssl rand -base64 32 | tr -d /=+ | cut -c1-32 |
32 chars | Postgres, Immich |
| API Key | openssl rand -hex 16 |
32 chars | Meilisearch |
| Admin Password | Manual | 16+ chars | LLDAP admin |
Remember: Strong, unique secrets are your first line of defense. Take the time to generate them properly! 🔐