#!/usr/bin/env bash # ============================================================================= # Freqtrade LXC Installer für Proxmox # Erstellt automatisch einen Docker-LXC und installiert: # Docker, Portainer, Filebrowser, Freqtrade # # Läuft auf dem PROXMOX HOST. # Quelle: https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy # ============================================================================= set -euo pipefail RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' info() { echo -e "${CYAN}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[ OK ]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; } step() { echo -e "\n${BOLD}${BLUE}▶ $*${NC}"; } ask() { local prompt="$1" default="${2:-}" [[ -n "$default" ]] && echo -ne "${YELLOW} ➜ ${NC}${prompt} [${CYAN}${default}${NC}]: " \ || echo -ne "${YELLOW} ➜ ${NC}${prompt}: " read -r REPLY; REPLY="${REPLY:-$default}" } ask_secret() { echo -ne "${YELLOW} ➜ ${NC}${1}: " read -rs REPLY; echo } confirm() { echo -ne "${YELLOW} ➜ ${NC}${1} [${CYAN}j${NC}/N]: " read -r ans; [[ "$ans" =~ ^[jJyY]$ ]] } # ── Banner ───────────────────────────────────────────────────────────────────── print_banner() { clear echo -e "${BOLD}${BLUE}" cat << 'BANNER' ___ _ _ | __| _ ___ __ _| |_ _ _ __ _ | | __ __ __ | _| '_/ -_) _` | _| '_/ _` | | |__/ _\ \/ / |_||_| \___\__, |\__|_| \__,_| |____\__/_/\_/ |__/ Proxmox LXC Installer BANNER echo -e "${NC}" echo -e " ${CYAN}Installiert: Docker · Portainer · Filebrowser · Freqtrade${NC}" echo -e " ${CYAN}Quelle: git.obraasch.de/olli1986/freqtrade-proxmox-deploy${NC}\n" echo -e " ${YELLOW}⚠ Nur für Test-/Lernzwecke. Trading auf eigenes Risiko!${NC}\n" } # ── Proxmox-Checks ───────────────────────────────────────────────────────────── check_host() { step "Proxmox Host prüfen" [[ $EUID -ne 0 ]] && error "Bitte als root ausführen!" command -v pct &>/dev/null || error "pct nicht gefunden – läuft das auf Proxmox?" command -v pvesh &>/dev/null || error "pvesh nicht gefunden." command -v pveam &>/dev/null || error "pveam nicht gefunden." success "Proxmox erkannt" } # ── Ressourcen ermitteln ─────────────────────────────────────────────────────── detect_resources() { NEXT_ID=$(pvesh get /cluster/nextid 2>/dev/null | tr -d '[:space:]' || echo "100") # Nur rootdir-fähige Storages (local unterstützt KEINE Container-Rootfs!) STORAGES=$(pvesm status --content rootdir 2>/dev/null | awk 'NR>1{print $1}' | tr '\n' ' ' || echo "local-lvm") [[ -z "${STORAGES// }" ]] && STORAGES="local-lvm" DEFAULT_STORAGE=$(pvesm status --content rootdir 2>/dev/null | awk 'NR==2{print $1}' || echo "local-lvm") [[ -z "$DEFAULT_STORAGE" ]] && DEFAULT_STORAGE="local-lvm" BRIDGES=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | tr '\n' ' ' || echo "vmbr0") DEFAULT_BRIDGE=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | head -1 || echo "vmbr0") } # ── Debian 12 Template ───────────────────────────────────────────────────────── prepare_template() { step "Debian 12 Template prüfen" TEMPLATE_FILE=$(pveam list local 2>/dev/null | grep -oP 'debian-12-standard_\S+' | sort -V | tail -1 || true) if [[ -z "$TEMPLATE_FILE" ]]; then info "Nicht lokal vorhanden – lade herunter..." pveam update &>/dev/null || warn "pveam update fehlgeschlagen" TEMPLATE_FILE=$(pveam available --section system 2>/dev/null \ | grep -oP 'debian-12-standard_\S+' | sort -V | tail -1 || echo "debian-12-standard_12.7-1_amd64.tar.zst") info "Lade: $TEMPLATE_FILE" pveam download local "$TEMPLATE_FILE" || error "Template-Download fehlgeschlagen!" fi TEMPLATE_PATH="local:vztmpl/${TEMPLATE_FILE}" success "Template bereit: $TEMPLATE_FILE" } # ── Konfiguration abfragen ───────────────────────────────────────────────────── collect_config() { step "LXC Konfiguration" echo -e " ${CYAN}Storages (rootdir-fähig):${NC} ${BOLD}${STORAGES}${NC}" echo -e " ${YELLOW} ↑ 'local' reicht nicht – braucht z.B. local-lvm${NC}" echo -e " ${CYAN}Bridges:${NC} ${BRIDGES}\n" ask "LXC Container-ID" "$NEXT_ID" CT_ID="$REPLY" [[ "$CT_ID" =~ ^[0-9]+$ ]] || error "Muss eine Zahl sein!" pct status "$CT_ID" &>/dev/null && error "CT-ID $CT_ID existiert bereits!" ask "Hostname" "freqtrade" ; CT_HOST="$REPLY" ask "CPU Kerne" "2" ; CT_CORES="$REPLY" ask "RAM in MB" "2048" ; CT_RAM="$REPLY" ask "Swap in MB" "512" ; CT_SWAP="$REPLY" ask "Disk in GB" "10" ; CT_DISK="$REPLY" ask "Storage Pool" "$DEFAULT_STORAGE" ; CT_STORAGE="$REPLY" ask "Bridge" "$DEFAULT_BRIDGE" ; CT_BRIDGE="$REPLY" ask "Netzwerk (dhcp oder 192.168.1.100/24)" "dhcp" ; CT_IP="$REPLY" CT_GW="" if [[ "$CT_IP" != "dhcp" ]]; then ask "Gateway"; CT_GW="$REPLY" fi ask_secret "Root-Passwort für den LXC" CT_PASS="$REPLY" [[ ${#CT_PASS} -lt 8 ]] && error "Passwort mind. 8 Zeichen!" step "Freqtrade Konfiguration" ask "FreqUI Port" "8080" ; FT_PORT="$REPLY" ask "Portainer Port" "9000" ; PT_PORT="$REPLY" ask "Filebrowser Port" "8081" ; FB_PORT="$REPLY" ask "Börse" "binance" ; FT_EXCHANGE="$REPLY" ask "Stake-Währung" "USDC" ; FT_STAKE="$REPLY" ask "Max. offene Trades" "3" ; FT_MAX_TRADES="$REPLY" ask "Timeframe" "5m" ; FT_TIMEFRAME="$REPLY" ask "FreqUI Benutzername" "freqtrader" ; FT_API_USER="$REPLY" ask_secret "FreqUI Passwort (mind. 8 Zeichen)" FT_API_PASS="$REPLY" [[ ${#FT_API_PASS} -lt 8 ]] && error "Passwort mind. 8 Zeichen!" FT_TELEGRAM=false; TG_TOKEN=""; TG_CHATID="" if confirm "Telegram-Benachrichtigungen?"; then FT_TELEGRAM=true ask "Telegram Bot-Token" ; TG_TOKEN="$REPLY" ask "Telegram Chat-ID" ; TG_CHATID="$REPLY" fi # Passwörter base64-kodiert weitergeben (sicher für Heredoc-Transport) CT_PASS_B64=$(printf '%s' "$CT_PASS" | base64 -w0) FT_API_PASS_B64=$(printf '%s' "$FT_API_PASS" | base64 -w0) TG_TOKEN_B64=$(printf '%s' "$TG_TOKEN" | base64 -w0) # Zufällige Secrets JWT_SECRET=$(openssl rand -hex 32) WS_TOKEN=$(openssl rand -hex 16) echo -e "\n${BOLD}${BLUE}══════════════ Zusammenfassung ══════════════${NC}" echo -e " CT ${CYAN}${CT_ID}${NC} | ${CYAN}${CT_HOST}${NC} | ${CYAN}${CT_CORES}${NC}x CPU | ${CYAN}${CT_RAM}${NC}MB RAM | ${CYAN}${CT_DISK}${NC}GB | ${CYAN}${CT_STORAGE}${NC}" echo -e " Netz: ${CYAN}${CT_IP}${NC} via ${CYAN}${CT_BRIDGE}${NC}" echo -e " FreqUI: ${CYAN}:${FT_PORT}${NC} | Portainer: ${CYAN}:${PT_PORT}${NC} | Filebrowser: ${CYAN}:${FB_PORT}${NC}" echo -e " Börse: ${CYAN}${FT_EXCHANGE}${NC} | Stake: ${CYAN}${FT_STAKE}${NC} | Telegram: ${CYAN}${FT_TELEGRAM}${NC}" echo -e "${BOLD}${BLUE}════════════════════════════════════════════${NC}\n" confirm "Installation starten?" || { info "Abgebrochen."; exit 0; } } # ── LXC erstellen ────────────────────────────────────────────────────────────── create_lxc() { step "LXC Container ${CT_ID} erstellen" local NET_CONFIG="name=eth0,bridge=${CT_BRIDGE},firewall=1" if [[ "$CT_IP" == "dhcp" ]]; then NET_CONFIG+=",ip=dhcp" else NET_CONFIG+=",ip=${CT_IP},gw=${CT_GW}" fi pct create "$CT_ID" "$TEMPLATE_PATH" \ --hostname "$CT_HOST" \ --password "$CT_PASS" \ --cores "$CT_CORES" \ --memory "$CT_RAM" \ --swap "$CT_SWAP" \ --rootfs "${CT_STORAGE}:${CT_DISK}" \ --net0 "$NET_CONFIG" \ --features "nesting=1,keyctl=1" \ --unprivileged 1 \ --onboot 1 \ --timezone "Europe/Berlin" \ --start 0 success "LXC erstellt" # TUN-Device für NordVPN/Gluetun vorbereiten # (muss VOR dem Start in die Config, danach braucht install-vpn.sh keinen manuellen Eingriff) cat >> "/etc/pve/lxc/${CT_ID}.conf" << 'TUNCFG' # TUN-Device fuer NordVPN/Gluetun (eingetragen von freqtrade-lxc-install.sh) lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file TUNCFG success "TUN-Device in LXC-Config eingetragen" # Container mit Tag "freqtrade" versehen (für proxmox-vpn-install.sh nutzbar) pct set "$CT_ID" --tags "freqtrade" 2>/dev/null || true info "Starte Container..." pct start "$CT_ID" info "Warte auf Netzwerk..." local tries=0 until pct exec "$CT_ID" -- bash -c "ip route | grep -q default" 2>/dev/null; do sleep 3; tries=$((tries+1)) echo -n "." [[ $tries -ge 20 ]] && error "Netzwerk nicht erreichbar – Bridge/IP prüfen!" done echo success "Container läuft und hat Netzwerk" } # ── Inner-Setup-Script generieren und im LXC ausführen ──────────────────────── run_inner_setup() { step "Installation im LXC starten" # Pairs korrekt aufbauen (BTC/STAKE, ETH/STAKE, BNB/STAKE) local PAIRS="\"BTC/${FT_STAKE}\", \"ETH/${FT_STAKE}\", \"BNB/${FT_STAKE}\"" # Telegram JSON-Block local TG_ENABLED="false" [[ "$FT_TELEGRAM" == "true" ]] && TG_ENABLED="true" cat > /tmp/ft_lxc_setup.sh << INNEREOF #!/usr/bin/env bash set -euo pipefail GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m' ok() { echo -e "\${GREEN}[ OK ]\${NC} \$*"; } inf() { echo -e "\${CYAN}[INF]\${NC} \$*"; } warn() { echo -e "\${YELLOW}[WARN]\${NC} \$*"; } fail() { echo -e "\${RED}[ERR]\${NC} \$*"; exit 1; } INSTALL_DIR="/opt/docker-projekte" FT_DIR="\$INSTALL_DIR/freqtrade" # Konfigurationswerte (vom Installer übergeben) FT_PORT="${FT_PORT}" FT_EXCHANGE="${FT_EXCHANGE}" FT_STAKE="${FT_STAKE}" FT_MAX_TRADES="${FT_MAX_TRADES}" FT_TIMEFRAME="${FT_TIMEFRAME}" FT_API_USER="${FT_API_USER}" FT_API_PASS=\$(echo "${FT_API_PASS_B64}" | base64 -d) TG_TOKEN=\$(echo "${TG_TOKEN_B64}" | base64 -d) TG_CHATID="${TG_CHATID}" TG_ENABLED="${TG_ENABLED}" JWT_SECRET="${JWT_SECRET}" WS_TOKEN="${WS_TOKEN}" PT_PORT="${PT_PORT}" FB_PORT="${FB_PORT}" # ── System ────────────────────────────────────────────────────────────────────── inf "System aktualisieren..." export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq curl ca-certificates gnupg python3 net-tools ok "Systempakete installiert" # ── Docker ────────────────────────────────────────────────────────────────────── inf "Docker installieren..." curl -fsSL https://get.docker.com | sh > /dev/null 2>&1 systemctl enable docker --now > /dev/null 2>&1 ok "Docker: \$(docker --version | grep -oP 'Docker version \K[^,]+')" # ── Verzeichnisse ──────────────────────────────────────────────────────────────── mkdir -p "\$FT_DIR" mkdir -p "\$INSTALL_DIR/portainer" mkdir -p "\$INSTALL_DIR/filebrowser/config" mkdir -p "\$INSTALL_DIR/filebrowser/database" ok "Verzeichnisse angelegt" # ── Portainer ─────────────────────────────────────────────────────────────────── inf "Portainer installieren (Port \${PT_PORT})..." cat > "\$INSTALL_DIR/portainer/docker-compose.yml" << 'PTEOF' services: portainer: image: portainer/portainer-ce:latest container_name: portainer restart: unless-stopped ports: - "PTPORT:9000" - "9443:9443" volumes: - /var/run/docker.sock:/var/run/docker.sock - portainer_data:/data volumes: portainer_data: PTEOF sed -i "s/PTPORT/\${PT_PORT}/" "\$INSTALL_DIR/portainer/docker-compose.yml" cd "\$INSTALL_DIR/portainer" docker compose up -d > /dev/null 2>&1 ok "Portainer gestartet" # ── Filebrowser ────────────────────────────────────────────────────────────────── inf "Filebrowser installieren (Port \${FB_PORT})..." cat > "\$INSTALL_DIR/filebrowser/docker-compose.yml" << 'FBEOF' services: filebrowser: image: filebrowser/filebrowser:latest container_name: filebrowser user: "0:0" ports: - "FBPORT:8080" volumes: - /opt/docker-projekte:/srv - /opt/docker-projekte/filebrowser/database:/database - /opt/docker-projekte/filebrowser/config:/config command: ["--address","0.0.0.0","--port","8080", "--database","/database/filebrowser.db", "--root","/srv","--noauth=false"] restart: unless-stopped FBEOF sed -i "s/FBPORT/\${FB_PORT}/" "\$INSTALL_DIR/filebrowser/docker-compose.yml" cd "\$INSTALL_DIR/filebrowser" docker compose up -d > /dev/null 2>&1 # Warten bis Filebrowser das Zufallspasswort geloggt hat (Log-Format ist konstant) # || true verhindert Abbruch durch set -e wenn grep nichts findet FB_PASS="" for i in \$(seq 1 10); do FB_PASS=\$(docker logs filebrowser 2>&1 | grep -oP "(?<=randomly generated password: )\S+" | tail -1 || true) [[ -n "\$FB_PASS" ]] && break sleep 2 done if [[ -z "\$FB_PASS" ]]; then warn "Passwort nicht im Log gefunden – manuell prüfen: docker logs filebrowser" FB_PASS="siehe: docker logs filebrowser" fi ok "Filebrowser gestartet – Passwort: \${FB_PASS}" echo "FILEBROWSER_PASS=\${FB_PASS}" >> /root/ft_install_info.txt # ── Freqtrade docker-compose.yml ──────────────────────────────────────────────── inf "Freqtrade docker-compose.yml herunterladen..." cd "\$FT_DIR" curl -fsSL https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml \ -o docker-compose.yml 2>/dev/null # WebUI von außen erreichbar machen sed -i 's/127\.0\.0\.1/0.0.0.0/g' docker-compose.yml # Benutzerdefinierten Port setzen falls nicht 8080 if [[ "\${FT_PORT}" != "8080" ]]; then sed -i "s/0\.0\.0\.0:8080/0.0.0.0:\${FT_PORT}/" docker-compose.yml fi ok "docker-compose.yml bereit" # ── user_data erstellen ────────────────────────────────────────────────────────── inf "user_data Verzeichnis erstellen..." docker compose run --rm freqtrade create-userdir --userdir user_data > /dev/null 2>&1 ok "user_data erstellt" # ── config.json mit Python3 generieren (sicheres JSON, keine Escaping-Probleme) ── inf "config.json erstellen..." export FT_EXCHANGE FT_STAKE FT_MAX_TRADES FT_TIMEFRAME FT_API_USER FT_API_PASS export TG_TOKEN TG_CHATID TG_ENABLED JWT_SECRET WS_TOKEN FT_DIR FT_PORT python3 << 'PYEOF' import json, os stake = os.environ["FT_STAKE"] exchange = os.environ["FT_EXCHANGE"] config = { "max_open_trades": int(os.environ["FT_MAX_TRADES"]), "stake_currency": stake, "stake_amount": "unlimited", "tradable_balance_ratio": 0.99, "fiat_display_currency": "EUR", "timeframe": os.environ["FT_TIMEFRAME"], "dry_run": True, "dry_run_wallet": 1000, "cancel_open_orders_on_exit": False, "trading_mode": "spot", "margin_mode": "", "unfilledtimeout": { "entry": 10, "exit": 10, "exit_timeout_count": 0, "unit": "minutes" }, "entry_pricing": { "price_side": "same", "use_order_book": True, "order_book_top": 1, "price_last_balance": 0.0, "check_depth_of_market": {"enabled": False, "bids_to_ask_delta": 1} }, "exit_pricing": { "price_side": "same", "use_order_book": True, "order_book_top": 1 }, "exchange": { "name": exchange, "key": "", "secret": "", "ccxt_config": {}, "ccxt_async_config": {}, "pair_whitelist": [ f"BTC/{stake}", f"ETH/{stake}", f"BNB/{stake}" ], "pair_blacklist": [ "BNB/.*" ] }, "pairlists": [{"method": "StaticPairList"}], "telegram": { "enabled": os.environ["TG_ENABLED"] == "true", "token": os.environ["TG_TOKEN"], "chat_id": os.environ["TG_CHATID"] }, "api_server": { "enabled": True, "listen_ip_address": "0.0.0.0", "listen_port": 8080, "verbosity": "error", "enable_openapi": False, "jwt_secret_key": os.environ["JWT_SECRET"], "ws_token": os.environ["WS_TOKEN"], "CORS_origins": [], "username": os.environ["FT_API_USER"], "password": os.environ["FT_API_PASS"] }, "bot_name": "freqtrade", "initial_state": "running", "force_entry_enable": False, "internals": {"process_throttle_secs": 5} } config_path = os.environ["FT_DIR"] + "/user_data/config.json" with open(config_path, "w") as f: json.dump(config, f, indent=4) print(f"config.json geschrieben: {config_path}") PYEOF ok "config.json erstellt (via Python3, valides JSON)" # ── Freqtrade starten ──────────────────────────────────────────────────────────── inf "Freqtrade starten..." cd "\$FT_DIR" docker compose up -d > /dev/null 2>&1 sleep 6 if docker ps --filter "name=freqtrade" --filter "status=running" | grep -q freqtrade; then ok "Freqtrade läuft" else echo -e "\${YELLOW}[WARN]\${NC} Freqtrade nicht gestartet – prüfe: docker logs freqtrade" fi # ── Ergebnis-Datei ──────────────────────────────────────────────────────────────── LXC_IP=\$(hostname -I | awk '{print \$1}') cat >> /root/ft_install_info.txt << INFOEOF LXC_IP=\$LXC_IP FT_PORT=\${FT_PORT} FT_API_USER=\${FT_API_USER} FB_PORT=\${FB_PORT} PT_PORT=\${PT_PORT} INFOEOF echo "" echo "=======================================" echo " Freqtrade Installation abgeschlossen " echo " FreqUI: http://\$LXC_IP:\${FT_PORT} " echo "=======================================" INNEREOF pct push "$CT_ID" /tmp/ft_lxc_setup.sh /root/ft_setup.sh pct exec "$CT_ID" -- chmod +x /root/ft_setup.sh info "Starte Setup im LXC (ca. 2-3 Minuten)..." pct exec "$CT_ID" -- bash /root/ft_setup.sh rm -f /tmp/ft_lxc_setup.sh success "Setup im LXC abgeschlossen" } # ── Ergebnis anzeigen ────────────────────────────────────────────────────────── show_result() { local TMP_INFO="/tmp/ft_info_${CT_ID}.txt" pct pull "$CT_ID" /root/ft_install_info.txt "$TMP_INFO" 2>/dev/null || true LXC_IP=""; FB_PASS=""; FILEBROWSER_PASS="" if [[ -f "$TMP_INFO" ]]; then source "$TMP_INFO" 2>/dev/null || true rm -f "$TMP_INFO" fi [[ -z "${LXC_IP:-}" ]] && LXC_IP=$(pct exec "$CT_ID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "unbekannt") [[ -z "${FILEBROWSER_PASS:-}" ]] && FILEBROWSER_PASS="${FB_PASS:-unbekannt}" echo echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${GREEN}║ ✅ Freqtrade LXC Installation komplett! ║${NC}" echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════╝${NC}" echo echo -e " ${BOLD}LXC:${NC} CT ${CYAN}${CT_ID}${NC} | ${CYAN}${CT_HOST}${NC} | IP: ${CYAN}${LXC_IP}${NC}" echo echo -e " ${BOLD}${GREEN}Freqtrade WebUI:${NC}" echo -e " ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}" echo -e " Benutzer: ${CYAN}${FT_API_USER}${NC} | Passwort: ${CYAN}(dein gewähltes)${NC}" echo echo -e " ${BOLD}${GREEN}Portainer (Docker-Verwaltung):${NC}" echo -e " ${CYAN}http://${LXC_IP}:${PT_PORT}${NC}" echo -e " ${YELLOW}→ Beim ersten Aufruf Admin-Passwort setzen!${NC}" echo echo -e " ${BOLD}${GREEN}Filebrowser (Dateimanager):${NC}" echo -e " ${CYAN}http://${LXC_IP}:${FB_PORT}${NC}" echo -e " Benutzer: ${CYAN}admin${NC} | Passwort: ${BOLD}${CYAN}${FILEBROWSER_PASS}${NC}" echo -e " ${YELLOW}→ Passwort nach dem Login ändern!${NC}" echo echo -e " ${BOLD}Wichtige Dateien:${NC}" echo -e " Config: ${CYAN}/opt/docker-projekte/freqtrade/user_data/config.json${NC}" echo -e " Logs: ${CYAN}/opt/docker-projekte/freqtrade/user_data/logs/${NC}" echo echo -e " ${BOLD}${YELLOW}Nächste Schritte:${NC}" echo -e " ${YELLOW}1. Binance API-Keys in config.json eintragen${NC}" echo -e " ${YELLOW}2. dry_run: false setzen wenn bereit${NC}" echo -e " ${YELLOW}3. Strategie anpassen${NC}" echo -e " ${YELLOW}4. NordVPN optional: install-vpn.sh im LXC ausführen${NC}" echo echo -e " ${BOLD}LXC-Konsole öffnen:${NC} ${CYAN}pct enter ${CT_ID}${NC}" echo -e " ${BOLD}NordVPN Addon:${NC}" echo -e " ${CYAN}bash -c \"\$(curl -fsSL https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy/raw/branch/main/scripts/install-vpn.sh)\"${NC}" echo } # ── Hauptprogramm ────────────────────────────────────────────────────────────── main() { print_banner check_host detect_resources prepare_template collect_config create_lxc run_inner_setup show_result } main "$@"