Skip to main content
Container scripts (ct/AppName.sh) are entry points for creating LXC containers with specific applications pre-installed on Proxmox VE. These scripts define container defaults, orchestrate the build process, and provide update mechanisms.

Overview

Execution Flow

Proxmox Host

ct/AppName.sh sourced (runs as root on host)

build.func: Creates LXC container + runs install script inside

install/AppName-install.sh (runs inside container)

Container ready with app installed

Key Integration Points

  • build.func - Main orchestrator (container creation, storage, variable management)
  • install.func - Container-specific setup (OS update, package management)
  • tools.func - Tool installation helpers (repositories, GitHub releases)
  • core.func - UI/messaging functions (colors, spinners, validation)
  • error_handler.func - Error handling and signal management

Container Creation Workflow

1

Set APP and defaults

Define application metadata and default container resources (CPU, RAM, disk, OS).
2

Display header

Show ASCII art header using header_info() function.
3

Parse arguments

Process command-line arguments and load build.func using variables() function.
4

Setup colors

Initialize ANSI color codes with color() function.
5

Initialize error handling

Setup trap handlers using catch_errors() function.
6

Show installation menu

Display 5 installation modes:
  • Default (Quick setup)
  • Advanced (19-step wizard)
  • User Defaults
  • App Defaults
  • Settings Menu
7

Build container

Create LXC container and execute install script inside.
8

Set description

Add container description visible in Proxmox UI.
9

Display success message

Show access URL and completion status.

Complete Script Template

Basic Structure

#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: YourUsername
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/example/project

# Import main orchestrator
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/build.func)

# Application Configuration
APP="ApplicationName"
var_tags="tag1;tag2;tag3"      # Max 3-4 tags, semicolon-separated

# Container Resources
var_cpu="2"                    # CPU cores
var_ram="2048"                 # RAM in MB
var_disk="10"                  # Disk in GB

# Container Type & OS
var_os="debian"                # Options: alpine, debian, ubuntu
var_version="12"               # Alpine: 3.20+, Debian: 11-13, Ubuntu: 20.04+
var_unprivileged="1"           # 1=unprivileged (secure), 0=privileged

# Display header and initialize
header_info "$APP"
variables
color
catch_errors

# Update function (runs when script is executed on existing container)
function update_script() {
  header_info
  check_container_storage
  check_container_resources
  
  # Verify app is installed
  if [[ ! -d /opt/appname ]]; then
    msg_error "No ${APP} Installation Found!"
    exit
  fi
  
  # Get latest version from GitHub
  RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \
    grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
  
  # Compare with saved version
  if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
    msg_info "Updating ${APP} to v${RELEASE}"
    
    # Update logic here
    cd /opt
    # Download, install, configure...
    
    # Save new version
    echo "${RELEASE}" > /opt/${APP}_version.txt
    
    msg_ok "Updated ${APP} to v${RELEASE}"
  else
    msg_ok "No update required. ${APP} is already at v${RELEASE}."
  fi
  
  exit
}

# Launch container creation
start
build_container
description

# Display success message
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}"

Real-World Examples

ct/pihole.sh
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://pi-hole.net/

APP="Pihole"
var_tags="${var_tags:-adblock}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-2}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"

header_info "$APP"
variables
color
catch_errors

function update_script() {
    header_info
    check_container_storage
    check_container_resources
    if [[ ! -d /etc/pihole ]]; then
        msg_error "No ${APP} Installation Found!"
        exit
    fi
    msg_info "Updating PiHole"
    set +e
    $STD apt update
    $STD apt upgrade -y
    /usr/local/bin/pihole -up
    msg_ok "Updated PiHole"
    msg_ok "Updated successfully!"
    exit
}

start
build_container
description

msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/admin${CL}"

Variable Naming Convention

Variables exposed to users must use the var_* prefix (e.g., var_cpu, var_hostname, var_ssh). Internal variables should use lowercase names (e.g., container_id, app_version).

Available Variables

VariableDescriptionExample
var_cpuCPU cores"2"
var_ramRAM in MB"2048"
var_diskDisk size in GB"10"
var_osOperating system"debian", "alpine", "ubuntu"
var_versionOS version"12", "3.20", "22.04"
var_unprivilegedContainer type"1" (unprivileged), "0" (privileged)
var_tagsApplication tags"web;dashboard;monitoring"

Default Values Precedence

Priority 1 (Highest): Environment Variables (var_cpu, var_ram, etc.)
Priority 2: App-Specific Defaults (/defaults/AppName.vars)
Priority 3: User Global Defaults (/default.vars)
Priority 4 (Lowest): Built-in Defaults (in build.func)

Core Functions

header_info()

Displays ASCII art header for the application.
header_info "$APP"

variables()

Initializes container variables, loads user arguments, and sets up orchestration.
variables

start()

Launches the container creation menu with 5 installation modes:
  1. Default Installation - Quick setup with predefined settings
  2. Advanced Installation - 19-step wizard with full control
  3. User Defaults - Load ~/.community-scripts/default.vars
  4. App Defaults - Load /defaults/AppName.vars
  5. Settings Menu - Interactive mode selection
start

build_container()

Main orchestrator for LXC container creation:
  1. Validates all variables
  2. Creates LXC container via pct create
  3. Executes install/AppName-install.sh inside container
  4. Monitors installation progress
  5. Handles errors and rollback on failure
build_container

description()

Sets container description/notes visible in Proxmox UI.
description

Update Function Best Practices

Always implement an update_script() function to enable easy updates for existing containers.
function update_script() {
  header_info
  
  # Always start with these checks
  check_container_storage
  check_container_resources
  
  # Verify app is installed
  if [[ ! -d /opt/appname ]]; then
    msg_error "No ${APP} Installation Found!"
    exit
  fi
  
  # Get latest version from GitHub
  RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \
    grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
  
  # Compare with saved version
  if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
    msg_info "Updating ${APP} to v${RELEASE}"
    
    # Backup user data (optional but recommended)
    cp -r /opt/appname /opt/appname-backup
    
    # Perform update
    cd /opt
    wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz"
    tar -xzf app-${RELEASE}.tar.gz
    
    # Restore user data if needed
    cp /opt/appname-backup/config/* /opt/appname/config/
    
    # Cleanup
    rm -rf app-${RELEASE}.tar.gz /opt/appname-backup
    
    # Save new version
    echo "${RELEASE}" > /opt/${APP}_version.txt
    
    msg_ok "Updated ${APP} to v${RELEASE}"
  else
    msg_ok "No update required. ${APP} is already at v${RELEASE}."
  fi
  
  exit
}

Troubleshooting

Container Creation Fails

Symptom: pct create exits with error code 209 Solution:
# Check existing containers
pct list | grep CTID

# Remove conflicting container
pct destroy CTID

# Retry ct/AppName.sh

Update Function Doesn’t Detect New Version

Debug:
# Check version file
cat /opt/AppName_version.txt

# Test GitHub API
curl -fsSL https://api.github.com/repos/user/repo/releases/latest | grep tag_name

Best Practices

DO:

  • Use meaningful defaults appropriate for your application
  • Implement version tracking with /opt/${APP}_version.txt
  • Handle edge cases in the update function
  • Use proper messaging with msg_info/msg_ok/msg_error
  • Test with both default and advanced installation modes
  • Use ${var_cpu:-2} syntax to allow environment variable overrides

DON’T:

  • Hardcode versions - always fetch latest from GitHub API
  • Use custom color codes - use built-in variables from color() function
  • Forget error handling in critical sections
  • Leave temporary files or backups
  • Use privileged containers unless absolutely necessary

Contribution Checklist

Before submitting a PR:
  • Shebang is #!/usr/bin/env bash
  • Imports build.func from community-scripts repo
  • Copyright header with author and source URL
  • APP variable matches filename (e.g., APP="Docker" for docker.sh)
  • var_tags are semicolon-separated with no spaces
  • var_cpu set appropriately (2-4 for most apps)
  • var_ram set appropriately (1024-4096 MB minimum)
  • var_disk sufficient for app + data (5-20 GB)
  • var_os is realistic for the application
  • update_script() function implemented
  • Update function checks if app is installed
  • Proper error handling with msg_error
  • Script tested with default installation
  • Script tested with advanced (19-step) installation
  • Update function tested on existing installation

Build docs developers (and LLMs) love