# Unattended Deployments Guide Complete guide for automated, zero-interaction container deployments using community-scripts for Proxmox VE. --- ## 🎯 What You'll Learn This comprehensive guide covers: - ✅ Complete automation of container deployments - ✅ Zero-interaction installations - ✅ Batch deployments (multiple containers) - ✅ Infrastructure as Code (Ansible, Terraform) - ✅ CI/CD pipeline integration - ✅ Error handling and rollback strategies - ✅ Production-ready deployment scripts - ✅ Security best practices --- ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites](#prerequisites) 3. [Deployment Methods](#deployment-methods) 4. [Single Container Deployment](#single-container-deployment) 5. [Batch Deployments](#batch-deployments) 6. [Infrastructure as Code](#infrastructure-as-code) 7. [CI/CD Integration](#cicd-integration) 8. [Error Handling](#error-handling) 9. [Security Considerations](#security-considerations) --- ## Overview Unattended deployments allow you to: - ✅ Deploy containers without manual interaction - ✅ Automate infrastructure provisioning - ✅ Integrate with CI/CD pipelines - ✅ Maintain consistent configurations - ✅ Scale deployments across multiple nodes --- ## Prerequisites ### 1. Proxmox VE Access ```bash # Verify you have root access whoami # Should return: root # Check Proxmox version (8.0+ or 9.0-9.1 required) pveversion ``` ### 2. Network Connectivity ```bash # Test GitHub access curl -I https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh # Test internet connectivity ping -c 1 1.1.1.1 ``` ### 3. Storage Available ```bash # List available storage pvesm status # Check free space df -h ``` --- ## Deployment Methods ### Method Comparison | Method | Use Case | Complexity | Flexibility | |--------|----------|------------|-------------| | **Environment Variables** | Quick one-offs | Low | High | | **App Defaults** | Repeat deployments | Low | Medium | | **Shell Scripts** | Batch operations | Medium | High | | **Ansible** | Infrastructure as Code | High | Very High | | **Terraform** | Cloud-native IaC | High | Very High | --- ## Single Container Deployment ### Basic Unattended Deployment **Simplest form:** ```bash var_hostname=myserver bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/ct/debian.sh)" ``` ### Complete Configuration Example ```bash #!/bin/bash # deploy-single.sh - Deploy a single container with full configuration var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname=production-app \ var_brg=vmbr0 \ var_net=dhcp \ var_ipv6_method=none \ var_ssh=yes \ var_ssh_authorized_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ... admin@workstation" \ var_nesting=1 \ var_tags=production,automated \ var_protection=yes \ var_verbose=no \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)" echo "✓ Container deployed successfully" ``` ### Using App Defaults **Step 1: Create defaults once (interactive)** ```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/pihole.sh)" # Select "Advanced Settings" → Configure → Save as "App Defaults" ``` **Step 2: Deploy unattended (uses saved defaults)** ```bash #!/bin/bash # deploy-with-defaults.sh # App defaults are loaded automatically bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/pihole.sh)" # Script will use /usr/local/community-scripts/defaults/pihole.vars ``` --- ## Batch Deployments ### Deploy Multiple Containers #### Simple Loop ```bash #!/bin/bash # batch-deploy-simple.sh apps=("debian" "ubuntu" "alpine") for app in "${apps[@]}"; do echo "Deploying $app..." var_hostname="$app-container" \ var_cpu=2 \ var_ram=2048 \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)" echo "✓ $app deployed" sleep 5 # Wait between deployments done ``` #### Advanced with Configuration Array ```bash #!/bin/bash # batch-deploy-advanced.sh - Deploy multiple containers with individual configs declare -A CONTAINERS=( ["pihole"]="2:1024:8:vmbr0:dns,network" ["homeassistant"]="4:4096:20:vmbr0:automation,ha" ["docker"]="6:8192:50:vmbr1:containers,docker" ["nginx"]="2:2048:10:vmbr0:webserver,proxy" ) for app in "${!CONTAINERS[@]}"; do # Parse configuration IFS=':' read -r cpu ram disk bridge tags <<< "${CONTAINERS[$app]}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Deploying: $app" echo " CPU: $cpu cores" echo " RAM: $ram MB" echo " Disk: $disk GB" echo " Bridge: $bridge" echo " Tags: $tags" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Deploy container var_unprivileged=1 \ var_cpu="$cpu" \ var_ram="$ram" \ var_disk="$disk" \ var_hostname="$app" \ var_brg="$bridge" \ var_net=dhcp \ var_ipv6_method=none \ var_ssh=yes \ var_tags="$tags,automated" \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)" 2>&1 | tee "deploy-${app}.log" if [ $? -eq 0 ]; then echo "✓ $app deployed successfully" else echo "✗ $app deployment failed - check deploy-${app}.log" fi sleep 5 done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Batch deployment complete!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ``` #### Parallel Deployment ```bash #!/bin/bash # parallel-deploy.sh - Deploy multiple containers in parallel deploy_container() { local app="$1" local cpu="$2" local ram="$3" local disk="$4" echo "[$app] Starting deployment..." var_cpu="$cpu" \ var_ram="$ram" \ var_disk="$disk" \ var_hostname="$app" \ var_net=dhcp \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)" \ &> "deploy-${app}.log" echo "[$app] ✓ Completed" } # Export function for parallel execution export -f deploy_container # Deploy in parallel (max 3 at a time) parallel -j 3 deploy_container ::: \ "debian 2 2048 10" \ "ubuntu 2 2048 10" \ "alpine 1 1024 5" \ "pihole 2 1024 8" \ "docker 4 4096 30" echo "All deployments complete!" ``` --- ## Infrastructure as Code ### Ansible Playbook #### Basic Playbook ```yaml --- # playbook-proxmox.yml - name: Deploy ProxmoxVED Containers hosts: proxmox_hosts become: yes tasks: - name: Deploy Debian Container shell: | var_unprivileged=1 \ var_cpu=2 \ var_ram=2048 \ var_disk=10 \ var_hostname=debian-{{ inventory_hostname }} \ var_net=dhcp \ var_ssh=yes \ var_tags=ansible,automated \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)" args: executable: /bin/bash register: deploy_result - name: Display deployment result debug: var: deploy_result.stdout_lines ``` #### Advanced Playbook with Variables ```yaml --- # advanced-playbook.yml - name: Deploy Multiple Container Types hosts: proxmox vars: containers: - name: pihole cpu: 2 ram: 1024 disk: 8 tags: "dns,network" - name: homeassistant cpu: 4 ram: 4096 disk: 20 tags: "automation,ha" - name: docker cpu: 6 ram: 8192 disk: 50 tags: "containers,docker" ssh_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" tasks: - name: Ensure community-scripts directory exists file: path: /usr/local/community-scripts/defaults state: directory mode: '0755' - name: Deploy containers shell: | var_unprivileged=1 \ var_cpu={{ item.cpu }} \ var_ram={{ item.ram }} \ var_disk={{ item.disk }} \ var_hostname={{ item.name }} \ var_brg=vmbr0 \ var_net=dhcp \ var_ipv6_method=none \ var_ssh=yes \ var_ssh_authorized_key="{{ ssh_key }}" \ var_tags="{{ item.tags }},ansible" \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/{{ item.name }}.sh)" args: executable: /bin/bash loop: "{{ containers }}" register: deployment_results - name: Wait for containers to be ready wait_for: timeout: 60 - name: Report deployment status debug: msg: "Deployed {{ item.item.name }} - Status: {{ 'Success' if item.rc == 0 else 'Failed' }}" loop: "{{ deployment_results.results }}" ``` Run with: ```bash ansible-playbook -i inventory.ini advanced-playbook.yml ``` ### Terraform Integration ```hcl # main.tf - Deploy containers via Terraform terraform { required_providers { proxmox = { source = "telmate/proxmox" version = "2.9.14" } } } provider "proxmox" { pm_api_url = "https://proxmox.example.com:8006/api2/json" pm_api_token_id = "terraform@pam!terraform" pm_api_token_secret = var.proxmox_token } resource "null_resource" "deploy_container" { for_each = var.containers provisioner "remote-exec" { inline = [ "var_unprivileged=1", "var_cpu=${each.value.cpu}", "var_ram=${each.value.ram}", "var_disk=${each.value.disk}", "var_hostname=${each.key}", "var_net=dhcp", "bash -c \"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${each.value.template}.sh)\"" ] connection { type = "ssh" host = var.proxmox_host user = "root" private_key = file("~/.ssh/id_rsa") } } } variable "containers" { type = map(object({ template = string cpu = number ram = number disk = number })) default = { "pihole" = { template = "pihole" cpu = 2 ram = 1024 disk = 8 } "homeassistant" = { template = "homeassistant" cpu = 4 ram = 4096 disk = 20 } } } ``` --- ## CI/CD Integration ### GitHub Actions ```yaml # .github/workflows/deploy-container.yml name: Deploy Container to Proxmox on: push: branches: [main] workflow_dispatch: inputs: container_type: description: 'Container type to deploy' required: true type: choice options: - debian - ubuntu - docker - pihole jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to Proxmox uses: appleboy/ssh-action@v0.1.10 with: host: ${{ secrets.PROXMOX_HOST }} username: root key: ${{ secrets.SSH_PRIVATE_KEY }} script: | var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname=${{ github.event.inputs.container_type }}-ci \ var_net=dhcp \ var_ssh=yes \ var_tags=ci-cd,automated \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${{ github.event.inputs.container_type }}.sh)" - name: Notify deployment status if: success() run: echo "✓ Container deployed successfully" ``` ### GitLab CI ```yaml # .gitlab-ci.yml stages: - deploy deploy_container: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client curl bash - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan $PROXMOX_HOST >> ~/.ssh/known_hosts script: - | ssh root@$PROXMOX_HOST << 'EOF' var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname=gitlab-ci-container \ var_net=dhcp \ var_tags=gitlab-ci,automated \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)" EOF only: - main when: manual ``` --- ## Error Handling ### Deployment Verification Script ```bash #!/bin/bash # deploy-with-verification.sh APP="debian" HOSTNAME="production-server" MAX_RETRIES=3 RETRY_COUNT=0 deploy_container() { echo "Attempting deployment (Try $((RETRY_COUNT + 1))/$MAX_RETRIES)..." var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname="$HOSTNAME" \ var_net=dhcp \ var_ssh=yes \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${APP}.sh)" 2>&1 | tee deploy.log return ${PIPESTATUS[0]} } verify_deployment() { echo "Verifying deployment..." # Check if container exists if ! pct list | grep -q "$HOSTNAME"; then echo "✗ Container not found in pct list" return 1 fi # Check if container is running CTID=$(pct list | grep "$HOSTNAME" | awk '{print $1}') STATUS=$(pct status "$CTID" | awk '{print $2}') if [ "$STATUS" != "running" ]; then echo "✗ Container not running (Status: $STATUS)" return 1 fi # Check network connectivity if ! pct exec "$CTID" -- ping -c 1 1.1.1.1 &>/dev/null; then echo "⚠ Warning: No internet connectivity" fi echo "✓ Deployment verified successfully" echo " Container ID: $CTID" echo " Status: $STATUS" echo " IP: $(pct exec "$CTID" -- hostname -I)" return 0 } # Main deployment loop with retry while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do if deploy_container; then if verify_deployment; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✓ Deployment successful!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 0 else echo "✗ Deployment verification failed" fi else echo "✗ Deployment failed" fi RETRY_COUNT=$((RETRY_COUNT + 1)) if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then echo "Retrying in 10 seconds..." sleep 10 fi done echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✗ Deployment failed after $MAX_RETRIES attempts" echo "Check deploy.log for details" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 1 ``` ### Rollback on Failure ```bash #!/bin/bash # deploy-with-rollback.sh APP="debian" HOSTNAME="test-server" SNAPSHOT_NAME="pre-deployment" # Take snapshot of existing container (if exists) backup_existing() { EXISTING_CTID=$(pct list | grep "$HOSTNAME" | awk '{print $1}') if [ -n "$EXISTING_CTID" ]; then echo "Creating snapshot of existing container..." pct snapshot "$EXISTING_CTID" "$SNAPSHOT_NAME" --description "Pre-deployment backup" return 0 fi return 1 } # Deploy new container deploy() { var_hostname="$HOSTNAME" \ var_cpu=4 \ var_ram=4096 \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${APP}.sh)" return $? } # Rollback to snapshot rollback() { local ctid="$1" echo "Rolling back to snapshot..." pct rollback "$ctid" "$SNAPSHOT_NAME" pct delsnapshot "$ctid" "$SNAPSHOT_NAME" } # Main execution backup_existing HAD_BACKUP=$? if deploy; then echo "✓ Deployment successful" [ $HAD_BACKUP -eq 0 ] && echo "You can remove the snapshot with: pct delsnapshot $SNAPSHOT_NAME" else echo "✗ Deployment failed" if [ $HAD_BACKUP -eq 0 ]; then read -p "Rollback to previous version? (y/N) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then rollback "$EXISTING_CTID" echo "✓ Rolled back successfully" fi fi exit 1 fi ``` --- ## Security Considerations ### Secure Deployment Script ```bash #!/bin/bash # secure-deploy.sh - Production-ready secure deployment set -euo pipefail # Exit on error, undefined vars, pipe failures # Configuration readonly APP="debian" readonly HOSTNAME="secure-server" readonly SSH_KEY_PATH="/root/.ssh/id_rsa.pub" readonly LOG_FILE="/var/log/container-deployments.log" # Logging function log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } # Validate prerequisites validate_environment() { log "Validating environment..." # Check if running as root if [ "$EUID" -ne 0 ]; then log "ERROR: Must run as root" exit 1 fi # Check SSH key exists if [ ! -f "$SSH_KEY_PATH" ]; then log "ERROR: SSH key not found at $SSH_KEY_PATH" exit 1 fi # Check internet connectivity if ! curl -s --max-time 5 https://github.com &>/dev/null; then log "ERROR: No internet connectivity" exit 1 fi log "✓ Environment validated" } # Secure deployment deploy_secure() { log "Starting secure deployment for $HOSTNAME..." SSH_KEY=$(cat "$SSH_KEY_PATH") var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname="$HOSTNAME" \ var_brg=vmbr0 \ var_net=dhcp \ var_ipv6_method=disable \ var_ssh=yes \ var_ssh_authorized_key="$SSH_KEY" \ var_nesting=0 \ var_keyctl=0 \ var_fuse=0 \ var_protection=yes \ var_tags=production,secure,automated \ var_verbose=no \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${APP}.sh)" 2>&1 | tee -a "$LOG_FILE" if [ ${PIPESTATUS[0]} -eq 0 ]; then log "✓ Deployment successful" return 0 else log "✗ Deployment failed" return 1 fi } # Main execution main() { validate_environment if deploy_secure; then log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "Secure deployment completed successfully" log "Container: $HOSTNAME" log "Features: Unprivileged, SSH-only, Protected" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 0 else log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "Deployment failed - check logs at $LOG_FILE" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 1 fi } main "$@" ``` ### SSH Key Management ```bash #!/bin/bash # deploy-with-ssh-keys.sh - Secure SSH key deployment # Load SSH keys from multiple sources load_ssh_keys() { local keys=() # Personal key if [ -f ~/.ssh/id_rsa.pub ]; then keys+=("$(cat ~/.ssh/id_rsa.pub)") fi # Team keys if [ -f /etc/ssh/authorized_keys.d/team ]; then while IFS= read -r key; do [ -n "$key" ] && keys+=("$key") done < /etc/ssh/authorized_keys.d/team fi # Join keys with newline printf "%s\n" "${keys[@]}" } # Deploy with multiple SSH keys SSH_KEYS=$(load_ssh_keys) var_ssh=yes \ var_ssh_authorized_key="$SSH_KEYS" \ var_hostname=multi-key-server \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)" ``` --- ## Complete Production Example ```bash #!/bin/bash # production-deploy.sh - Complete production deployment system set -euo pipefail #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Configuration #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly LOG_DIR="/var/log/proxmox-deployments" readonly CONFIG_FILE="$SCRIPT_DIR/deployment-config.json" #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Functions #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ setup_logging() { mkdir -p "$LOG_DIR" exec 1> >(tee -a "$LOG_DIR/deployment-$(date +%Y%m%d-%H%M%S).log") exec 2>&1 } log_info() { echo "[INFO] $(date +'%H:%M:%S') - $*"; } log_error() { echo "[ERROR] $(date +'%H:%M:%S') - $*" >&2; } log_success() { echo "[SUCCESS] $(date +'%H:%M:%S') - $*"; } validate_prerequisites() { log_info "Validating prerequisites..." [ "$EUID" -eq 0 ] || { log_error "Must run as root"; exit 1; } command -v jq >/dev/null 2>&1 || { log_error "jq not installed"; exit 1; } command -v curl >/dev/null 2>&1 || { log_error "curl not installed"; exit 1; } log_success "Prerequisites validated" } deploy_from_config() { local config_file="$1" if [ ! -f "$config_file" ]; then log_error "Config file not found: $config_file" return 1 fi local container_count container_count=$(jq '.containers | length' "$config_file") log_info "Deploying $container_count containers from config..." for i in $(seq 0 $((container_count - 1))); do local name cpu ram disk app tags name=$(jq -r ".containers[$i].name" "$config_file") cpu=$(jq -r ".containers[$i].cpu" "$config_file") ram=$(jq -r ".containers[$i].ram" "$config_file") disk=$(jq -r ".containers[$i].disk" "$config_file") app=$(jq -r ".containers[$i].app" "$config_file") tags=$(jq -r ".containers[$i].tags" "$config_file") log_info "Deploying container: $name ($app)" var_unprivileged=1 \ var_cpu="$cpu" \ var_ram="$ram" \ var_disk="$disk" \ var_hostname="$name" \ var_net=dhcp \ var_ssh=yes \ var_tags="$tags,automated" \ var_protection=yes \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)" if [ $? -eq 0 ]; then log_success "Deployed: $name" else log_error "Failed to deploy: $name" fi sleep 5 done } generate_report() { log_info "Generating deployment report..." echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "DEPLOYMENT REPORT" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Time: $(date)" echo "" pct list echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" } #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Main #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ main() { setup_logging log_info "Starting production deployment system" validate_prerequisites deploy_from_config "$CONFIG_FILE" generate_report log_success "Production deployment complete" } main "$@" ``` **Example config file (deployment-config.json):** ```json { "containers": [ { "name": "pihole", "app": "pihole", "cpu": 2, "ram": 1024, "disk": 8, "tags": "dns,network,production" }, { "name": "homeassistant", "app": "homeassistant", "cpu": 4, "ram": 4096, "disk": 20, "tags": "automation,ha,production" }, { "name": "docker-host", "app": "docker", "cpu": 8, "ram": 16384, "disk": 100, "tags": "containers,docker,production" } ] } ``` --- ## See Also - [Defaults System Guide](DEFAULTS_GUIDE.md) - [Configuration Reference](CONFIGURATION_REFERENCE.md) - [Security Best Practices](SECURITY_GUIDE.md) - [Network Configuration](NETWORK_GUIDE.md)