homelab/docs/guides/secrets-management.md
Claude 4adaa8e8be
docs: Add comprehensive documentation for homelab setup and operations
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!**
2025-11-06 19:32:10 +00:00

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:

  1. What the secret is for
  2. How to generate it
  3. 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_SECRET
  • VIKUNJA_SERVICE_JWTSECRET
  • NEXTAUTH_SECRET
  • SESSION_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_PASS
  • LDAP_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 .env file
  • 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

  1. Start LLDAP and Tinyauth
  2. Access protected service (e.g., https://tasks.fig.systems)
  3. Should redirect to auth.fig.systems
  4. Login with LLDAP credentials
  5. 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:

  1. Deploy services
  2. Configure SSO
  3. Set up backups
  4. Store secrets in password manager
  5. 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! 🔐