13 KiB
Build.func Wiki
Central LXC container build and configuration orchestration engine providing the main creation workflow, 19-step advanced wizard, defaults system, variable management, and state machine for container lifecycle.
📋 Table of Contents
- Overview
- Core Functions
- Variable Management
- Build Workflow
- Advanced Settings Wizard
- Defaults System
- Best Practices
- Development Mode
- Contributing
Overview
Build.func is the 3800+ line orchestration engine for LXC container creation:
- ✅ 19-step interactive advanced settings wizard
- ✅ 3-tier defaults precedence system (app → user → global)
- ✅ Variable whitelisting for security
- ✅ State machine workflow management
- ✅ Container resource allocation (CPU, RAM, disk)
- ✅ Storage selection and validation
- ✅ Network configuration (bridge, MAC, VLAN, IPv6)
- ✅ Session tracking and logging
- ✅ Comprehensive pre-flight validation checks
Execution Flow
Script Invocation
↓
variables() → Initialize core variables, SESSION_ID, UUID
↓
build.func functions sourced
↓
Pre-flight checks (maxkeys, template availability)
↓
Create container (pct create ...)
↓
Network configuration
↓
Storage tuning
↓
Installation script execution
↓
Completion & cleanup
Core Functions
variables()
Purpose: Initializes all core variables, generates unique session ID, and captures application defaults for precedence logic.
Signature:
variables()
Parameters: None
Returns: No explicit return value (sets global variables)
Variables Initialized:
| Variable | Source | Purpose |
|---|---|---|
NSAPP |
APP converted to lowercase |
Normalized app name |
var_install |
${NSAPP}-install |
Installation script name |
PVEHOST_NAME |
hostname |
Proxmox hostname |
DIAGNOSTICS |
Set to "yes" | Enable telemetry |
METHOD |
Set to "default" | Setup method |
RANDOM_UUID |
/proc/sys/kernel/random/uuid |
Session UUID |
SESSION_ID |
First 8 chars of UUID | Short session ID |
BUILD_LOG |
/tmp/create-lxc-${SESSION_ID}.log |
Host-side log file |
PVEVERSION |
pveversion |
Proxmox VE version |
KERNEL_VERSION |
uname -r |
System kernel version |
App Default Capture (3-tier precedence):
# Tier 1: App-declared defaults (highest priority)
APP_DEFAULT_CPU=${var_cpu:-}
APP_DEFAULT_RAM=${var_ram:-}
APP_DEFAULT_DISK=${var_disk:-}
# Tier 2: User configuration (~/.community-scripts/defaults)
# Tier 3: Global defaults (built-in)
Dev Mode Setup:
# Parse dev_mode early for special behaviors
parse_dev_mode
# If dev_mode=logs, use persistent logging location
if [[ "${DEV_MODE_LOGS}" == "true" ]]; then
mkdir -p /var/log/community-scripts
BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log"
fi
Usage Examples:
# Example 1: Initialize with default app
APP="Jellyfin"
variables
# Result:
# NSAPP="jellyfin"
# SESSION_ID="550e8400"
# BUILD_LOG="/tmp/create-lxc-550e8400.log"
# Example 2: With dev mode
dev_mode="trace,logs"
APP="MyApp"
variables
# Result:
# Persistent logging enabled
# Bash tracing configured
# BUILD_LOG="/var/log/community-scripts/create-lxc-550e8400-20241201_103000.log"
maxkeys_check()
Purpose: Validates kernel keyring limits don't prevent container creation (prevents "key quota exceeded" errors).
Signature:
maxkeys_check()
Parameters: None
Returns: 0 if limits acceptable; exits with error if exceeded
Checks:
/proc/sys/kernel/keys/maxkeys- Maximum keys per user/proc/sys/kernel/keys/maxbytes- Maximum key bytes per user/proc/key-users- Current usage for UID 100000 (LXC user)
Warning Thresholds:
- Keys: Current >= (maxkeys - 100)
- Bytes: Current >= (maxbytes - 1000)
Recovery Suggestions:
# If warning triggered, suggests sysctl configuration
sysctl -w kernel.keys.maxkeys=200000
sysctl -w kernel.keys.maxbytes=40000000
# Add to persistent config
echo "kernel.keys.maxkeys=200000" >> /etc/sysctl.d/98-community-scripts.conf
sysctl -p
Usage Examples:
# Example 1: Healthy keyring usage
maxkeys_check
# Silent success: Usage is normal
# Example 2: Near limit
maxkeys_check
# Warning displayed with suggested sysctl values
# Allows continuation but recommends tuning
# Example 3: Exceeded limit
maxkeys_check
# Error: Exits with code 1
# Suggests increasing limits before retry
Variable Management
default_var_settings()
Purpose: Loads or creates default variable settings with 3-tier precedence.
Signature:
default_var_settings()
Precedence Order:
1. App-declared defaults (var_cpu, var_ram, var_disk from script)
2. User defaults (~/.community-scripts/defaults.sh)
3. Global built-in defaults
User Defaults Location:
~/.community-scripts/defaults.sh
Example User Defaults File:
# ~/.community-scripts/defaults.sh
CORE_COUNT=4 # Override default CPU
RAM_SIZE=4096 # Override default RAM (MB)
DISK_SIZE=32 # Override default disk (GB)
BRIDGE="vmbr0" # Preferred bridge
STORAGE="local-lvm" # Preferred storage
DISABLEIPV6="no" # Network preference
VERBOSE="no" # Output preference
load_vars_file()
Purpose: Loads saved container variables from previous configuration.
Signature:
load_vars_file()
Parameters: None
Returns: 0 if loaded; 1 if no saved config found
File Location:
~/.community-scripts/${NSAPP}.vars
Variables Loaded:
- All whitelist-approved variables (CORE_COUNT, RAM_SIZE, DISK_SIZE, etc.)
- Saved settings from previous container creation
Usage Examples:
# Example 1: Load previous config
if load_vars_file; then
msg_ok "Loaded previous settings for $NSAPP"
else
msg_info "No previous configuration found, using defaults"
fi
# Example 2: Offer to use saved config
# Interactive: Prompts user to confirm previously saved values
maybe_offer_save_app_defaults()
Purpose: Optionally saves current configuration for reuse in future container creations.
Signature:
maybe_offer_save_app_defaults()
Parameters: None
Returns: No explicit return value (saves or skips)
Behavior:
- Prompts user if they want to save current settings
- Saves to
~/.community-scripts/${NSAPP}.vars - User can load these settings in future runs via
load_vars_file() - Saves whitelisted variables only (security)
Variables Saved:
- CORE_COUNT, RAM_SIZE, DISK_SIZE
- BRIDGE, STORAGE, MACADDRESS
- VLAN_TAG, DISABLEIPV6
- PASSWORD settings
- Custom network configuration
Usage Examples:
# Example 1: After configuration
configure_container
# ... all settings done ...
maybe_offer_save_app_defaults
# Prompts: "Save these settings for future use? [y/n]"
# If yes: Saves to ~/.community-scripts/jellyfin.vars
# Example 2: Reload in next run
# User runs script again
# Prompted: "Use saved settings from last time? [y/n]"
# If yes: Load_vars_file() populates all variables
Build Workflow
install_script()
Purpose: Orchestrates container installation workflow inside the LXC container.
Signature:
install_script()
Parameters: None (uses global NSAPP variable)
Returns: 0 on success; exits with error code on failure
Installation Steps:
- Copy install script into container
- Execute via
pct exec $CTID bash /tmp/... - Capture output and exit code
- Report completion to API
- Handle errors with cleanup
Error Handling:
# If installation fails:
# - Captures exit code
# - Posts failure to API (if telemetry enabled)
# - Displays error with explanation
# - Offers debug shell (if DEV_MODE_BREAKPOINT)
# - Cleans up container (unless DEV_MODE_KEEP)
Advanced Settings Wizard
advanced_settings()
Purpose: Interactive 19-step wizard for advanced container configuration.
Signature:
advanced_settings()
Parameters: None
Returns: No explicit return value (populates variables)
Wizard Steps (19 total):
- CPU Cores - Allocation (1-128)
- RAM Size - Allocation in MB (256-65536)
- Disk Size - Allocation in GB (1-4096)
- Storage - Select storage backend (local, local-lvm, etc.)
- Bridge - Network bridge (vmbr0, vmbr1, etc.)
- MAC Address - Custom or auto-generated
- VLAN Tag - Optional VLAN configuration
- IPv6 - Enable/disable IPv6
- Disable IPV6 - Explicit disable option
- DHCP - DHCP or static IP
- IP Configuration - If static: IP/mask
- Gateway - Network gateway
- DNS - DNS server configuration
- Hostname - Container hostname
- Root Password - Set or leave empty (auto-login)
- SSH Access - Enable root SSH
- Features - FUSE, Nesting, keyctl, etc.
- Start on Boot - Autostart configuration
- Privileged Mode - Privileged or unprivileged container
User Input Methods:
- Whiptail dialogs (graphical)
- Command-line prompts (fallback)
- Validation of all inputs
- Confirmation summary before creation
Usage Examples:
# Example 1: Run wizard
advanced_settings
# User prompted for each of 19 settings
# Responses stored in variables
# Example 2: Scripted (skip prompts)
CORE_COUNT=4
RAM_SIZE=4096
DISK_SIZE=32
# ... set all 19 variables ...
# advanced_settings() skips prompts since variables already set
Defaults System
3-Tier Precedence Logic
Tier 1 (Highest Priority): App-Declared Defaults
# In app script header (before default.vars sourced):
var_cpu=4
var_ram=2048
var_disk=20
# If user has higher value in tier 2/3, app value takes precedence
Tier 2 (Medium Priority): User Defaults
# In ~/.community-scripts/defaults.sh:
CORE_COUNT=6
RAM_SIZE=4096
DISK_SIZE=32
# Can be overridden by app defaults (tier 1)
Tier 3 (Lowest Priority): Global Built-in Defaults
# Built into build.func:
CORE_COUNT=2 (default)
RAM_SIZE=2048 (default, in MB)
DISK_SIZE=8 (default, in GB)
Resolution Algorithm:
# For CPU cores (example):
if [ -n "$APP_DEFAULT_CPU" ]; then
CORE_COUNT=$APP_DEFAULT_CPU # Tier 1 wins
elif [ -n "$USER_DEFAULT_CPU" ]; then
CORE_COUNT=$USER_DEFAULT_CPU # Tier 2
else
CORE_COUNT=2 # Tier 3 (global)
fi
Best Practices
1. Always Call variables() First
#!/bin/bash
source <(curl -fsSL .../build.func)
load_functions
catch_errors
# Must be first real function call
variables
# Then safe to use SESSION_ID, BUILD_LOG, etc.
msg_info "Building container (Session: $SESSION_ID)"
2. Declare App Defaults Before Sourcing build.func
#!/bin/bash
# Declare app defaults BEFORE sourcing build.func
var_cpu=4
var_ram=4096
var_disk=20
source <(curl -fsSL .../build.func)
variables # These defaults are captured
# Now var_cpu, var_ram, var_disk are in APP_DEFAULT_*
3. Use Variable Whitelisting
# Only these variables are allowed to be saved/loaded:
WHITELIST="CORE_COUNT RAM_SIZE DISK_SIZE BRIDGE STORAGE MACADDRESS VLAN_TAG DISABLEIPV6"
# Sensitive variables are NEVER saved:
# PASSWORD, SSH keys, API tokens, etc.
4. Check Pre-flight Conditions
variables
maxkeys_check # Validate kernel limits
pve_check # Validate PVE version
arch_check # Validate architecture
# Only proceed after all checks pass
msg_ok "Pre-flight checks passed"
5. Track Sessions
# Use SESSION_ID in all logs
BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log"
# Keep logs for troubleshooting
# Can be reviewed later: tail -50 /tmp/create-lxc-550e8400.log
Development Mode
Dev Mode Variables
Set via environment or in script:
dev_mode="trace,keep,breakpoint"
parse_dev_mode
# Enables:
# - DEV_MODE_TRACE=true (bash -x)
# - DEV_MODE_KEEP=true (never delete container)
# - DEV_MODE_BREAKPOINT=true (shell on error)
Debug Container Creation
# Run with all debugging enabled
dev_mode="trace,keep,logs" bash ct/jellyfin.sh
# Then review logs:
tail -200 /var/log/community-scripts/create-lxc-*.log
# Container stays running (DEV_MODE_KEEP)
# Allows ssh inspection: ssh root@<container-ip>
Contributing
Adding New Wizard Steps
- Add step number and variable to documentation
- Add whiptail prompt in
advanced_settings() - Add validation logic
- Add to whitelist if user should save it
- Update documentation with examples
Extending Defaults System
To add new tier or change precedence:
- Update 3-tier logic section
- Modify resolution algorithm
- Document new precedence order
- Update whitelist accordingly
Testing Build Workflow
# Test with dry-run mode
dev_mode="dryrun" bash ct/myapp.sh
# Shows all commands without executing
# Test with keep mode
dev_mode="keep" bash ct/myapp.sh
# Container stays if fails, allows inspection
Notes
- Build.func is large and complex (3800+ lines) - handles most container creation logic
- Variables are passed to container via pct set/environment
- Session ID enables request tracking across distributed logs
- Defaults system is flexible (3-tier precedence)
- Pre-flight checks prevent many common errors