# 🛠️ **Application Installation Scripts (install/AppName-install.sh)** **Modern Guide to Writing In-Container Installation Scripts** > **Updated**: December 2025 > **Context**: Integrated with tools.func, error_handler.func, and install.func > **Examples Used**: `/install/pihole-install.sh`, `/install/mealie-install.sh` --- ## 📋 Table of Contents - [Overview](#overview) - [Execution Context](#execution-context) - [File Structure](#file-structure) - [Complete Script Template](#complete-script-template) - [Installation Phases](#installation-phases) - [Function Reference](#function-reference) - [Best Practices](#best-practices) - [Real Examples](#real-examples) - [Troubleshooting](#troubleshooting) - [Contribution Checklist](#contribution-checklist) --- ## Overview ### Purpose Installation scripts (`install/AppName-install.sh`) **run inside the LXC container** and are responsible for: 1. Setting up the container OS (updates, packages) 2. Installing application dependencies 3. Downloading and configuring the application 4. Setting up services and systemd units 5. Creating version tracking files for updates 6. Generating credentials/configurations 7. Final cleanup and validation ### Execution Flow ``` ct/AppName.sh (Proxmox Host) ↓ build_container() ↓ pct exec CTID bash -c "$(cat install/AppName-install.sh)" ↓ install/AppName-install.sh (Inside Container) ↓ Container Ready with App Installed ``` --- ## Execution Context ### Environment Variables Available ```bash # From Proxmox/Container CTID # Container ID (100, 101, etc.) PCT_OSTYPE # OS type (alpine, debian, ubuntu) HOSTNAME # Container hostname # From build.func FUNCTIONS_FILE_PATH # Bash functions library (core.func + tools.func) VERBOSE # Verbose mode (yes/no) STD # Standard redirection variable (silent/empty) # From install.func APP # Application name NSAPP # Normalized app name (lowercase, no spaces) METHOD # Installation method (ct/install) RANDOM_UUID # Session UUID for telemetry ``` --- ## File Structure ### Minimal install/AppName-install.sh Template ```bash #!/usr/bin/env bash # [1] Shebang # [2] Copyright/Metadata # Copyright (c) 2021-2025 community-scripts ORG # Author: YourUsername # License: MIT # Source: https://example.com # [3] Load functions source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os # [4] Installation steps msg_info "Installing Dependencies" $STD apt-get install -y package1 package2 msg_ok "Installed Dependencies" # [5] Final setup motd_ssh customize cleanup_lxc ``` --- ## Complete Script Template ### Phase 1: Header & Initialization ```bash #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/application/repo # Load all available functions (from core.func + tools.func) source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Initialize environment color # Setup ANSI colors and icons verb_ip6 # Configure IPv6 (if needed) catch_errors # Setup error traps setting_up_container # Verify OS is ready network_check # Verify internet connectivity update_os # Update packages (apk/apt) ``` ### Phase 2: Dependency Installation ```bash msg_info "Installing Dependencies" $STD apt-get install -y \ curl \ wget \ git \ nano \ build-essential \ libssl-dev \ python3-dev msg_ok "Installed Dependencies" ``` ### Phase 3: Tool Setup (Using tools.func) ```bash # Setup specific tool versions NODE_VERSION="22" setup_nodejs PHP_VERSION="8.4" setup_php PYTHON_VERSION="3.12" setup_uv ``` ### Phase 4: Application Download & Setup ```bash # Download 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)}') wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" cd /opt tar -xzf app-${RELEASE}.tar.gz rm -f app-${RELEASE}.tar.gz ``` ### Phase 5: Configuration Files ```bash # Using cat << EOF (multiline) cat <<'EOF' >/etc/nginx/sites-available/appname server { listen 80; server_name _; root /opt/appname/public; index index.php index.html; } EOF # Using sed for replacements sed -i -e "s|^DB_HOST=.*|DB_HOST=localhost|" \ -e "s|^DB_USER=.*|DB_USER=appuser|" \ /opt/appname/.env ``` ### Phase 6: Database Setup (If Needed) ```bash msg_info "Setting up Database" DB_NAME="appname_db" DB_USER="appuser" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) # For MySQL/MariaDB mysql -u root < /opt/${APP}_version.txt # Or with additional metadata cat > /opt/${APP}_version.txt < /opt/${APP}_version.txt ``` 6. **Handle Alpine vs Debian Differences** ```bash # ✅ Good: Detect OS if grep -qi 'alpine' /etc/os-release; then apk add package else apt-get install -y package fi ``` ### ❌ DON'T: 1. **Hardcode Versions** ```bash # ❌ Bad: Won't auto-update wget https://example.com/app-1.2.3.tar.gz ``` 2. **Use Root Without Password** ```bash # ❌ Bad: Security risk mysql -u root ``` 3. **Forget Error Handling** ```bash # ❌ Bad: Silent failures wget https://example.com/file tar -xzf file ``` 4. **Leave Temporary Files** ```bash # ✅ Always cleanup rm -rf /opt/app-${RELEASE}.tar.gz ``` --- ## Real Examples ### Example 1: Node.js Application ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color catch_errors setting_up_container network_check update_os msg_info "Installing Node.js" NODE_VERSION="22" setup_nodejs msg_ok "Node.js installed" msg_info "Installing Application" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app.tar.gz" tar -xzf app.tar.gz echo "${RELEASE}" > /opt/app_version.txt msg_ok "Application installed" systemctl enable --now app cleanup_lxc ``` ### Example 2: PHP Application with Database ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_MODULE="bcmath,curl,pdo_mysql" setup_php MARIADB_VERSION="11.4" setup_mariadb # Database setup DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mysql -u root < /opt/app/.env <