#!/usr/bin/env bash set -euo pipefail ### ---------- helpers ---------- die() { echo "❌ $*" >&2; exit 1; } info() { echo "▶ $*"; } ok() { echo "✅ $*"; } choose_from_list() { local prompt="$1" shift local items=("$@") echo echo "$prompt" echo local i=1 for item in "${items[@]}"; do printf " %2d) %s\n" "$i" "$item" ((i++)) done while true; do echo read -rp "Enter number or exact value: " choice # numeric if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#items[@]} )); then echo "${items[choice-1]}" return fi # exact match for item in "${items[@]}"; do if [[ "$choice" == "$item" ]]; then echo "$item" return fi done echo "❌ Invalid selection. Try again." done } ### ---------- banner ---------- cat </dev/null && die "Container ID $CT_ID already exists" read -rp "Hostname: " HOSTNAME read -rp "Rootfs size (GB) [16]: " ROOTFS_SIZE read -rp "Memory (MB) [4096]: " MEMORY read -rp "CPU cores [2]: " CORES read -rsp "Root password: " PASSWORD echo ROOTFS_SIZE=${ROOTFS_SIZE:-16} MEMORY=${MEMORY:-4096} CORES=${CORES:-2} ### ---------- templates ---------- info "Detecting LXC templates..." mapfile -t TEMPLATES < <(pveam list local | awk '/vztmpl/ {print $1}') [[ ${#TEMPLATES[@]} -gt 0 ]] || die "No templates found in local storage" # Prefer Ubuntu if present DEFAULT_TEMPLATE="" for t in "${TEMPLATES[@]}"; do [[ "$t" =~ ubuntu ]] && DEFAULT_TEMPLATE="$t" && break done if [[ -n "$DEFAULT_TEMPLATE" ]]; then echo read -rp "Ubuntu template found. Use it? [Y/n]: " yn if [[ ! "$yn" =~ ^[Nn]$ ]]; then TEMPLATE="$DEFAULT_TEMPLATE" fi fi [[ -z "${TEMPLATE:-}" ]] && TEMPLATE=$(choose_from_list "Select LXC template:" "${TEMPLATES[@]}") ### ---------- bridges ---------- info "Detecting network bridges..." mapfile -t BRIDGES < <(awk '/iface vmbr/ {print $2}' /etc/network/interfaces) [[ ${#BRIDGES[@]} -gt 0 ]] || die "No vmbr bridges found" DEFAULT_BRIDGE="${BRIDGES[0]}" read -rp "Use default bridge (${DEFAULT_BRIDGE})? [Y/n]: " yn if [[ "$yn" =~ ^[Nn]$ ]]; then BRIDGE=$(choose_from_list "Select network bridge:" "${BRIDGES[@]}") else BRIDGE="$DEFAULT_BRIDGE" fi ### ---------- summary ---------- cat <> /etc/pve/lxc/${CT_ID}.conf lxc.apparmor.profile: unconfined lxc.cgroup.devices.allow: a lxc.cap.drop: lxc.mount.auto: "proc:rw sys:rw" EOF pct start "$CT_ID" sleep 5 info "Configuring container..." pct exec "$CT_ID" -- bash -c "echo 'root:$PASSWORD' | chpasswd" pct exec "$CT_ID" -- bash -c " apt update && apt install -y curl ca-certificates " pct exec "$CT_ID" -- bash -c " cat <<'EOF' > /etc/rc.local #!/bin/sh -e if [ ! -e /dev/kmsg ]; then ln -s /dev/console /dev/kmsg fi mount --make-rshared / EOF chmod +x /etc/rc.local /etc/rc.local " ok "Container $CT_ID is ready for K3s installation." echo "Next step inside container:" echo " curl -sfL https://get.k3s.io | sh"