Removed outdated documentation files from docs/. Added new detailed guides to ct/ and install/ directories. Updated README.md to reflect new navigation, learning paths, and documentation structure. Relocated system guides and technical references to settings/ and install/ subdirectories for improved organization.
13 KiB
🛠️ 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
- Execution Context
- File Structure
- Complete Script Template
- Installation Phases
- Function Reference
- Best Practices
- Real Examples
- Troubleshooting
- Contribution Checklist
Overview
Purpose
Installation scripts (install/AppName-install.sh) run inside the LXC container and are responsible for:
- Setting up the container OS (updates, packages)
- Installing application dependencies
- Downloading and configuring the application
- Setting up services and systemd units
- Creating version tracking files for updates
- Generating credentials/configurations
- 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
# 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
#!/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
#!/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
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)
# 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
# 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
# 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)
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 <<EOF
CREATE DATABASE ${DB_NAME};
CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF
msg_ok "Database setup complete"
Phase 7: Permission & Ownership
msg_info "Setting permissions"
# Web applications typically run as www-data
chown -R www-data:www-data /opt/appname
chmod -R 755 /opt/appname
chmod -R 644 /opt/appname/*
chmod 755 /opt/appname/*/.*
msg_ok "Permissions set"
Phase 8: Service Configuration
# Enable systemd service
systemctl enable -q --now appname
# Or for OpenRC (Alpine)
rc-service appname start
rc-update add appname default
# Verify service is running
if systemctl is-active --quiet appname; then
msg_ok "Service running successfully"
else
msg_error "Service failed to start"
journalctl -u appname -n 20
exit 1
fi
Phase 9: Version Tracking
# Essential for update detection
echo "${RELEASE}" > /opt/${APP}_version.txt
# Or with additional metadata
cat > /opt/${APP}_version.txt <<EOF
Version: ${RELEASE}
InstallDate: $(date)
InstallMethod: ${METHOD}
EOF
Phase 10: Final Setup & Cleanup
# Display MOTD and enable autologin
motd_ssh
# Final customization
customize
# Clean up package manager cache
msg_info "Cleaning up"
apt-get -y autoremove
apt-get -y autoclean
msg_ok "Cleaned"
# Or for Alpine
apk cache clean
rm -rf /var/cache/apk/*
# System cleanup
cleanup_lxc
Installation Phases
Phase 1: Container OS Setup
- Network interface brought up and configured
- Internet connectivity verified
- Package lists updated
- All OS packages upgraded to latest versions
Phase 2: Base Dependencies
msg_info "Installing Base Dependencies"
$STD apt-get install -y \
curl wget git nano build-essential
msg_ok "Installed Base Dependencies"
Phase 3: Tool Installation
NODE_VERSION="22" setup_nodejs
PHP_VERSION="8.4" setup_php
Phase 4: Application Setup
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"
Phase 5: Configuration
Application-specific configuration files and environment setup
Phase 6: Service Registration
Enable and verify systemd services are running
Function Reference
Core Messaging Functions
msg_info(message)
Displays an info message with spinner animation
msg_info "Installing application"
# Output: ⏳ Installing application (with spinning animation)
msg_ok(message)
Displays success message with checkmark
msg_ok "Installation completed"
# Output: ✔️ Installation completed
msg_error(message)
Displays error message and exits
msg_error "Installation failed"
# Output: ✖️ Installation failed
Package Management
$STD Variable
Controls output verbosity
# Silent mode (respects VERBOSE setting)
$STD apt-get install -y nginx
update_os()
Updates OS packages
update_os
# Runs: apt update && apt upgrade
Tool Installation Functions
setup_nodejs()
Installs Node.js with optional global modules
NODE_VERSION="22" setup_nodejs
NODE_VERSION="22" NODE_MODULE="yarn,@vue/cli" setup_nodejs
setup_php()
Installs PHP with optional extensions
PHP_VERSION="8.4" PHP_MODULE="bcmath,curl,gd,intl,redis" setup_php
Other Tools
setup_mariadb # MariaDB database
setup_mysql # MySQL database
setup_postgresql # PostgreSQL
setup_docker # Docker Engine
setup_composer # PHP Composer
setup_python # Python 3
setup_ruby # Ruby
setup_rust # Rust
Cleanup Functions
cleanup_lxc()
Comprehensive container cleanup
- Removes package manager caches
- Cleans temporary files
- Clears language package caches
- Removes systemd journal logs
cleanup_lxc
# Output: ⏳ Cleaning up
# ✔️ Cleaned
Best Practices
✅ DO:
- Always Use $STD for Commands
# ✅ Good: Respects VERBOSE setting
$STD apt-get install -y nginx
- Generate Random Passwords Safely
# ✅ Good: Alphanumeric only
DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
- Check Command Success
# ✅ Good: Verify success
if ! wget -q "https://example.com/file.tar.gz"; then
msg_error "Download failed"
exit 1
fi
- Set Proper Permissions
# ✅ Good: Explicit permissions
chown -R www-data:www-data /opt/appname
chmod -R 755 /opt/appname
- Save Version for Update Checks
# ✅ Good: Version tracked
echo "${RELEASE}" > /opt/${APP}_version.txt
- Handle Alpine vs Debian Differences
# ✅ Good: Detect OS
if grep -qi 'alpine' /etc/os-release; then
apk add package
else
apt-get install -y package
fi
❌ DON'T:
- Hardcode Versions
# ❌ Bad: Won't auto-update
wget https://example.com/app-1.2.3.tar.gz
- Use Root Without Password
# ❌ Bad: Security risk
mysql -u root
- Forget Error Handling
# ❌ Bad: Silent failures
wget https://example.com/file
tar -xzf file
- Leave Temporary Files
# ✅ Always cleanup
rm -rf /opt/app-${RELEASE}.tar.gz
Real Examples
Example 1: Node.js Application
#!/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
#!/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 <<EOF
CREATE DATABASE appdb;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EOF
# App installation
cd /opt
wget -q https://github.com/user/repo/releases/latest/download/app.tar.gz
tar -xzf app.tar.gz
# Configuration
cat > /opt/app/.env <<EOF
DB_HOST=localhost
DB_NAME=appdb
DB_USER=appuser
DB_PASS=${DB_PASS}
EOF
chown -R www-data:www-data /opt/app
systemctl enable --now php-fpm
cleanup_lxc
Troubleshooting
Installation Hangs
Check internet connectivity:
ping -c 1 8.8.8.8
Enable verbose mode:
# In ct/AppName.sh, before running
VERBOSE="yes" bash install/AppName-install.sh
Package Not Found
Update package lists:
apt update
apt-cache search package_name
Service Won't Start
Check logs:
journalctl -u appname -n 50
systemctl status appname
Contribution Checklist
Before submitting a PR:
Structure
- Shebang is
#!/usr/bin/env bash - Loads functions from
$FUNCTIONS_FILE_PATH - Copyright header with author
- Clear phase comments
Installation
setting_up_containercalled earlynetwork_checkbefore downloadsupdate_osbefore package installation- All errors checked properly
Functions
- Uses
msg_info/msg_ok/msg_errorfor status - Uses
$STDfor command output silencing - Version saved to
/opt/${APP}_version.txt - Proper permissions set
Cleanup
motd_sshcalled for final setupcustomizecalled for optionscleanup_lxccalled at end
Testing
- Tested with default settings
- Tested with advanced (19-step) mode
- Service starts and runs correctly
Last Updated: December 2025 Compatibility: ProxmoxVED with install.func v3+