Merge pull request #2 from efigueroa/claude/gitops-home-services-011CUqEzDETA2BqAzYUcXtjt
feat: Add service template, backup solution, dashboard, and IaC tooling
This commit is contained in:
commit
312144f37e
16 changed files with 2480 additions and 0 deletions
|
|
@ -32,6 +32,8 @@ compose/
|
||||||
│ ├── sabnzbd/ # Usenet downloader
|
│ ├── sabnzbd/ # Usenet downloader
|
||||||
│ └── qbittorrent/# Torrent client
|
│ └── qbittorrent/# Torrent client
|
||||||
└── services/ # Utility services
|
└── services/ # Utility services
|
||||||
|
├── homarr/ # Dashboard (home.fig.systems)
|
||||||
|
├── backrest/ # Backup manager (backup.fig.systems)
|
||||||
├── linkwarden/ # Bookmark manager (links.fig.systems)
|
├── linkwarden/ # Bookmark manager (links.fig.systems)
|
||||||
├── vikunja/ # Task management (tasks.fig.systems)
|
├── vikunja/ # Task management (tasks.fig.systems)
|
||||||
├── lubelogger/ # Vehicle tracker (garage.fig.systems)
|
├── lubelogger/ # Vehicle tracker (garage.fig.systems)
|
||||||
|
|
@ -56,6 +58,8 @@ All services are accessible via:
|
||||||
| Traefik Dashboard | traefik.fig.systems | ✅ |
|
| Traefik Dashboard | traefik.fig.systems | ✅ |
|
||||||
| LLDAP | lldap.fig.systems | ✅ |
|
| LLDAP | lldap.fig.systems | ✅ |
|
||||||
| Tinyauth | auth.fig.systems | ❌ |
|
| Tinyauth | auth.fig.systems | ❌ |
|
||||||
|
| Homarr | home.fig.systems | ✅ |
|
||||||
|
| Backrest | backup.fig.systems | ✅ |
|
||||||
| Jellyfin | flix.fig.systems | ❌* |
|
| Jellyfin | flix.fig.systems | ❌* |
|
||||||
| Jellyseerr | requests.fig.systems | ✅ |
|
| Jellyseerr | requests.fig.systems | ✅ |
|
||||||
| Immich | photos.fig.systems | ❌* |
|
| Immich | photos.fig.systems | ❌* |
|
||||||
|
|
@ -143,6 +147,8 @@ cd compose/media/automation/qbittorrent && docker compose up -d
|
||||||
# Utility services
|
# Utility services
|
||||||
cd compose/services/linkwarden && docker compose up -d
|
cd compose/services/linkwarden && docker compose up -d
|
||||||
cd compose/services/vikunja && docker compose up -d
|
cd compose/services/vikunja && docker compose up -d
|
||||||
|
cd compose/services/homarr && docker compose up -d
|
||||||
|
cd compose/services/backrest && docker compose up -d
|
||||||
cd compose/services/lubelogger && docker compose up -d
|
cd compose/services/lubelogger && docker compose up -d
|
||||||
cd compose/services/calibre-web && docker compose up -d
|
cd compose/services/calibre-web && docker compose up -d
|
||||||
cd compose/services/booklore && docker compose up -d
|
cd compose/services/booklore && docker compose up -d
|
||||||
|
|
|
||||||
16
compose/services/backrest/.env
Normal file
16
compose/services/backrest/.env
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Backrest Configuration
|
||||||
|
# Backrest provides a web UI for managing Restic backups
|
||||||
|
|
||||||
|
# Timezone
|
||||||
|
TZ=America/Los_Angeles
|
||||||
|
|
||||||
|
# Note: Backup repositories and credentials are configured through the web UI
|
||||||
|
# Access the UI at https://backup.fig.systems to set up your Backblaze B2 repository
|
||||||
|
|
||||||
|
# Example B2 repository format (configured in web UI):
|
||||||
|
# Repository Type: S3 (S3-compatible storage)
|
||||||
|
# Endpoint: s3.us-west-002.backblazeb2.com
|
||||||
|
# Bucket: your-bucket-name
|
||||||
|
# Access Key ID: your-b2-key-id
|
||||||
|
# Secret Access Key: your-b2-application-key
|
||||||
|
# Path: /backrest-backups
|
||||||
7
compose/services/backrest/.gitignore
vendored
Normal file
7
compose/services/backrest/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Backrest data and cache
|
||||||
|
data/
|
||||||
|
config/
|
||||||
|
cache/
|
||||||
|
|
||||||
|
# Keep .env.example if created
|
||||||
|
!.env.example
|
||||||
346
compose/services/backrest/README.md
Normal file
346
compose/services/backrest/README.md
Normal file
|
|
@ -0,0 +1,346 @@
|
||||||
|
# Backrest - Web UI for Restic Backups
|
||||||
|
|
||||||
|
Backrest provides a modern web interface for managing Restic backups with support for Backblaze B2 (S3-compatible) storage.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Web UI**: User-friendly interface for managing backups
|
||||||
|
- **Encrypted Backups**: Uses Restic for encrypted, deduplicated backups
|
||||||
|
- **Scheduling**: Built-in scheduler for automatic backups
|
||||||
|
- **Monitoring**: View backup status, logs, and statistics
|
||||||
|
- **Multiple Repositories**: Support for multiple backup destinations
|
||||||
|
- **Retention Policies**: Automatic cleanup of old backups
|
||||||
|
- **Notifications**: Email and webhook notifications for backup events
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Start the Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd compose/services/backrest
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Access Web UI
|
||||||
|
|
||||||
|
Open your browser and navigate to:
|
||||||
|
- https://backup.fig.systems
|
||||||
|
- Or: https://backup.edfig.dev
|
||||||
|
|
||||||
|
Login with your SSO credentials (tinyauth).
|
||||||
|
|
||||||
|
### 3. Configure Backblaze B2 Repository
|
||||||
|
|
||||||
|
1. **Create B2 Bucket** (if not already done):
|
||||||
|
- Go to https://secure.backblaze.com/b2_buckets.htm
|
||||||
|
- Click "Create a Bucket"
|
||||||
|
- Name: `homelab-backups` (or your choice)
|
||||||
|
- Files: Private
|
||||||
|
- Encryption: Server-Side (or Disabled - Backrest encrypts client-side)
|
||||||
|
|
||||||
|
2. **Create Application Key**:
|
||||||
|
- Go to https://secure.backblaze.com/app_keys.htm
|
||||||
|
- Click "Add a New Application Key"
|
||||||
|
- Name: `backrest-homelab`
|
||||||
|
- Access: Read and Write
|
||||||
|
- Bucket: Select your backup bucket
|
||||||
|
- Save the `keyID` and `applicationKey`
|
||||||
|
|
||||||
|
3. **Add Repository in Backrest**:
|
||||||
|
- Click "Add Repository"
|
||||||
|
- Repository Name: `B2 Immich Photos`
|
||||||
|
- Storage Type: `S3-compatible storage`
|
||||||
|
- Configuration:
|
||||||
|
```
|
||||||
|
Endpoint: s3.us-west-002.backblazeb2.com
|
||||||
|
Region: us-west-002
|
||||||
|
Bucket: homelab-backups
|
||||||
|
Path: /immich-photos
|
||||||
|
Access Key ID: [your B2 keyID]
|
||||||
|
Secret Access Key: [your B2 applicationKey]
|
||||||
|
```
|
||||||
|
- Encryption Password: Set a strong password (SAVE THIS!)
|
||||||
|
- Click "Initialize Repository"
|
||||||
|
|
||||||
|
### 4. Create Backup Plan
|
||||||
|
|
||||||
|
1. **Add Plan**:
|
||||||
|
- Click "Add Plan"
|
||||||
|
- Plan Name: `Immich Daily Backup`
|
||||||
|
- Repository: Select your B2 repository
|
||||||
|
- Paths to Backup:
|
||||||
|
- `/backups/immich`
|
||||||
|
- Exclude Patterns (optional):
|
||||||
|
- `*.tmp`
|
||||||
|
- `*.log`
|
||||||
|
|
||||||
|
2. **Schedule**:
|
||||||
|
- Backup Schedule: `0 3 * * *` (3 AM daily)
|
||||||
|
- Enable "Automatic Backups"
|
||||||
|
|
||||||
|
3. **Retention Policy**:
|
||||||
|
- Keep Last: 7 daily backups
|
||||||
|
- Keep Weekly: 4 weekly backups
|
||||||
|
- Keep Monthly: 6 monthly backups
|
||||||
|
- Keep Yearly: 2 yearly backups
|
||||||
|
|
||||||
|
4. **Notifications** (optional):
|
||||||
|
- Configure email or webhook for backup status
|
||||||
|
- Alert on failures
|
||||||
|
|
||||||
|
### 5. Run First Backup
|
||||||
|
|
||||||
|
Click "Run Now" to start your first backup immediately.
|
||||||
|
|
||||||
|
## Backup Locations
|
||||||
|
|
||||||
|
The service has access to these directories:
|
||||||
|
|
||||||
|
- `/backups/immich` - Immich photos (read-only)
|
||||||
|
- `/backups/homelab-config` - All compose configurations (read-only)
|
||||||
|
|
||||||
|
You can add more volumes in `compose.yaml` as needed.
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### View Backup Status
|
||||||
|
|
||||||
|
In the Backrest web UI:
|
||||||
|
- Dashboard shows all backup plans and their status
|
||||||
|
- Click on a plan to see backup history
|
||||||
|
- View logs for detailed information
|
||||||
|
|
||||||
|
### Check Repository Size
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via web UI: Repository → Statistics
|
||||||
|
# Shows: Total size, deduplicated size, number of snapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify Backups
|
||||||
|
|
||||||
|
Backrest has built-in verification:
|
||||||
|
1. Go to Repository → Verify
|
||||||
|
2. Click "Run Verification"
|
||||||
|
3. Check results for any errors
|
||||||
|
|
||||||
|
## Restore Files
|
||||||
|
|
||||||
|
### Via Web UI
|
||||||
|
|
||||||
|
1. Go to Plan → Snapshots
|
||||||
|
2. Select snapshot to restore
|
||||||
|
3. Click "Browse Files"
|
||||||
|
4. Select files/folders to restore
|
||||||
|
5. Choose restore location
|
||||||
|
6. Click "Restore"
|
||||||
|
|
||||||
|
### Via CLI (Advanced)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List snapshots
|
||||||
|
docker exec backrest restic -r [repository] snapshots
|
||||||
|
|
||||||
|
# Restore specific snapshot
|
||||||
|
docker exec backrest restic -r [repository] restore [snapshot-id] --target /restore
|
||||||
|
|
||||||
|
# Restore specific file
|
||||||
|
docker exec backrest restic -r [repository] restore [snapshot-id] --target /restore --include /path/to/file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Backup
|
||||||
|
|
||||||
|
### Backup Backrest Config
|
||||||
|
|
||||||
|
Your Backrest configuration (plans, schedules, repositories) is stored in:
|
||||||
|
- `./config/config.json`
|
||||||
|
|
||||||
|
**Important**: Backup this file! It contains your repository credentials (encrypted).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create backup
|
||||||
|
cp config/config.json config/config.json.backup
|
||||||
|
|
||||||
|
# Restore backup
|
||||||
|
cp config/config.json.backup config/config.json
|
||||||
|
docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export Configuration
|
||||||
|
|
||||||
|
In Web UI:
|
||||||
|
1. Settings → Export Configuration
|
||||||
|
2. Save JSON file securely
|
||||||
|
3. Store encryption passwords separately
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Cannot Access Web UI
|
||||||
|
|
||||||
|
Check container status:
|
||||||
|
```bash
|
||||||
|
docker compose logs backrest
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify Traefik routing:
|
||||||
|
```bash
|
||||||
|
docker logs traefik | grep backrest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Fails
|
||||||
|
|
||||||
|
1. **Check Logs**:
|
||||||
|
- Web UI: Plan → View Logs
|
||||||
|
- Or: `docker compose logs -f backrest`
|
||||||
|
|
||||||
|
2. **Verify B2 Credentials**:
|
||||||
|
- Test connection in Repository settings
|
||||||
|
- Ensure application key has read/write access
|
||||||
|
|
||||||
|
3. **Check Disk Space**:
|
||||||
|
```bash
|
||||||
|
df -h
|
||||||
|
docker exec backrest df -h /cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Repository Locked
|
||||||
|
|
||||||
|
If a backup is interrupted, the repository may be locked:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via Web UI: Repository → Unlock
|
||||||
|
# Or via CLI:
|
||||||
|
docker exec backrest restic -r [repository] unlock
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slow Backups
|
||||||
|
|
||||||
|
1. **Enable Caching**: Already configured via `XDG_CACHE_HOME`
|
||||||
|
2. **Increase Upload Speed**: Check B2 endpoint is geographically close
|
||||||
|
3. **Exclude Unnecessary Files**: Add patterns to exclude list
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Encryption
|
||||||
|
|
||||||
|
- **Client-side**: All data encrypted before upload
|
||||||
|
- **Repository Password**: Required to access backups
|
||||||
|
- **Storage**: Store repository passwords in password manager
|
||||||
|
|
||||||
|
### Access Control
|
||||||
|
|
||||||
|
- **SSO Protected**: Web UI requires authentication via tinyauth
|
||||||
|
- **API Keys**: B2 application keys scoped to specific bucket
|
||||||
|
- **Read-Only Mounts**: Backup sources mounted read-only
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
1. **Test Restores**: Regularly test restoring files
|
||||||
|
2. **Monitor Backups**: Check backup status weekly
|
||||||
|
3. **Verify Integrity**: Run verification monthly
|
||||||
|
4. **Secure Passwords**: Use strong, unique repository passwords
|
||||||
|
5. **Document Recovery**: Keep recovery procedures documented
|
||||||
|
6. **Offsite Storage**: B2 provides geographic redundancy
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
### Add More Backup Sources
|
||||||
|
|
||||||
|
Edit `compose.yaml` to add more volumes:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- /path/to/backup:/backups/name:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
Then create a new backup plan in the web UI.
|
||||||
|
|
||||||
|
### Multiple Repositories
|
||||||
|
|
||||||
|
Configure multiple destinations:
|
||||||
|
1. Primary: Backblaze B2
|
||||||
|
2. Secondary: Local NAS/USB drive
|
||||||
|
3. Archive: Another cloud provider
|
||||||
|
|
||||||
|
### Webhooks
|
||||||
|
|
||||||
|
Configure webhooks for monitoring:
|
||||||
|
1. Settings → Notifications
|
||||||
|
2. Add Webhook URL (e.g., Discord, Slack, Uptime Kuma)
|
||||||
|
3. Select events: Backup Success, Backup Failure
|
||||||
|
|
||||||
|
### Custom Retention
|
||||||
|
|
||||||
|
Fine-tune retention policies:
|
||||||
|
```
|
||||||
|
--keep-within 7d
|
||||||
|
--keep-within-daily 30d
|
||||||
|
--keep-within-weekly 90d
|
||||||
|
--keep-within-monthly 1y
|
||||||
|
--keep-within-yearly 5y
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resource Usage
|
||||||
|
|
||||||
|
**Typical Usage:**
|
||||||
|
- CPU: Low (spikes during backup)
|
||||||
|
- Memory: ~200-500MB
|
||||||
|
- Disk: Cache grows over time (monitor)
|
||||||
|
- Network: Depends on backup size
|
||||||
|
|
||||||
|
**Monitoring Cache Size:**
|
||||||
|
```bash
|
||||||
|
du -sh compose/services/backrest/cache
|
||||||
|
```
|
||||||
|
|
||||||
|
Clean cache if needed (safe to delete - will rebuild):
|
||||||
|
```bash
|
||||||
|
rm -rf compose/services/backrest/cache/*
|
||||||
|
docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backrest vs Duplicati
|
||||||
|
|
||||||
|
We chose Backrest over Duplicati because:
|
||||||
|
- **Modern**: Built on Restic (actively developed)
|
||||||
|
- **Performance**: Better deduplication and compression
|
||||||
|
- **Reliability**: Restic is battle-tested
|
||||||
|
- **Features**: More advanced scheduling and monitoring
|
||||||
|
- **UI**: Clean, responsive interface
|
||||||
|
|
||||||
|
## Cost Estimation
|
||||||
|
|
||||||
|
**Backblaze B2 Pricing (2024):**
|
||||||
|
- Storage: $0.006/GB/month
|
||||||
|
- Download: $0.01/GB (first 3x storage free)
|
||||||
|
- Upload: Free
|
||||||
|
|
||||||
|
**Example: 100GB Immich photos**
|
||||||
|
- Storage Cost: $0.60/month
|
||||||
|
- Download (3 restores/month): Free
|
||||||
|
- **Total: ~$0.60/month**
|
||||||
|
|
||||||
|
**With Deduplication:**
|
||||||
|
- First backup: 100GB
|
||||||
|
- Daily incrementals: ~1-5GB
|
||||||
|
- Monthly growth: ~20GB
|
||||||
|
- Avg monthly cost: ~$0.70
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Backrest Documentation](https://github.com/garethgeorge/backrest)
|
||||||
|
- [Restic Documentation](https://restic.readthedocs.io/)
|
||||||
|
- [Backblaze B2 Documentation](https://www.backblaze.com/b2/docs/)
|
||||||
|
- [S3-compatible API Guide](https://www.backblaze.com/b2/docs/s3_compatible_api.html)
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. ✅ Configure B2 repository
|
||||||
|
2. ✅ Create backup plan for Immich
|
||||||
|
3. ⬜ Run initial backup
|
||||||
|
4. ⬜ Verify backup integrity
|
||||||
|
5. ⬜ Test restore procedure
|
||||||
|
6. ⬜ Set up notifications
|
||||||
|
7. ⬜ Add homelab-config backups
|
||||||
|
8. ⬜ Schedule monthly verification
|
||||||
49
compose/services/backrest/compose.yaml
Normal file
49
compose/services/backrest/compose.yaml
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
services:
|
||||||
|
backrest:
|
||||||
|
image: garethgeorge/backrest:latest
|
||||||
|
container_name: backrest
|
||||||
|
hostname: backrest
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- homelab
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./config:/config
|
||||||
|
- ./cache:/cache
|
||||||
|
- /home/user/homelab/compose/media/frontend/immich/upload:/backups/immich:ro
|
||||||
|
- /home/user/homelab/compose:/backups/homelab-config:ro
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- BACKREST_DATA=/data
|
||||||
|
- BACKREST_CONFIG=/config/config.json
|
||||||
|
- XDG_CACHE_HOME=/cache
|
||||||
|
- TZ=${TZ:-America/Los_Angeles}
|
||||||
|
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: true
|
||||||
|
traefik.http.routers.backrest.rule: Host(`backup.fig.systems`) || Host(`backup.edfig.dev`)
|
||||||
|
traefik.http.routers.backrest.entrypoints: websecure
|
||||||
|
traefik.http.routers.backrest.tls.certresolver: letsencrypt
|
||||||
|
traefik.http.services.backrest.loadbalancer.server.port: 9898
|
||||||
|
|
||||||
|
# Require authentication
|
||||||
|
traefik.http.routers.backrest.middlewares: tinyauth
|
||||||
|
|
||||||
|
# Homarr Discovery
|
||||||
|
homarr.name: Backrest Backup
|
||||||
|
homarr.group: Services
|
||||||
|
homarr.icon: mdi:backup-restore
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:9898/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
networks:
|
||||||
|
homelab:
|
||||||
|
external: true
|
||||||
332
compose/services/homarr/README.md
Normal file
332
compose/services/homarr/README.md
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
# Homarr Dashboard
|
||||||
|
|
||||||
|
Modern, customizable dashboard with automatic Docker service discovery.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🎨 **Modern UI** - Beautiful, responsive design
|
||||||
|
- 🔍 **Auto-Discovery** - Automatically finds Docker services
|
||||||
|
- 📊 **Widgets** - System stats, weather, calendar, RSS, etc.
|
||||||
|
- 🏷️ **Labels** - Organize services by category
|
||||||
|
- 🔗 **Integration** - Connects to *arr apps, Jellyfin, etc.
|
||||||
|
- 🎯 **Customizable** - Drag-and-drop layout
|
||||||
|
- 🌙 **Dark Mode** - Built-in dark theme
|
||||||
|
- 📱 **Mobile Friendly** - Works on all devices
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
- **URL:** https://home.fig.systems or https://home.edfig.dev
|
||||||
|
- **Port:** 7575 (if accessing directly)
|
||||||
|
|
||||||
|
## First-Time Setup
|
||||||
|
|
||||||
|
### 1. Deploy Homarr
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd compose/services/homarr
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Access Dashboard
|
||||||
|
|
||||||
|
Open https://home.fig.systems in your browser.
|
||||||
|
|
||||||
|
### 3. Auto-Discovery
|
||||||
|
|
||||||
|
Homarr will automatically detect services with these labels:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
homarr.name: "Service Name"
|
||||||
|
homarr.group: "Category"
|
||||||
|
homarr.icon: "/icons/service.png"
|
||||||
|
homarr.href: "https://service.fig.systems"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding Services to Dashboard
|
||||||
|
|
||||||
|
### Automatic (Recommended)
|
||||||
|
|
||||||
|
Add labels to your service's `compose.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
# Traefik labels...
|
||||||
|
traefik.enable: true
|
||||||
|
# ... etc
|
||||||
|
|
||||||
|
# Homarr labels
|
||||||
|
homarr.name: Jellyfin
|
||||||
|
homarr.group: Media
|
||||||
|
homarr.icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jellyfin.png
|
||||||
|
homarr.href: https://flix.fig.systems
|
||||||
|
```
|
||||||
|
|
||||||
|
Redeploy the service:
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Homarr will automatically add it to the dashboard!
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
1. Click the "+" button in Homarr
|
||||||
|
2. Select "Add Service"
|
||||||
|
3. Fill in:
|
||||||
|
- **Name:** Service name
|
||||||
|
- **URL:** https://service.fig.systems
|
||||||
|
- **Icon:** Choose from library or custom URL
|
||||||
|
- **Category:** Group services (Media, Services, etc.)
|
||||||
|
|
||||||
|
## Integration with Services
|
||||||
|
|
||||||
|
### Jellyfin
|
||||||
|
|
||||||
|
Add to Jellyfin's `compose.yaml`:
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
homarr.name: Jellyfin
|
||||||
|
homarr.group: Media
|
||||||
|
homarr.icon: /icons/jellyfin.png
|
||||||
|
homarr.widget.type: jellyfin
|
||||||
|
homarr.widget.url: http://jellyfin:8096
|
||||||
|
homarr.widget.key: ${JELLYFIN_API_KEY}
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows: Currently playing, library stats
|
||||||
|
|
||||||
|
### Sonarr/Radarr
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
homarr.name: Sonarr
|
||||||
|
homarr.group: Media Automation
|
||||||
|
homarr.icon: /icons/sonarr.png
|
||||||
|
homarr.widget.type: sonarr
|
||||||
|
homarr.widget.url: http://sonarr:8989
|
||||||
|
homarr.widget.key: ${SONARR_API_KEY}
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows: Queue, calendar, missing episodes
|
||||||
|
|
||||||
|
### qBittorrent
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
homarr.name: qBittorrent
|
||||||
|
homarr.group: Downloads
|
||||||
|
homarr.icon: /icons/qbittorrent.png
|
||||||
|
homarr.widget.type: qbittorrent
|
||||||
|
homarr.widget.url: http://qbittorrent:8080
|
||||||
|
homarr.widget.username: ${QBIT_USERNAME}
|
||||||
|
homarr.widget.password: ${QBIT_PASSWORD}
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows: Active torrents, download speed
|
||||||
|
|
||||||
|
## Available Widgets
|
||||||
|
|
||||||
|
### System Monitoring
|
||||||
|
- **CPU Usage** - Real-time CPU stats
|
||||||
|
- **Memory Usage** - RAM usage
|
||||||
|
- **Disk Space** - Storage capacity
|
||||||
|
- **Network** - Upload/download speeds
|
||||||
|
|
||||||
|
### Services
|
||||||
|
- **Jellyfin** - Media server stats
|
||||||
|
- **Sonarr** - TV show automation
|
||||||
|
- **Radarr** - Movie automation
|
||||||
|
- **Lidarr** - Music automation
|
||||||
|
- **Readarr** - Book automation
|
||||||
|
- **Prowlarr** - Indexer management
|
||||||
|
- **SABnzbd** - Usenet downloads
|
||||||
|
- **qBittorrent** - Torrent downloads
|
||||||
|
- **Overseerr/Jellyseerr** - Media requests
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
- **Weather** - Local weather forecast
|
||||||
|
- **Calendar** - Events and tasks
|
||||||
|
- **RSS Feeds** - News aggregator
|
||||||
|
- **Docker** - Container status
|
||||||
|
- **Speed Test** - Internet speed
|
||||||
|
- **Notes** - Sticky notes
|
||||||
|
- **Iframe** - Embed any website
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Change Theme
|
||||||
|
|
||||||
|
1. Click settings icon (⚙️)
|
||||||
|
2. Go to "Appearance"
|
||||||
|
3. Choose color scheme
|
||||||
|
4. Save
|
||||||
|
|
||||||
|
### Reorganize Layout
|
||||||
|
|
||||||
|
1. Click edit mode (✏️)
|
||||||
|
2. Drag and drop services
|
||||||
|
3. Resize widgets
|
||||||
|
4. Click save
|
||||||
|
|
||||||
|
### Add Categories
|
||||||
|
|
||||||
|
1. Click "Add Category"
|
||||||
|
2. Name it (e.g., "Media", "Tools", "Infrastructure")
|
||||||
|
3. Drag services into categories
|
||||||
|
4. Collapse/expand as needed
|
||||||
|
|
||||||
|
### Custom Icons
|
||||||
|
|
||||||
|
**Option 1: Use Icon Library**
|
||||||
|
- Homarr includes icons from [Dashboard Icons](https://github.com/walkxcode/dashboard-icons)
|
||||||
|
- Search by service name
|
||||||
|
|
||||||
|
**Option 2: Custom URL**
|
||||||
|
```
|
||||||
|
https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/service.png
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 3: Local Icons**
|
||||||
|
- Place in `./icons/` directory
|
||||||
|
- Reference as `/icons/service.png`
|
||||||
|
|
||||||
|
## Recommended Dashboard Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ 🏠 Homelab Dashboard │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ [System Stats] [Weather] [Calendar] │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 📺 Media │
|
||||||
|
│ [Jellyfin] [Jellyseerr] [Immich] │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 🤖 Media Automation │
|
||||||
|
│ [Sonarr] [Radarr] [qBittorrent] │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 🛠️ Services │
|
||||||
|
│ [Linkwarden] [Vikunja] [FreshRSS] │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 🔧 Infrastructure │
|
||||||
|
│ [Traefik] [LLDAP] [Tinyauth] │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add to All Services
|
||||||
|
|
||||||
|
To make all your services auto-discoverable, add these labels:
|
||||||
|
|
||||||
|
### Jellyfin
|
||||||
|
```yaml
|
||||||
|
homarr.name: Jellyfin
|
||||||
|
homarr.group: Media
|
||||||
|
homarr.icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jellyfin.png
|
||||||
|
```
|
||||||
|
|
||||||
|
### Jellyseerr
|
||||||
|
```yaml
|
||||||
|
homarr.name: Jellyseerr
|
||||||
|
homarr.group: Media
|
||||||
|
homarr.icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jellyseerr.png
|
||||||
|
```
|
||||||
|
|
||||||
|
### Immich
|
||||||
|
```yaml
|
||||||
|
homarr.name: Immich Photos
|
||||||
|
homarr.group: Media
|
||||||
|
homarr.icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/immich.png
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sonarr/Radarr/SABnzbd/qBittorrent
|
||||||
|
```yaml
|
||||||
|
homarr.name: [Service]
|
||||||
|
homarr.group: Automation
|
||||||
|
homarr.icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/[service].png
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linkwarden/Vikunja/etc.
|
||||||
|
```yaml
|
||||||
|
homarr.name: [Service]
|
||||||
|
homarr.group: Utilities
|
||||||
|
homarr.icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/[service].png
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mobile Access
|
||||||
|
|
||||||
|
Homarr is fully responsive. For best mobile experience:
|
||||||
|
|
||||||
|
1. Add to home screen (iOS/Android)
|
||||||
|
2. Works as PWA (Progressive Web App)
|
||||||
|
3. Touch-optimized interface
|
||||||
|
|
||||||
|
## Backup Configuration
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
```bash
|
||||||
|
cd compose/services/homarr
|
||||||
|
tar -czf homarr-backup-$(date +%Y%m%d).tar.gz config/ data/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore
|
||||||
|
```bash
|
||||||
|
cd compose/services/homarr
|
||||||
|
tar -xzf homarr-backup-YYYYMMDD.tar.gz
|
||||||
|
docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Services not auto-discovered
|
||||||
|
|
||||||
|
Check Docker socket permission:
|
||||||
|
```bash
|
||||||
|
docker logs homarr
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify labels on service:
|
||||||
|
```bash
|
||||||
|
docker inspect service-name | grep homarr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Can't connect to services
|
||||||
|
|
||||||
|
Services must be on same Docker network or accessible via hostname.
|
||||||
|
|
||||||
|
Use container names, not `localhost`:
|
||||||
|
- ✅ `http://jellyfin:8096`
|
||||||
|
- ❌ `http://localhost:8096`
|
||||||
|
|
||||||
|
### Widgets not working
|
||||||
|
|
||||||
|
1. Check API keys are correct
|
||||||
|
2. Verify service URLs (use container names)
|
||||||
|
3. Check service is running: `docker ps`
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
| Dashboard | Auto-Discovery | Widgets | Complexity |
|
||||||
|
|-----------|---------------|---------|------------|
|
||||||
|
| **Homarr** | ✅ Excellent | ✅ Many | Low |
|
||||||
|
| Homepage | ✅ Good | ✅ Many | Low |
|
||||||
|
| Heimdall | ❌ Manual | ❌ Few | Very Low |
|
||||||
|
| Dashy | ⚠️ Limited | ✅ Some | Medium |
|
||||||
|
| Homer | ❌ Manual | ❌ None | Very Low |
|
||||||
|
| Organizr | ⚠️ Limited | ✅ Many | High |
|
||||||
|
|
||||||
|
**Homarr chosen for:** Best balance of features, auto-discovery, and ease of use.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Official Docs](https://homarr.dev/docs)
|
||||||
|
- [GitHub](https://github.com/ajnart/homarr)
|
||||||
|
- [Discord Community](https://discord.gg/aCsmEV5RgA)
|
||||||
|
- [Icon Library](https://github.com/walkxcode/dashboard-icons)
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
1. **Start Simple** - Add core services first, expand later
|
||||||
|
2. **Use Categories** - Group related services
|
||||||
|
3. **Enable Widgets** - Make dashboard informative
|
||||||
|
4. **Mobile First** - Test on phone/tablet
|
||||||
|
5. **Backup Config** - Save your layout regularly
|
||||||
57
compose/services/homarr/compose.yaml
Normal file
57
compose/services/homarr/compose.yaml
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Homarr - Modern dashboard with Docker auto-discovery
|
||||||
|
# Docs: https://homarr.dev/docs/getting-started/installation
|
||||||
|
# GitHub: https://github.com/ajnart/homarr
|
||||||
|
|
||||||
|
services:
|
||||||
|
homarr:
|
||||||
|
container_name: homarr
|
||||||
|
image: ghcr.io/ajnart/homarr:latest
|
||||||
|
|
||||||
|
environment:
|
||||||
|
# Timezone
|
||||||
|
- TZ=America/Los_Angeles
|
||||||
|
|
||||||
|
# Base path (if behind reverse proxy with path)
|
||||||
|
# - BASE_URL=/dashboard
|
||||||
|
|
||||||
|
# Port (default: 7575)
|
||||||
|
- PORT=7575
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
# - AUTH_PROVIDER=oidc # For SSO integration
|
||||||
|
# - DEFAULT_COLOR_SCHEME=dark
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Configuration and data
|
||||||
|
- ./config:/app/data/configs
|
||||||
|
- ./data:/data
|
||||||
|
- ./icons:/app/public/icons
|
||||||
|
|
||||||
|
# Docker socket for auto-discovery
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- homelab
|
||||||
|
|
||||||
|
labels:
|
||||||
|
traefik.enable: true
|
||||||
|
|
||||||
|
# Web UI routing
|
||||||
|
traefik.http.routers.homarr.rule: Host(`home.fig.systems`) || Host(`home.edfig.dev`)
|
||||||
|
traefik.http.routers.homarr.entrypoints: websecure
|
||||||
|
traefik.http.routers.homarr.tls.certresolver: letsencrypt
|
||||||
|
traefik.http.services.homarr.loadbalancer.server.port: 7575
|
||||||
|
|
||||||
|
# SSO Protection (optional - dashboard may be public)
|
||||||
|
# traefik.http.routers.homarr.middlewares: tinyauth
|
||||||
|
|
||||||
|
# Homarr Labels for custom configuration
|
||||||
|
homarr.name: Homarr Dashboard
|
||||||
|
homarr.group: Infrastructure
|
||||||
|
homarr.icon: /icons/homarr.png
|
||||||
|
|
||||||
|
networks:
|
||||||
|
homelab:
|
||||||
|
external: true
|
||||||
45
templates/service-template/.env.example
Normal file
45
templates/service-template/.env.example
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Service Configuration
|
||||||
|
# Copy this file to .env and update with your actual values
|
||||||
|
|
||||||
|
# Timezone
|
||||||
|
TZ=America/Los_Angeles
|
||||||
|
|
||||||
|
# User/Group IDs (match your host user)
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
|
||||||
|
# Service-specific configuration
|
||||||
|
# Uncomment and configure as needed:
|
||||||
|
|
||||||
|
# Database Configuration (if using database)
|
||||||
|
# DB_PASSWORD=changeme_please_set_secure_password
|
||||||
|
# DB_USERNAME=serviceuser
|
||||||
|
# DB_DATABASE_NAME=servicedb
|
||||||
|
|
||||||
|
# Redis Configuration (if using Redis)
|
||||||
|
# REDIS_PASSWORD=changeme_please_set_secure_password
|
||||||
|
|
||||||
|
# Application Settings
|
||||||
|
# APP_URL=https://service.fig.systems
|
||||||
|
# APP_SECRET=changeme_please_set_random_secret
|
||||||
|
# API_KEY=changeme_your_api_key_here
|
||||||
|
|
||||||
|
# Email/SMTP Configuration (if needed)
|
||||||
|
# SMTP_HOST=smtp.gmail.com
|
||||||
|
# SMTP_PORT=587
|
||||||
|
# SMTP_USER=your-email@gmail.com
|
||||||
|
# SMTP_PASSWORD=changeme_your_app_password
|
||||||
|
# SMTP_FROM=Service <noreply@fig.systems>
|
||||||
|
|
||||||
|
# Storage Configuration
|
||||||
|
# STORAGE_PATH=/data
|
||||||
|
# MAX_UPLOAD_SIZE=100M
|
||||||
|
|
||||||
|
# Security
|
||||||
|
# ADMIN_EMAIL=admin@edfig.dev
|
||||||
|
# ADMIN_PASSWORD=changeme_please_set_secure_password
|
||||||
|
|
||||||
|
# Feature Flags (example)
|
||||||
|
# ENABLE_REGISTRATION=false
|
||||||
|
# ENABLE_API=true
|
||||||
|
# LOG_LEVEL=info
|
||||||
250
templates/service-template/README.md
Normal file
250
templates/service-template/README.md
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
# Service Template
|
||||||
|
|
||||||
|
This template provides a starting point for adding new services to your homelab.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Copy this template:**
|
||||||
|
```bash
|
||||||
|
cp -r templates/service-template compose/[category]/[service-name]
|
||||||
|
cd compose/[category]/[service-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Choose the correct category:**
|
||||||
|
- `compose/core/` - Infrastructure services (reverse proxy, auth, etc.)
|
||||||
|
- `compose/media/` - Media-related services (streaming, automation)
|
||||||
|
- `compose/services/` - Utility services (bookmarks, tasks, etc.)
|
||||||
|
|
||||||
|
3. **Update compose.yaml:**
|
||||||
|
- Replace `service-name` with actual service name
|
||||||
|
- Update `image:` with the correct Docker image
|
||||||
|
- Configure environment variables
|
||||||
|
- Update port numbers
|
||||||
|
- Update Traefik domain (replace `service.fig.systems`)
|
||||||
|
- Add any required volumes or dependencies
|
||||||
|
|
||||||
|
4. **Create .env file (if needed):**
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env and set real values
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Update README.md:**
|
||||||
|
- Add service to main README.md service table
|
||||||
|
- Include service URL and description
|
||||||
|
- Document any special configuration
|
||||||
|
|
||||||
|
6. **Test deployment:**
|
||||||
|
```bash
|
||||||
|
docker compose config # Validate syntax
|
||||||
|
docker compose up -d # Deploy
|
||||||
|
docker compose logs -f # Check logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template Features
|
||||||
|
|
||||||
|
### Included by Default
|
||||||
|
- ✅ Traefik integration with SSL/TLS
|
||||||
|
- ✅ Dual domain support (fig.systems + edfig.dev)
|
||||||
|
- ✅ SSO middleware support (commented)
|
||||||
|
- ✅ Network configuration (homelab external)
|
||||||
|
- ✅ Standard environment variables (PUID, PGID, TZ)
|
||||||
|
- ✅ Restart policy
|
||||||
|
- ✅ Health check template
|
||||||
|
|
||||||
|
### Optional Components (Commented)
|
||||||
|
- Database service (PostgreSQL example)
|
||||||
|
- Redis cache
|
||||||
|
- Internal network for multi-container setups
|
||||||
|
- Port exposure (prefer Traefik)
|
||||||
|
- Named volumes
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Simple Single-Container Service
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: app:latest
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
networks:
|
||||||
|
- homelab
|
||||||
|
labels:
|
||||||
|
# Traefik config
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Container with Database
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: app:latest
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
networks:
|
||||||
|
- homelab
|
||||||
|
- app_internal
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
networks:
|
||||||
|
- app_internal
|
||||||
|
|
||||||
|
networks:
|
||||||
|
homelab:
|
||||||
|
external: true
|
||||||
|
app_internal:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service with Media Access
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: app:latest
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- /media/movies:/movies:ro
|
||||||
|
- /media/books:/books:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
Before submitting a PR with a new service:
|
||||||
|
|
||||||
|
- [ ] Service name is descriptive and lowercase with hyphens
|
||||||
|
- [ ] Docker image is from a trusted source
|
||||||
|
- [ ] All placeholder passwords use `changeme_*` format
|
||||||
|
- [ ] Traefik labels are complete (router, entrypoint, tls, rule)
|
||||||
|
- [ ] Both domains configured (fig.systems + edfig.dev)
|
||||||
|
- [ ] SSO middleware decision made (enabled/disabled with comment)
|
||||||
|
- [ ] Networks properly configured (external: true)
|
||||||
|
- [ ] Health check added (if applicable)
|
||||||
|
- [ ] Service added to README.md
|
||||||
|
- [ ] Documentation header in compose.yaml
|
||||||
|
- [ ] .env.example provided (if using env_file)
|
||||||
|
- [ ] Tested locally before committing
|
||||||
|
|
||||||
|
## Domain Selection
|
||||||
|
|
||||||
|
Choose a subdomain that makes sense:
|
||||||
|
|
||||||
|
**Common Patterns:**
|
||||||
|
- Service name: `servicename.fig.systems` (most common)
|
||||||
|
- Function-based: `monitor.fig.systems`, `backup.fig.systems`
|
||||||
|
- Alternative names: `flix.fig.systems` (Jellyfin), `requests.fig.systems` (Jellyseerr)
|
||||||
|
|
||||||
|
**Reserved Domains:**
|
||||||
|
- `auth.fig.systems` - Tinyauth
|
||||||
|
- `lldap.fig.systems` - LLDAP
|
||||||
|
- `traefik.fig.systems` - Traefik dashboard
|
||||||
|
- See README.md for complete list
|
||||||
|
|
||||||
|
## Network Configuration
|
||||||
|
|
||||||
|
### Single Container
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
homelab:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Container (with internal network)
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
homelab:
|
||||||
|
external: true # For Traefik access
|
||||||
|
service_internal:
|
||||||
|
name: service_internal
|
||||||
|
driver: bridge # For inter-container communication
|
||||||
|
```
|
||||||
|
|
||||||
|
### Traefik Network Selection
|
||||||
|
If using multiple networks, specify which Traefik should use:
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
traefik.docker.network: homelab
|
||||||
|
```
|
||||||
|
|
||||||
|
## Volume Patterns
|
||||||
|
|
||||||
|
### Configuration Only
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Data Storage
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- ./data:/data
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Media Access (read-only recommended)
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- /media/movies:/movies:ro
|
||||||
|
- /media/tv:/tv:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Database
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/postgresql/data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Service won't start
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker compose logs app
|
||||||
|
|
||||||
|
# Validate compose syntax
|
||||||
|
docker compose config
|
||||||
|
|
||||||
|
# Check network exists
|
||||||
|
docker network ls | grep homelab
|
||||||
|
```
|
||||||
|
|
||||||
|
### Can't access via domain
|
||||||
|
```bash
|
||||||
|
# Check Traefik is running
|
||||||
|
docker ps | grep traefik
|
||||||
|
|
||||||
|
# Check Traefik logs
|
||||||
|
docker logs traefik
|
||||||
|
|
||||||
|
# Verify DNS points to server
|
||||||
|
dig service.fig.systems
|
||||||
|
|
||||||
|
# Check SSL certificate
|
||||||
|
curl -I https://service.fig.systems
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission errors
|
||||||
|
```bash
|
||||||
|
# Check PUID/PGID match your user
|
||||||
|
id
|
||||||
|
|
||||||
|
# Fix ownership
|
||||||
|
sudo chown -R 1000:1000 ./config ./data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See these services for reference:
|
||||||
|
- **Simple:** `compose/services/filebrowser/`
|
||||||
|
- **With database:** `compose/services/vikunja/`
|
||||||
|
- **Multi-container:** `compose/media/frontend/immich/`
|
||||||
|
- **Media service:** `compose/media/frontend/jellyfin/`
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Docker Compose Docs](https://docs.docker.com/compose/)
|
||||||
|
- [Traefik Docker Provider](https://doc.traefik.io/traefik/providers/docker/)
|
||||||
|
- [LinuxServer.io Images](https://fleet.linuxserver.io/)
|
||||||
|
- [Awesome Selfhosted](https://github.com/awesome-selfhosted/awesome-selfhosted)
|
||||||
118
templates/service-template/compose.yaml
Normal file
118
templates/service-template/compose.yaml
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
# Service Name - Brief Description
|
||||||
|
# Official Docs: https://docs.example.com
|
||||||
|
# Docker Hub: https://hub.docker.com/r/example/service
|
||||||
|
|
||||||
|
services:
|
||||||
|
service-name:
|
||||||
|
container_name: service-name
|
||||||
|
image: example/service:latest
|
||||||
|
|
||||||
|
# Environment Variables
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/Los_Angeles
|
||||||
|
# Add service-specific variables here
|
||||||
|
|
||||||
|
# Optional: Use .env file for sensitive config
|
||||||
|
# env_file: .env
|
||||||
|
|
||||||
|
# Volumes - Persistent data storage
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- ./data:/data
|
||||||
|
# Add media folder mounts if needed:
|
||||||
|
# - /media/movies:/movies:ro
|
||||||
|
# - /media/books:/books:ro
|
||||||
|
|
||||||
|
# Ports (optional - prefer Traefik routing)
|
||||||
|
# ports:
|
||||||
|
# - "8080:8080"
|
||||||
|
|
||||||
|
# Restart Policy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Networks
|
||||||
|
networks:
|
||||||
|
- homelab
|
||||||
|
# Add internal network if multi-container:
|
||||||
|
# - service_internal
|
||||||
|
|
||||||
|
# Health Check (optional but recommended)
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||||
|
# interval: 30s
|
||||||
|
# timeout: 10s
|
||||||
|
# retries: 3
|
||||||
|
# start_period: 40s
|
||||||
|
|
||||||
|
# Dependencies (if needed)
|
||||||
|
# depends_on:
|
||||||
|
# - database
|
||||||
|
# - redis
|
||||||
|
|
||||||
|
# Traefik Labels - Web Access with SSL
|
||||||
|
labels:
|
||||||
|
traefik.enable: true
|
||||||
|
|
||||||
|
# Routing - Replace 'service' with actual service name
|
||||||
|
traefik.http.routers.service-name.rule: Host(`service.fig.systems`) || Host(`service.edfig.dev`)
|
||||||
|
traefik.http.routers.service-name.entrypoints: websecure
|
||||||
|
traefik.http.routers.service-name.tls.certresolver: letsencrypt
|
||||||
|
|
||||||
|
# Service port - Change to actual port
|
||||||
|
traefik.http.services.service-name.loadbalancer.server.port: 8080
|
||||||
|
|
||||||
|
# SSO Protection (optional - uncomment if needed)
|
||||||
|
traefik.http.routers.service-name.middlewares: tinyauth
|
||||||
|
|
||||||
|
# Network selection (if using multiple networks)
|
||||||
|
# traefik.docker.network: homelab
|
||||||
|
|
||||||
|
# Optional: Database service (PostgreSQL example)
|
||||||
|
# database:
|
||||||
|
# container_name: service-name-db
|
||||||
|
# image: postgres:16-alpine
|
||||||
|
# environment:
|
||||||
|
# POSTGRES_DB: servicedb
|
||||||
|
# POSTGRES_USER: serviceuser
|
||||||
|
# POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
# TZ: America/Los_Angeles
|
||||||
|
# volumes:
|
||||||
|
# - ./db:/var/lib/postgresql/data
|
||||||
|
# restart: unless-stopped
|
||||||
|
# networks:
|
||||||
|
# - service_internal
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD-SHELL", "pg_isready -h localhost -U $$POSTGRES_USER"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 5s
|
||||||
|
# retries: 5
|
||||||
|
|
||||||
|
# Optional: Redis cache
|
||||||
|
# redis:
|
||||||
|
# container_name: service-name-redis
|
||||||
|
# image: redis:alpine
|
||||||
|
# restart: unless-stopped
|
||||||
|
# networks:
|
||||||
|
# - service_internal
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "redis-cli", "ping"]
|
||||||
|
# interval: 30s
|
||||||
|
# timeout: 10s
|
||||||
|
# retries: 5
|
||||||
|
|
||||||
|
# Networks
|
||||||
|
networks:
|
||||||
|
homelab:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
# Internal network (if multi-container service)
|
||||||
|
# service_internal:
|
||||||
|
# name: service_internal
|
||||||
|
# driver: bridge
|
||||||
|
|
||||||
|
# Named Volumes (optional)
|
||||||
|
# volumes:
|
||||||
|
# data:
|
||||||
|
# cache:
|
||||||
506
terraform/README.md
Normal file
506
terraform/README.md
Normal file
|
|
@ -0,0 +1,506 @@
|
||||||
|
# OpenTofu Infrastructure as Code for Proxmox
|
||||||
|
|
||||||
|
This directory contains OpenTofu (Terraform) configurations for managing Proxmox infrastructure.
|
||||||
|
|
||||||
|
## What is OpenTofu?
|
||||||
|
|
||||||
|
OpenTofu is an open-source fork of Terraform, providing Infrastructure as Code (IaC) capabilities. It allows you to:
|
||||||
|
|
||||||
|
- 📝 **Define infrastructure as code** - Version control your infrastructure
|
||||||
|
- 🔄 **Automate provisioning** - Create VMs/containers with one command
|
||||||
|
- 🎯 **Consistency** - Same config = same result every time
|
||||||
|
- 🔍 **Plan changes** - Preview changes before applying
|
||||||
|
- 🗑️ **Easy cleanup** - Destroy infrastructure when done
|
||||||
|
|
||||||
|
## Why OpenTofu over Terraform?
|
||||||
|
|
||||||
|
- ✅ **Truly Open Source** - MPL 2.0 license (vs. Terraform's BSL)
|
||||||
|
- ✅ **Community Driven** - Not controlled by single company
|
||||||
|
- ✅ **Terraform Compatible** - Drop-in replacement
|
||||||
|
- ✅ **Active Development** - Regular updates and features
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Install OpenTofu
|
||||||
|
|
||||||
|
**Linux/macOS:**
|
||||||
|
```bash
|
||||||
|
# Install via package manager
|
||||||
|
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh | sh
|
||||||
|
|
||||||
|
# Or via Homebrew (macOS/Linux)
|
||||||
|
brew install opentofu
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verify installation:**
|
||||||
|
```bash
|
||||||
|
tofu version
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure Proxmox API
|
||||||
|
|
||||||
|
**Create API Token in Proxmox:**
|
||||||
|
1. Login to Proxmox web UI
|
||||||
|
2. Datacenter → Permissions → API Tokens
|
||||||
|
3. Add new token:
|
||||||
|
- User: `root@pam`
|
||||||
|
- Token ID: `terraform`
|
||||||
|
- Privilege Separation: Unchecked (for full access)
|
||||||
|
4. Save the token ID and secret!
|
||||||
|
|
||||||
|
**Set environment variables:**
|
||||||
|
```bash
|
||||||
|
export PM_API_URL="https://proxmox.local:8006/api2/json"
|
||||||
|
export PM_API_TOKEN_ID="root@pam!terraform"
|
||||||
|
export PM_API_TOKEN_SECRET="your-secret-here"
|
||||||
|
|
||||||
|
# Verify SSL (optional, set to false for self-signed certs)
|
||||||
|
export PM_TLS_INSECURE=true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Choose Your Use Case
|
||||||
|
|
||||||
|
We provide examples for common scenarios:
|
||||||
|
|
||||||
|
| Example | Description | Best For |
|
||||||
|
|---------|-------------|----------|
|
||||||
|
| [single-vm](./proxmox-examples/single-vm/) | Simple Ubuntu VM | Learning, testing |
|
||||||
|
| [docker-host](./proxmox-examples/docker-host/) | VM for Docker containers | Production homelab |
|
||||||
|
| [lxc-containers](./proxmox-examples/lxc-containers/) | Lightweight LXC containers | Resource efficiency |
|
||||||
|
| [multi-node](./proxmox-examples/multi-node/) | Multiple VMs/services | Complex deployments |
|
||||||
|
| [cloud-init](./proxmox-examples/cloud-init/) | Cloud-init automation | Production VMs |
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
terraform/
|
||||||
|
├── README.md # This file
|
||||||
|
├── proxmox-examples/
|
||||||
|
│ ├── single-vm/ # Simple VM example
|
||||||
|
│ │ ├── main.tf
|
||||||
|
│ │ ├── variables.tf
|
||||||
|
│ │ └── terraform.tfvars
|
||||||
|
│ ├── docker-host/ # Docker host VM
|
||||||
|
│ ├── lxc-containers/ # LXC container examples
|
||||||
|
│ ├── multi-node/ # Multiple VM deployment
|
||||||
|
│ └── cloud-init/ # Cloud-init examples
|
||||||
|
└── modules/ # Reusable modules (future)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Workflow
|
||||||
|
|
||||||
|
### Initialize
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd proxmox-examples/single-vm
|
||||||
|
tofu init
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plan
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu plan
|
||||||
|
```
|
||||||
|
|
||||||
|
Preview changes before applying.
|
||||||
|
|
||||||
|
### Apply
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu apply
|
||||||
|
```
|
||||||
|
|
||||||
|
Review plan and type `yes` to proceed.
|
||||||
|
|
||||||
|
### Destroy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu destroy
|
||||||
|
```
|
||||||
|
|
||||||
|
Removes all managed resources.
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Initialize and download providers
|
||||||
|
tofu init
|
||||||
|
|
||||||
|
# Validate configuration syntax
|
||||||
|
tofu validate
|
||||||
|
|
||||||
|
# Format code to standard style
|
||||||
|
tofu fmt
|
||||||
|
|
||||||
|
# Preview changes
|
||||||
|
tofu plan
|
||||||
|
|
||||||
|
# Apply changes
|
||||||
|
tofu apply
|
||||||
|
|
||||||
|
# Apply without confirmation (careful!)
|
||||||
|
tofu apply -auto-approve
|
||||||
|
|
||||||
|
# Show current state
|
||||||
|
tofu show
|
||||||
|
|
||||||
|
# List all resources
|
||||||
|
tofu state list
|
||||||
|
|
||||||
|
# Destroy specific resource
|
||||||
|
tofu destroy -target=proxmox_vm_qemu.vm
|
||||||
|
|
||||||
|
# Destroy everything
|
||||||
|
tofu destroy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provider Configuration
|
||||||
|
|
||||||
|
### Proxmox Provider
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
proxmox = {
|
||||||
|
source = "bpg/proxmox"
|
||||||
|
version = "~> 0.50"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "proxmox" {
|
||||||
|
endpoint = var.pm_api_url
|
||||||
|
api_token = "${var.pm_token_id}!${var.pm_token_secret}"
|
||||||
|
insecure = true # For self-signed certs
|
||||||
|
|
||||||
|
ssh {
|
||||||
|
agent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Use Variables
|
||||||
|
|
||||||
|
Don't hardcode values:
|
||||||
|
```hcl
|
||||||
|
# Bad
|
||||||
|
target_node = "pve"
|
||||||
|
|
||||||
|
# Good
|
||||||
|
target_node = var.proxmox_node
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Use terraform.tfvars
|
||||||
|
|
||||||
|
Store configuration separately:
|
||||||
|
```hcl
|
||||||
|
# terraform.tfvars
|
||||||
|
proxmox_node = "pve"
|
||||||
|
vm_name = "docker-host"
|
||||||
|
vm_cores = 4
|
||||||
|
vm_memory = 8192
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Version Control
|
||||||
|
|
||||||
|
**Commit:**
|
||||||
|
- ✅ `*.tf` files
|
||||||
|
- ✅ `*.tfvars` (if no secrets)
|
||||||
|
- ✅ `.terraform.lock.hcl`
|
||||||
|
|
||||||
|
**DO NOT commit:**
|
||||||
|
- ❌ `terraform.tfstate`
|
||||||
|
- ❌ `terraform.tfstate.backup`
|
||||||
|
- ❌ `.terraform/` directory
|
||||||
|
- ❌ Secrets/passwords
|
||||||
|
|
||||||
|
Use `.gitignore`:
|
||||||
|
```
|
||||||
|
.terraform/
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.backup
|
||||||
|
*.tfvars # If contains secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Use Modules
|
||||||
|
|
||||||
|
For reusable components:
|
||||||
|
```hcl
|
||||||
|
module "docker_vm" {
|
||||||
|
source = "./modules/docker-host"
|
||||||
|
|
||||||
|
vm_name = "docker-01"
|
||||||
|
cores = 4
|
||||||
|
memory = 8192
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. State Management
|
||||||
|
|
||||||
|
**Local State (default):**
|
||||||
|
- Simple, single-user
|
||||||
|
- State in `terraform.tfstate`
|
||||||
|
|
||||||
|
**Remote State (recommended for teams):**
|
||||||
|
```hcl
|
||||||
|
terraform {
|
||||||
|
backend "s3" {
|
||||||
|
bucket = "my-terraform-state"
|
||||||
|
key = "proxmox/terraform.tfstate"
|
||||||
|
region = "us-east-1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Use Cases
|
||||||
|
|
||||||
|
### Homelab Docker Host
|
||||||
|
|
||||||
|
Provision a VM optimized for Docker:
|
||||||
|
- 4-8 CPU cores
|
||||||
|
- 8-16GB RAM
|
||||||
|
- 50GB+ disk
|
||||||
|
- Ubuntu Server 24.04
|
||||||
|
- Docker pre-installed via cloud-init
|
||||||
|
|
||||||
|
See: `proxmox-examples/docker-host/`
|
||||||
|
|
||||||
|
### Development Environment
|
||||||
|
|
||||||
|
Multiple VMs for testing:
|
||||||
|
- Web server VM
|
||||||
|
- Database VM
|
||||||
|
- Application VM
|
||||||
|
- All networked together
|
||||||
|
|
||||||
|
See: `proxmox-examples/multi-node/`
|
||||||
|
|
||||||
|
### LXC Containers
|
||||||
|
|
||||||
|
Lightweight containers for services:
|
||||||
|
- Lower overhead than VMs
|
||||||
|
- Fast startup
|
||||||
|
- Resource efficient
|
||||||
|
|
||||||
|
See: `proxmox-examples/lxc-containers/`
|
||||||
|
|
||||||
|
## Proxmox Provider Resources
|
||||||
|
|
||||||
|
### Virtual Machines (QEMU)
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "proxmox_vm_qemu" "vm" {
|
||||||
|
name = "my-vm"
|
||||||
|
target_node = "pve"
|
||||||
|
|
||||||
|
clone = "ubuntu-cloud-template" # Template to clone
|
||||||
|
cores = 2
|
||||||
|
memory = 2048
|
||||||
|
|
||||||
|
disk {
|
||||||
|
size = "20G"
|
||||||
|
storage = "local-lvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
model = "virtio"
|
||||||
|
bridge = "vmbr0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### LXC Containers
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "proxmox_lxc" "container" {
|
||||||
|
hostname = "my-container"
|
||||||
|
target_node = "pve"
|
||||||
|
|
||||||
|
ostemplate = "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.gz"
|
||||||
|
cores = 1
|
||||||
|
memory = 512
|
||||||
|
|
||||||
|
rootfs {
|
||||||
|
storage = "local-lvm"
|
||||||
|
size = "8G"
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
name = "eth0"
|
||||||
|
bridge = "vmbr0"
|
||||||
|
ip = "dhcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cloud-Init
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "proxmox_vm_qemu" "cloudinit_vm" {
|
||||||
|
# ... basic config ...
|
||||||
|
|
||||||
|
ciuser = "ubuntu"
|
||||||
|
cipassword = var.vm_password
|
||||||
|
sshkeys = file("~/.ssh/id_rsa.pub")
|
||||||
|
|
||||||
|
ipconfig0 = "ip=dhcp"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### SSL Certificate Errors
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PM_TLS_INSECURE=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Or add to provider:
|
||||||
|
```hcl
|
||||||
|
provider "proxmox" {
|
||||||
|
insecure = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Permission Errors
|
||||||
|
|
||||||
|
Ensure API token has necessary permissions:
|
||||||
|
```bash
|
||||||
|
# In Proxmox shell
|
||||||
|
pveum acl modify / -token 'root@pam!terraform' -role Administrator
|
||||||
|
```
|
||||||
|
|
||||||
|
### VM Clone Errors
|
||||||
|
|
||||||
|
Ensure template exists:
|
||||||
|
```bash
|
||||||
|
# List VMs
|
||||||
|
qm list
|
||||||
|
|
||||||
|
# Check template flag
|
||||||
|
qm config 9000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timeout Errors
|
||||||
|
|
||||||
|
Increase timeout:
|
||||||
|
```hcl
|
||||||
|
resource "proxmox_vm_qemu" "vm" {
|
||||||
|
# ...
|
||||||
|
timeout_create = "30m"
|
||||||
|
timeout_clone = "30m"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration from Terraform
|
||||||
|
|
||||||
|
OpenTofu is a drop-in replacement:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rename binary
|
||||||
|
alias tofu=terraform
|
||||||
|
|
||||||
|
# Or replace commands
|
||||||
|
terraform → tofu
|
||||||
|
```
|
||||||
|
|
||||||
|
State files are compatible - no conversion needed!
|
||||||
|
|
||||||
|
## Advanced Topics
|
||||||
|
|
||||||
|
### Custom Cloud Images
|
||||||
|
|
||||||
|
1. Download cloud image
|
||||||
|
2. Create VM template
|
||||||
|
3. Use cloud-init for customization
|
||||||
|
|
||||||
|
See: `proxmox-examples/cloud-init/`
|
||||||
|
|
||||||
|
### Network Configuration
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# VLAN tagging
|
||||||
|
network {
|
||||||
|
model = "virtio"
|
||||||
|
bridge = "vmbr0"
|
||||||
|
tag = 100 # VLAN 100
|
||||||
|
}
|
||||||
|
|
||||||
|
# Multiple NICs
|
||||||
|
network {
|
||||||
|
model = "virtio"
|
||||||
|
bridge = "vmbr0"
|
||||||
|
}
|
||||||
|
network {
|
||||||
|
model = "virtio"
|
||||||
|
bridge = "vmbr1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Storage Options
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# Local LVM
|
||||||
|
disk {
|
||||||
|
storage = "local-lvm"
|
||||||
|
size = "50G"
|
||||||
|
type = "scsi"
|
||||||
|
}
|
||||||
|
|
||||||
|
# NFS/CIFS
|
||||||
|
disk {
|
||||||
|
storage = "nfs-storage"
|
||||||
|
size = "100G"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Multiple disks
|
||||||
|
disk {
|
||||||
|
slot = 0
|
||||||
|
size = "50G"
|
||||||
|
storage = "local-lvm"
|
||||||
|
}
|
||||||
|
disk {
|
||||||
|
slot = 1
|
||||||
|
size = "100G"
|
||||||
|
storage = "data"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recommended Resources
|
||||||
|
|
||||||
|
### Providers
|
||||||
|
|
||||||
|
- **[bpg/proxmox](https://registry.terraform.io/providers/bpg/proxmox)** - Most feature-complete (recommended)
|
||||||
|
- **[Telmate/proxmox](https://registry.terraform.io/providers/Telmate/proxmox)** - Legacy, still works
|
||||||
|
|
||||||
|
### Learning
|
||||||
|
|
||||||
|
- [OpenTofu Docs](https://opentofu.org/docs/)
|
||||||
|
- [Proxmox Provider Docs](https://registry.terraform.io/providers/bpg/proxmox/latest/docs)
|
||||||
|
- [Terraform/OpenTofu Tutorial](https://developer.hashicorp.com/terraform/tutorials)
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
|
||||||
|
- **[tflint](https://github.com/terraform-linters/tflint)** - Linting
|
||||||
|
- **[terraform-docs](https://github.com/terraform-docs/terraform-docs)** - Generate docs
|
||||||
|
- **[infracost](https://www.infracost.io/)** - Cost estimation
|
||||||
|
- **[terragrunt](https://terragrunt.gruntwork.io/)** - Wrapper for DRY configs
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Start Simple:** Try `proxmox-examples/single-vm/`
|
||||||
|
2. **Learn Basics:** Get familiar with plan/apply/destroy
|
||||||
|
3. **Expand:** Try docker-host or multi-node
|
||||||
|
4. **Customize:** Adapt examples to your needs
|
||||||
|
5. **Automate:** Integrate with CI/CD
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Check example READMEs in each directory
|
||||||
|
- Review Proxmox provider docs
|
||||||
|
- OpenTofu community Discord
|
||||||
|
- Ask in r/Proxmox or r/selfhosted
|
||||||
|
|
||||||
|
Happy Infrastructure as Code! 🚀
|
||||||
405
terraform/proxmox-examples/docker-host/README.md
Normal file
405
terraform/proxmox-examples/docker-host/README.md
Normal file
|
|
@ -0,0 +1,405 @@
|
||||||
|
# Docker Host VM with OpenTofu
|
||||||
|
|
||||||
|
This configuration creates a VM optimized for running Docker containers in your homelab.
|
||||||
|
|
||||||
|
## What This Creates
|
||||||
|
|
||||||
|
- ✅ Ubuntu VM (from cloud template)
|
||||||
|
- ✅ Docker & Docker Compose installed
|
||||||
|
- ✅ Homelab network created
|
||||||
|
- ✅ /media directories structure
|
||||||
|
- ✅ SSH key authentication
|
||||||
|
- ✅ Automatic updates enabled
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### 1. Create Ubuntu Cloud Template
|
||||||
|
|
||||||
|
First, create a cloud-init enabled template in Proxmox:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH to Proxmox server
|
||||||
|
ssh root@proxmox.local
|
||||||
|
|
||||||
|
# Download Ubuntu cloud image
|
||||||
|
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
|
||||||
|
|
||||||
|
# Create VM
|
||||||
|
qm create 9000 --name ubuntu-cloud-template --memory 2048 --net0 virtio,bridge=vmbr0
|
||||||
|
|
||||||
|
# Import disk
|
||||||
|
qm importdisk 9000 jammy-server-cloudimg-amd64.img local-lvm
|
||||||
|
|
||||||
|
# Attach disk
|
||||||
|
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0
|
||||||
|
|
||||||
|
# Add cloud-init drive
|
||||||
|
qm set 9000 --ide2 local-lvm:cloudinit
|
||||||
|
|
||||||
|
# Set boot disk
|
||||||
|
qm set 9000 --boot c --bootdisk scsi0
|
||||||
|
|
||||||
|
# Add serial console
|
||||||
|
qm set 9000 --serial0 socket --vga serial0
|
||||||
|
|
||||||
|
# Convert to template
|
||||||
|
qm template 9000
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm jammy-server-cloudimg-amd64.img
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create API Token
|
||||||
|
|
||||||
|
In Proxmox UI:
|
||||||
|
1. Datacenter → Permissions → API Tokens
|
||||||
|
2. Add → User: `root@pam`, Token ID: `terraform`
|
||||||
|
3. Uncheck "Privilege Separation"
|
||||||
|
4. Save the secret!
|
||||||
|
|
||||||
|
### 3. Install OpenTofu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh | sh
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
tofu version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Configure Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd terraform/proxmox-examples/docker-host
|
||||||
|
|
||||||
|
# Copy example config
|
||||||
|
cp terraform.tfvars.example terraform.tfvars
|
||||||
|
|
||||||
|
# Edit with your values
|
||||||
|
nano terraform.tfvars
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required changes:**
|
||||||
|
- `pm_api_token_secret` - Your Proxmox API secret
|
||||||
|
- `vm_ssh_keys` - Your SSH public key
|
||||||
|
- `vm_password` - Set a secure password
|
||||||
|
|
||||||
|
**Optional changes:**
|
||||||
|
- `vm_name` - Change VM name
|
||||||
|
- `vm_cores` / `vm_memory` - Adjust resources
|
||||||
|
- `vm_ip_address` - Set static IP (or keep DHCP)
|
||||||
|
|
||||||
|
### 2. Initialize
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu init
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Plan
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu plan
|
||||||
|
```
|
||||||
|
|
||||||
|
Review what will be created.
|
||||||
|
|
||||||
|
### 4. Apply
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu apply
|
||||||
|
```
|
||||||
|
|
||||||
|
Type `yes` to confirm.
|
||||||
|
|
||||||
|
### 5. Connect
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get SSH command from output
|
||||||
|
tofu output ssh_command
|
||||||
|
|
||||||
|
# Or manually
|
||||||
|
ssh ubuntu@<VM-IP>
|
||||||
|
|
||||||
|
# Verify Docker
|
||||||
|
docker --version
|
||||||
|
docker ps
|
||||||
|
docker network ls | grep homelab
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### Resource Sizing
|
||||||
|
|
||||||
|
**Light workload (1-5 containers):**
|
||||||
|
```hcl
|
||||||
|
vm_cores = 2
|
||||||
|
vm_memory = 4096
|
||||||
|
disk_size = "30"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Medium workload (5-15 containers):**
|
||||||
|
```hcl
|
||||||
|
vm_cores = 4
|
||||||
|
vm_memory = 8192
|
||||||
|
disk_size = "50"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Heavy workload (15+ containers):**
|
||||||
|
```hcl
|
||||||
|
vm_cores = 8
|
||||||
|
vm_memory = 16384
|
||||||
|
disk_size = "100"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Configuration
|
||||||
|
|
||||||
|
**DHCP (easiest):**
|
||||||
|
```hcl
|
||||||
|
vm_ip_address = "dhcp"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Static IP:**
|
||||||
|
```hcl
|
||||||
|
vm_ip_address = "192.168.1.100"
|
||||||
|
vm_ip_netmask = 24
|
||||||
|
vm_gateway = "192.168.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple SSH Keys
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
vm_ssh_keys = [
|
||||||
|
"ssh-rsa AAAAB3... user1@laptop",
|
||||||
|
"ssh-rsa AAAAB3... user2@desktop"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-Deployment
|
||||||
|
|
||||||
|
### Deploy Homelab Services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH to VM
|
||||||
|
ssh ubuntu@<VM-IP>
|
||||||
|
|
||||||
|
# Clone homelab repo (if not auto-cloned)
|
||||||
|
git clone https://github.com/efigueroa/homelab.git
|
||||||
|
cd homelab
|
||||||
|
|
||||||
|
# Deploy services
|
||||||
|
cd compose/core/traefik
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
cd ../lldap
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Continue with other services...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Docker
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
|
||||||
|
# Check network
|
||||||
|
docker network ls | grep homelab
|
||||||
|
|
||||||
|
# Check media directories
|
||||||
|
ls -la /media
|
||||||
|
|
||||||
|
# Check system resources
|
||||||
|
htop
|
||||||
|
df -h
|
||||||
|
```
|
||||||
|
|
||||||
|
## Managing the VM
|
||||||
|
|
||||||
|
### View State
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu show
|
||||||
|
tofu state list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update VM
|
||||||
|
|
||||||
|
1. Edit `terraform.tfvars`:
|
||||||
|
```hcl
|
||||||
|
vm_cores = 8 # Increase from 4
|
||||||
|
vm_memory = 16384 # Increase from 8192
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Apply changes:
|
||||||
|
```bash
|
||||||
|
tofu plan
|
||||||
|
tofu apply
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Some changes require VM restart.
|
||||||
|
|
||||||
|
### Destroy VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup any data first!
|
||||||
|
tofu destroy
|
||||||
|
```
|
||||||
|
|
||||||
|
Type `yes` to confirm deletion.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Template Not Found
|
||||||
|
|
||||||
|
Error: `template with ID 9000 not found`
|
||||||
|
|
||||||
|
**Solution:** Create cloud template (see Prerequisites)
|
||||||
|
|
||||||
|
### API Permission Error
|
||||||
|
|
||||||
|
Error: `permission denied`
|
||||||
|
|
||||||
|
**Solution:** Check API token permissions:
|
||||||
|
```bash
|
||||||
|
pveum acl modify / -token 'root@pam!terraform' -role Administrator
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cloud-Init Not Working
|
||||||
|
|
||||||
|
**Check cloud-init status:**
|
||||||
|
```bash
|
||||||
|
ssh ubuntu@<VM-IP>
|
||||||
|
sudo cloud-init status
|
||||||
|
sudo cat /var/log/cloud-init-output.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Not Installed
|
||||||
|
|
||||||
|
**Manual installation:**
|
||||||
|
```bash
|
||||||
|
ssh ubuntu@<VM-IP>
|
||||||
|
curl -fsSL https://get.docker.com | sudo sh
|
||||||
|
sudo usermod -aG docker ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
### VM Won't Start
|
||||||
|
|
||||||
|
**Check Proxmox logs:**
|
||||||
|
```bash
|
||||||
|
# On Proxmox server
|
||||||
|
qm status <VM-ID>
|
||||||
|
tail -f /var/log/pve/tasks/active
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Multiple VMs
|
||||||
|
|
||||||
|
Create `docker-host-02.tfvars`:
|
||||||
|
```hcl
|
||||||
|
vm_name = "docker-host-02"
|
||||||
|
vm_ip_address = "192.168.1.101"
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy:
|
||||||
|
```bash
|
||||||
|
tofu apply -var-file="docker-host-02.tfvars"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Cloud-Init
|
||||||
|
|
||||||
|
Edit `main.tf` to add custom cloud-init sections:
|
||||||
|
```yaml
|
||||||
|
users:
|
||||||
|
- name: myuser
|
||||||
|
groups: sudo, docker
|
||||||
|
shell: /bin/bash
|
||||||
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- zsh
|
||||||
|
- tmux
|
||||||
|
- neovim
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Attach Additional Disk
|
||||||
|
|
||||||
|
Add to `main.tf`:
|
||||||
|
```hcl
|
||||||
|
disk {
|
||||||
|
datastore_id = var.storage
|
||||||
|
size = 200
|
||||||
|
interface = "scsi1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Mount in cloud-init:
|
||||||
|
```yaml
|
||||||
|
mounts:
|
||||||
|
- ["/dev/sdb", "/mnt/data", "ext4", "defaults", "0", "0"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cost Analysis
|
||||||
|
|
||||||
|
**Resource Usage:**
|
||||||
|
- 4 cores, 8GB RAM, 50GB disk
|
||||||
|
- Running 24/7
|
||||||
|
|
||||||
|
**Homelab Cost:** $0 (uses existing hardware)
|
||||||
|
|
||||||
|
**If in cloud (comparison):**
|
||||||
|
- AWS: ~$50-100/month
|
||||||
|
- DigitalOcean: ~$40/month
|
||||||
|
- Linode: ~$40/month
|
||||||
|
|
||||||
|
**Homelab ROI:** Pays for itself in ~2-3 months!
|
||||||
|
|
||||||
|
## Security Hardening
|
||||||
|
|
||||||
|
### Enable Firewall
|
||||||
|
|
||||||
|
Add to cloud-init:
|
||||||
|
```yaml
|
||||||
|
runcmd:
|
||||||
|
- ufw default deny incoming
|
||||||
|
- ufw default allow outgoing
|
||||||
|
- ufw allow ssh
|
||||||
|
- ufw allow 80/tcp
|
||||||
|
- ufw allow 443/tcp
|
||||||
|
- ufw --force enable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable Password Authentication
|
||||||
|
|
||||||
|
After SSH key setup:
|
||||||
|
```yaml
|
||||||
|
ssh_pwauth: false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automatic Updates
|
||||||
|
|
||||||
|
Already enabled in cloud-init. Verify:
|
||||||
|
```bash
|
||||||
|
sudo systemctl status unattended-upgrades
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. ✅ Deploy core services (Traefik, LLDAP, Tinyauth)
|
||||||
|
2. ✅ Configure SSL certificates
|
||||||
|
3. ✅ Deploy media services
|
||||||
|
4. ✅ Set up backups (Restic)
|
||||||
|
5. ✅ Add monitoring (Prometheus/Grafana)
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [OpenTofu Docs](https://opentofu.org/docs/)
|
||||||
|
- [Proxmox Provider](https://registry.terraform.io/providers/bpg/proxmox/latest/docs)
|
||||||
|
- [Cloud-Init Docs](https://cloudinit.readthedocs.io/)
|
||||||
|
- [Docker Docs](https://docs.docker.com/)
|
||||||
155
terraform/proxmox-examples/docker-host/main.tf
Normal file
155
terraform/proxmox-examples/docker-host/main.tf
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
terraform {
|
||||||
|
required_version = ">= 1.6"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
proxmox = {
|
||||||
|
source = "bpg/proxmox"
|
||||||
|
version = "~> 0.50"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "proxmox" {
|
||||||
|
endpoint = var.pm_api_url
|
||||||
|
|
||||||
|
api_token = var.pm_api_token_secret != "" ? "${var.pm_api_token_id}=${var.pm_api_token_secret}" : null
|
||||||
|
|
||||||
|
# For self-signed certificates
|
||||||
|
insecure = var.pm_tls_insecure
|
||||||
|
|
||||||
|
ssh {
|
||||||
|
agent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "proxmox_virtual_environment_vm" "docker_host" {
|
||||||
|
name = var.vm_name
|
||||||
|
description = "Docker host for homelab services - Managed by OpenTofu"
|
||||||
|
node_name = var.proxmox_node
|
||||||
|
|
||||||
|
# Clone from template (must exist in Proxmox)
|
||||||
|
clone {
|
||||||
|
vm_id = var.template_vm_id
|
||||||
|
full = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# CPU configuration
|
||||||
|
cpu {
|
||||||
|
cores = var.vm_cores
|
||||||
|
type = "host" # Use host CPU type for best performance
|
||||||
|
}
|
||||||
|
|
||||||
|
# Memory configuration
|
||||||
|
memory {
|
||||||
|
dedicated = var.vm_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
# Network interface
|
||||||
|
network_device {
|
||||||
|
bridge = var.network_bridge
|
||||||
|
model = "virtio"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disk configuration
|
||||||
|
disk {
|
||||||
|
datastore_id = var.storage
|
||||||
|
size = var.disk_size
|
||||||
|
interface = "scsi0"
|
||||||
|
discard = "on" # Enable TRIM for SSDs
|
||||||
|
iothread = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cloud-init configuration
|
||||||
|
initialization {
|
||||||
|
ip_config {
|
||||||
|
ipv4 {
|
||||||
|
address = var.vm_ip_address == "dhcp" ? "dhcp" : "${var.vm_ip_address}/${var.vm_ip_netmask}"
|
||||||
|
gateway = var.vm_gateway
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user_account {
|
||||||
|
username = var.vm_username
|
||||||
|
keys = var.vm_ssh_keys
|
||||||
|
password = var.vm_password
|
||||||
|
}
|
||||||
|
|
||||||
|
user_data_file_id = proxmox_virtual_environment_file.cloud_init_user_data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start VM on boot
|
||||||
|
on_boot = true
|
||||||
|
|
||||||
|
# Tags for organization
|
||||||
|
tags = ["terraform", "docker", "homelab"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cloud-init user data for Docker installation
|
||||||
|
resource "proxmox_virtual_environment_file" "cloud_init_user_data" {
|
||||||
|
content_type = "snippets"
|
||||||
|
datastore_id = "local"
|
||||||
|
node_name = var.proxmox_node
|
||||||
|
|
||||||
|
source_raw {
|
||||||
|
data = <<-EOF
|
||||||
|
#cloud-config
|
||||||
|
hostname: ${var.vm_name}
|
||||||
|
manage_etc_hosts: true
|
||||||
|
|
||||||
|
# Install Docker and dependencies
|
||||||
|
package_update: true
|
||||||
|
package_upgrade: true
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- apt-transport-https
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- gnupg
|
||||||
|
- lsb-release
|
||||||
|
- git
|
||||||
|
- vim
|
||||||
|
- htop
|
||||||
|
- net-tools
|
||||||
|
|
||||||
|
# Add Docker's official GPG key and repository
|
||||||
|
runcmd:
|
||||||
|
- mkdir -p /etc/apt/keyrings
|
||||||
|
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
- chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
- systemctl enable docker
|
||||||
|
- systemctl start docker
|
||||||
|
- usermod -aG docker ${var.vm_username}
|
||||||
|
- docker network create homelab || true
|
||||||
|
|
||||||
|
# Create media directories
|
||||||
|
write_files:
|
||||||
|
- path: /usr/local/bin/setup-media-dirs
|
||||||
|
permissions: '0755'
|
||||||
|
content: |
|
||||||
|
#!/bin/bash
|
||||||
|
mkdir -p /media/{audiobooks,books,comics,complete,downloads,homemovies,incomplete,movies,music,photos,tv}
|
||||||
|
chown -R ${var.vm_username}:${var.vm_username} /media
|
||||||
|
chmod -R 755 /media
|
||||||
|
|
||||||
|
# Run setup script
|
||||||
|
runcmd:
|
||||||
|
- /usr/local/bin/setup-media-dirs
|
||||||
|
|
||||||
|
# Optional: Clone homelab repo
|
||||||
|
${var.clone_homelab_repo ? "- su - ${var.vm_username} -c 'cd ~ && git clone https://github.com/${var.github_username}/homelab.git'" : "# Homelab repo cloning disabled"}
|
||||||
|
|
||||||
|
# Set timezone
|
||||||
|
timezone: ${var.vm_timezone}
|
||||||
|
|
||||||
|
# Reboot after setup
|
||||||
|
power_state:
|
||||||
|
mode: reboot
|
||||||
|
condition: true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
file_name = "cloud-init-docker-${var.vm_name}.yaml"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
terraform/proxmox-examples/docker-host/outputs.tf
Normal file
29
terraform/proxmox-examples/docker-host/outputs.tf
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
output "vm_id" {
|
||||||
|
description = "VM ID"
|
||||||
|
value = proxmox_virtual_environment_vm.docker_host.vm_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vm_name" {
|
||||||
|
description = "VM name"
|
||||||
|
value = proxmox_virtual_environment_vm.docker_host.name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vm_ipv4_address" {
|
||||||
|
description = "VM IPv4 address"
|
||||||
|
value = try(proxmox_virtual_environment_vm.docker_host.ipv4_addresses[1][0], "DHCP - check Proxmox UI")
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vm_mac_address" {
|
||||||
|
description = "VM MAC address"
|
||||||
|
value = proxmox_virtual_environment_vm.docker_host.mac_addresses[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ssh_command" {
|
||||||
|
description = "SSH command to connect to VM"
|
||||||
|
value = "ssh ${var.vm_username}@${try(proxmox_virtual_environment_vm.docker_host.ipv4_addresses[1][0], "DHCP-ADDRESS")}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "docker_status_command" {
|
||||||
|
description = "Command to check Docker status"
|
||||||
|
value = "ssh ${var.vm_username}@${try(proxmox_virtual_environment_vm.docker_host.ipv4_addresses[1][0], "DHCP-ADDRESS")} 'docker ps'"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Proxmox Connection
|
||||||
|
pm_api_url = "https://proxmox.local:8006"
|
||||||
|
pm_api_token_id = "root@pam!terraform"
|
||||||
|
pm_api_token_secret = "your-secret-here"
|
||||||
|
pm_tls_insecure = true
|
||||||
|
|
||||||
|
# Proxmox Configuration
|
||||||
|
proxmox_node = "pve"
|
||||||
|
template_vm_id = 9000
|
||||||
|
storage = "local-lvm"
|
||||||
|
network_bridge = "vmbr0"
|
||||||
|
|
||||||
|
# VM Configuration
|
||||||
|
vm_name = "docker-host"
|
||||||
|
vm_cores = 4
|
||||||
|
vm_memory = 8192
|
||||||
|
disk_size = "50"
|
||||||
|
|
||||||
|
# Network Configuration
|
||||||
|
vm_ip_address = "dhcp" # Or "192.168.1.100" for static
|
||||||
|
vm_ip_netmask = 24
|
||||||
|
vm_gateway = "192.168.1.1"
|
||||||
|
|
||||||
|
# User Configuration
|
||||||
|
vm_username = "ubuntu"
|
||||||
|
vm_password = "changeme_please_set_secure_password"
|
||||||
|
vm_ssh_keys = [
|
||||||
|
"ssh-rsa AAAAB3NzaC1yc2E... your-key-here"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
vm_timezone = "America/Los_Angeles"
|
||||||
|
clone_homelab_repo = true
|
||||||
|
github_username = "efigueroa"
|
||||||
125
terraform/proxmox-examples/docker-host/variables.tf
Normal file
125
terraform/proxmox-examples/docker-host/variables.tf
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
variable "pm_api_url" {
|
||||||
|
description = "Proxmox API URL"
|
||||||
|
type = string
|
||||||
|
default = "https://proxmox.local:8006"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pm_api_token_id" {
|
||||||
|
description = "Proxmox API token ID (format: user@realm!tokenid)"
|
||||||
|
type = string
|
||||||
|
default = "root@pam!terraform"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pm_api_token_secret" {
|
||||||
|
description = "Proxmox API token secret"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pm_tls_insecure" {
|
||||||
|
description = "Disable TLS verification for self-signed certificates"
|
||||||
|
type = bool
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "proxmox_node" {
|
||||||
|
description = "Proxmox node name"
|
||||||
|
type = string
|
||||||
|
default = "pve"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_name" {
|
||||||
|
description = "VM name"
|
||||||
|
type = string
|
||||||
|
default = "docker-host"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "template_vm_id" {
|
||||||
|
description = "Template VM ID to clone from"
|
||||||
|
type = number
|
||||||
|
default = 9000
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_cores" {
|
||||||
|
description = "Number of CPU cores"
|
||||||
|
type = number
|
||||||
|
default = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_memory" {
|
||||||
|
description = "Memory in MB"
|
||||||
|
type = number
|
||||||
|
default = 8192
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "disk_size" {
|
||||||
|
description = "Disk size (e.g., 50G, 100G)"
|
||||||
|
type = string
|
||||||
|
default = "50"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "storage" {
|
||||||
|
description = "Storage pool name"
|
||||||
|
type = string
|
||||||
|
default = "local-lvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "network_bridge" {
|
||||||
|
description = "Network bridge"
|
||||||
|
type = string
|
||||||
|
default = "vmbr0"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_ip_address" {
|
||||||
|
description = "Static IP address or 'dhcp'"
|
||||||
|
type = string
|
||||||
|
default = "dhcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_ip_netmask" {
|
||||||
|
description = "Network netmask (CIDR notation, e.g., 24)"
|
||||||
|
type = number
|
||||||
|
default = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_gateway" {
|
||||||
|
description = "Network gateway"
|
||||||
|
type = string
|
||||||
|
default = "192.168.1.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_username" {
|
||||||
|
description = "VM username"
|
||||||
|
type = string
|
||||||
|
default = "ubuntu"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_password" {
|
||||||
|
description = "VM user password"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_ssh_keys" {
|
||||||
|
description = "List of SSH public keys"
|
||||||
|
type = list(string)
|
||||||
|
default = []
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vm_timezone" {
|
||||||
|
description = "VM timezone"
|
||||||
|
type = string
|
||||||
|
default = "America/Los_Angeles"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "clone_homelab_repo" {
|
||||||
|
description = "Clone homelab repository on first boot"
|
||||||
|
type = bool
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "github_username" {
|
||||||
|
description = "GitHub username for homelab repo"
|
||||||
|
type = string
|
||||||
|
default = "efigueroa"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue