Quick Start
The simplest unattended deployment:var_hostname=myserver bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)"
Prerequisites
Proxmox VE Access
Verify root access and Proxmox version:
whoami # Should return: root
pveversion # Should be 8.0+ or 9.0-9.1
Network Connectivity
Test GitHub and internet access:
curl -I https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh
ping -c 1 1.1.1.1
Deployment Methods
Environment Variables
Complexity: Low
Flexibility: High
Best for: Quick one-off deployments
Flexibility: High
Best for: Quick one-off deployments
App Defaults
Complexity: Low
Flexibility: Medium
Best for: Repeating identical deployments
Flexibility: Medium
Best for: Repeating identical deployments
Shell Scripts
Complexity: Medium
Flexibility: High
Best for: Batch operations and automation
Flexibility: High
Best for: Batch operations and automation
Ansible/Terraform
Complexity: High
Flexibility: Very High
Best for: Enterprise Infrastructure as Code
Flexibility: Very High
Best for: Enterprise Infrastructure as Code
Single Container Deployment
Basic Example
#!/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_os=debian \
var_version=13 \
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/ProxmoxVE/main/ct/debian.sh)"
echo "✓ Container deployed successfully"
Automatic IP Assignment
Use IP range scanning to automatically assign the first available IP:#!/bin/bash
# deploy-with-ip-scan.sh - Auto-assign first free IP from range
var_unprivileged=1 \
var_cpu=4 \
var_ram=4096 \
var_hostname=web-server \
var_net=192.168.1.100/24-192.168.1.150/24 \
var_gateway=192.168.1.1 \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)"
# The script will:
# 1. Ping 192.168.1.100 - if responds, skip
# 2. Ping 192.168.1.101 - if responds, skip
# 3. Continue until first IP that doesn't respond
# 4. Assign that IP to the container
IP range format is
START_IP/CIDR-END_IP/CIDR. Both sides must include the same CIDR notation.Using Saved Defaults
- Step 1: Create Defaults
- Step 2: Deploy Unattended
Run once interactively to save your configuration:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)"
# Select "Advanced Settings" → Configure → Save as "App Defaults"
Future deployments use saved defaults automatically:
#!/bin/bash
# deploy-with-defaults.sh
# App defaults are loaded automatically from:
# /usr/local/community-scripts/defaults/pihole.vars
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)"
Batch Deployments
Simple Loop
Deploy multiple containers sequentially:#!/bin/bash
# batch-deploy-simple.sh
apps=("debian" "ubuntu" "alpine")
for app in "${apps[@]}"; do
echo "Deploying $app..."
var_hostname="$app-server" \
var_cpu=2 \
var_ram=2048 \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)"
echo "✓ $app deployed"
sleep 5 # Wait between deployments
done
Advanced Configuration Array
Deploy multiple containers with individual configurations:#!/bin/bash
# batch-deploy-advanced.sh
declare -A CONTAINERS=(
["pihole"]="2:1024:8:vmbr0:dns,network"
["homeassistant"]="4:4096:20:vmbr1:automation,ha"
["docker"]="6:8192:50:vmbr0:containers,docker"
)
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/ProxmoxVE/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
Deploy multiple containers simultaneously:#!/bin/bash
# parallel-deploy.sh
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/ProxmoxVE/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"
echo "All deployments complete!"
Infrastructure as Code
Ansible Playbook
- Basic Playbook
- Advanced Playbook
---
# playbook-proxmox.yml
- name: Deploy ProxmoxVE 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/ProxmoxVE/main/ct/debian.sh)"
args:
executable: /bin/bash
register: deploy_result
- name: Display deployment result
debug:
var: deploy_result.stdout_lines
---
# 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/ProxmoxVE/main/ct/{{ item.name }}.sh)"
args:
executable: /bin/bash
loop: "{{ containers }}"
register: deployment_results
- name: Report deployment status
debug:
msg: "Deployed {{ item.item.name }} - Status: {{ 'Success' if item.rc == 0 else 'Failed' }}"
loop: "{{ deployment_results.results }}"
ansible-playbook -i inventory.ini advanced-playbook.yml
Terraform
# main.tf
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/ProxmoxVE/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
# .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
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to Proxmox
uses: appleboy/[email protected]
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/ProxmoxVE/main/ct/${{ github.event.inputs.container_type }}.sh)"
- name: Notify deployment status
if: success()
run: echo "✓ Container deployed successfully"
GitLab CI
# .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/ProxmoxVE/main/ct/debian.sh)"
EOF
only:
- main
when: manual
Error Handling
Deployment with Retry Logic
#!/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/ProxmoxVE/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
Security Best Practices
Secure Deployment Script
#!/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/ProxmoxVE/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 "$@"
Security Reminders:
- Never store passwords in defaults files - use SSH keys
- Always use unprivileged containers when possible
- Enable container protection for production deployments
- Use static IPs for critical infrastructure
- Disable unnecessary features (FUSE, TUN, etc.)
Common Deployment Patterns
Docker Host
var_unprivileged=0 \
var_nesting=1 \
var_keyctl=1 \
var_cpu=4 \
var_ram=8192 \
var_disk=50 \
var_hostname=docker-host \
bash docker-install.sh
VPN Gateway
var_tun=yes \
var_cpu=2 \
var_ram=1024 \
var_disk=10 \
var_hostname=wireguard \
var_tags="vpn;network" \
bash wireguard-install.sh
Media Server with GPU
var_gpu=yes \
var_unprivileged=1 \
var_cpu=6 \
var_ram=8192 \
var_disk=100 \
var_hostname=plex \
var_tags="media;transcoding" \
bash plex-install.sh
Related Resources
Environment Variables
Complete variable reference
Defaults System
Save reusable configurations
Troubleshooting
Common deployment issues