Files
proxmox-lxc-shell-commands/proxmox-lxc-openwebui.sh

408 lines
13 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# OpenWebUI LXC Installation Script
# Description: Install OpenWebUI - LLM Web Interface with Ollama integration
# OS: Debian 12 (Bookworm)
# Ports: Web UI: 8080
# Repository: https://github.com/open-webui/open-webui
# Last Updated: 2026-01
set -euo pipefail
#################################################################
# Configuration Variables
#################################################################
# Container Configuration
CT_ID=${CT_ID:-27002}
CT_HOSTNAME=${CT_HOSTNAME:-"eunha-openwebui"}
CT_CORES=${CT_CORES:-4}
CT_MEMORY=${CT_MEMORY:-8192}
CT_SWAP=${CT_SWAP:-4096}
CT_DISK_SIZE=${CT_DISK_SIZE:-50}
# Network Configuration
CT_IP=${CT_IP:-"dhcp"}
CT_GATEWAY=${CT_GATEWAY:-""}
CT_BRIDGE=${CT_BRIDGE:-"vmbr0"}
CT_NAMESERVER=${CT_NAMESERVER:-"8.8.8.8"}
# Storage Configuration
CT_STORAGE=${CT_STORAGE:-"local-lvm"}
TEMPLATE_STORAGE=${TEMPLATE_STORAGE:-"local"}
# Debian Template
DEBIAN_VERSION="12"
TEMPLATE_NAME=""
# OpenWebUI Configuration
OPENWEBUI_PORT=${OPENWEBUI_PORT:-8080}
OPENWEBUI_DATA="/opt/open-webui"
OLLAMA_HOST=${OLLAMA_HOST:-"http://host.docker.internal:11434"}
# Container Options - privileged for Docker support
CT_ONBOOT=${CT_ONBOOT:-1}
CT_UNPRIVILEGED=0
CT_FEATURES="keyctl=1,nesting=1"
#################################################################
# Color Output Functions
#################################################################
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
#################################################################
# Validation Functions
#################################################################
check_root() {
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root"
exit 1
fi
}
check_proxmox() {
if ! command -v pct &> /dev/null; then
error "This script must be run on a Proxmox VE host"
exit 1
fi
}
check_container_exists() {
if pct status "$CT_ID" &> /dev/null; then
error "Container ID $CT_ID already exists"
exit 1
fi
}
detect_and_download_template() {
info "Updating template database..."
pveam update 2>&1 | grep -q "update successful\|already up to date" || true
info "Detecting available Debian ${DEBIAN_VERSION} template..."
local available_template
available_template=$(pveam available --section system 2>/dev/null | grep "debian-${DEBIAN_VERSION}" | grep "standard" | tail -1 | awk '{print $2}')
if [[ -z "$available_template" ]]; then
error "No Debian ${DEBIAN_VERSION} template found"
exit 1
fi
TEMPLATE_NAME="$available_template"
info "Found template: $TEMPLATE_NAME"
if pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE_NAME"; then
success "Template already downloaded"
return 0
fi
warn "Downloading Debian template..."
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE_NAME" 2>&1
success "Template downloaded successfully"
}
#################################################################
# Container Creation Functions
#################################################################
create_container() {
info "Creating LXC container $CT_ID ($CT_HOSTNAME)..."
local net_config="name=eth0,bridge=${CT_BRIDGE},ip=${CT_IP}"
if [[ "$CT_IP" != "dhcp" ]] && [[ -n "$CT_GATEWAY" ]]; then
net_config="${net_config},gw=${CT_GATEWAY}"
fi
pct create "$CT_ID" \
"${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE_NAME}" \
--hostname "$CT_HOSTNAME" \
--cores "$CT_CORES" \
--memory "$CT_MEMORY" \
--swap "$CT_SWAP" \
--rootfs "${CT_STORAGE}:${CT_DISK_SIZE}" \
--net0 "$net_config" \
--nameserver "$CT_NAMESERVER" \
--onboot "$CT_ONBOOT" \
--unprivileged "$CT_UNPRIVILEGED" \
--features "$CT_FEATURES" \
--ostype debian || {
error "Failed to create container"
exit 1
}
success "Container $CT_ID created successfully"
}
start_container() {
info "Starting container $CT_ID..."
pct start "$CT_ID" || {
error "Failed to start container"
exit 1
}
info "Waiting for container to boot..."
sleep 5
local max_attempts=30
local attempt=0
while [[ $attempt -lt $max_attempts ]]; do
if pct exec "$CT_ID" -- test -f /etc/os-release 2>/dev/null; then
success "Container is running"
return 0
fi
sleep 1
((attempt++))
done
error "Container failed to start properly"
exit 1
}
configure_autologin() {
info "Configuring root autologin..."
pct exec "$CT_ID" -- bash -c 'mkdir -p /etc/systemd/system/console-getty.service.d/'
pct exec "$CT_ID" -- bash -c 'cat > /etc/systemd/system/console-getty.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud console 115200,38400,9600 \$TERM
EOF'
pct exec "$CT_ID" -- bash -c 'systemctl daemon-reload'
success "Autologin configured"
}
#################################################################
# Application Installation Functions
#################################################################
install_dependencies() {
info "Installing dependencies..."
pct exec "$CT_ID" -- bash -c "apt-get update -qq"
pct exec "$CT_ID" -- bash -c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
curl ca-certificates gnupg git"
success "Dependencies installed"
}
install_docker() {
info "Installing Docker..."
pct exec "$CT_ID" -- bash -c '
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin
'
pct exec "$CT_ID" -- bash -c "systemctl enable docker && systemctl start docker"
success "Docker installed"
}
install_openwebui() {
info "Installing OpenWebUI..."
# Create data directory
pct exec "$CT_ID" -- bash -c "mkdir -p ${OPENWEBUI_DATA}"
# Create docker-compose.yml
pct exec "$CT_ID" -- bash -c "cat > ${OPENWEBUI_DATA}/docker-compose.yml << 'COMPOSE_EOF'
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
ports:
- \"${OPENWEBUI_PORT}:8080\"
volumes:
- open-webui-data:/app/backend/data
environment:
- OLLAMA_BASE_URL=${OLLAMA_HOST}
- WEBUI_AUTH=true
- ENABLE_SIGNUP=true
- ENABLE_API_KEY=true
extra_hosts:
- \"host.docker.internal:host-gateway\"
volumes:
open-webui-data:
COMPOSE_EOF"
# Update compose file with actual values
pct exec "$CT_ID" -- bash -c "sed -i 's|\${OPENWEBUI_PORT}|${OPENWEBUI_PORT}|g' ${OPENWEBUI_DATA}/docker-compose.yml"
pct exec "$CT_ID" -- bash -c "sed -i 's|\${OLLAMA_HOST}|${OLLAMA_HOST}|g' ${OPENWEBUI_DATA}/docker-compose.yml"
# Create systemd service
pct exec "$CT_ID" -- bash -c "cat > /etc/systemd/system/openwebui.service << 'SERVICE_EOF'
[Unit]
Description=OpenWebUI Container
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/open-webui
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
SERVICE_EOF"
# Pull and start
info "Pulling OpenWebUI Docker image (this may take a while)..."
pct exec "$CT_ID" -- bash -c "cd ${OPENWEBUI_DATA} && docker compose pull"
pct exec "$CT_ID" -- bash -c "systemctl daemon-reload && systemctl enable openwebui && systemctl start openwebui"
# Wait for OpenWebUI to start
info "Waiting for OpenWebUI to start..."
local max_attempts=60
local attempt=0
while [[ $attempt -lt $max_attempts ]]; do
if pct exec "$CT_ID" -- bash -c "curl -s -o /dev/null -w '%{http_code}' http://localhost:${OPENWEBUI_PORT}" 2>/dev/null | grep -q "200\|301\|302"; then
success "OpenWebUI is running"
return 0
fi
sleep 5
((attempt++))
if [[ $((attempt % 6)) -eq 0 ]]; then
info "Still waiting... ($((attempt * 5))s elapsed)"
fi
done
warn "OpenWebUI may still be starting. Check: pct exec $CT_ID -- docker logs open-webui"
}
add_container_notes() {
info "Adding container notes..."
local container_ip
if [[ "$CT_IP" == "dhcp" ]]; then
sleep 3
container_ip=$(pct exec "$CT_ID" -- hostname -I 2>/dev/null | awk '{print $1}')
if [[ -z "$container_ip" ]]; then
container_ip="[DHCP - check after boot]"
fi
else
container_ip="${CT_IP%/*}"
fi
local notes="OpenWebUI - LLM Web Interface
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 CONTAINER DETAILS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Container ID: ${CT_ID}
Hostname: ${CT_HOSTNAME}
IP Address: ${container_ip}
CPU Cores: ${CT_CORES}
Memory: ${CT_MEMORY}MB
Disk Size: ${CT_DISK_SIZE}GB
🌐 APPLICATION ACCESS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Web UI: http://${container_ip}:${OPENWEBUI_PORT}
Ollama Host: ${OLLAMA_HOST}
📧 SETUP INSTRUCTIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Access Web UI and create admin account
2. Configure Ollama connection if needed
3. Add LLM models via Ollama
🔧 SERVICE MANAGEMENT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Status: pct exec ${CT_ID} -- docker ps
Logs: pct exec ${CT_ID} -- docker logs -f open-webui
Restart: pct exec ${CT_ID} -- systemctl restart openwebui
Stop: pct exec ${CT_ID} -- systemctl stop openwebui
Start: pct exec ${CT_ID} -- systemctl start openwebui
📦 CONTAINER MANAGEMENT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Enter: pct enter ${CT_ID}
Start: pct start ${CT_ID}
Stop: pct stop ${CT_ID}
Delete: pct destroy ${CT_ID}
AUTO-GENERATED BY: proxmox-infra/proxmox-lxc-shell-commands"
pct set "$CT_ID" -description "$notes" 2>/dev/null || true
success "Container notes added"
}
display_info() {
local container_ip
if [[ "$CT_IP" == "dhcp" ]]; then
container_ip=$(pct exec "$CT_ID" -- hostname -I | awk '{print $1}')
else
container_ip="${CT_IP%/*}"
fi
echo ""
echo "================================================================="
success "OpenWebUI LXC Container Setup Complete!"
echo "================================================================="
echo ""
info "All access information has been saved to container Notes"
echo ""
echo "Container Details:"
echo " • Container ID: $CT_ID"
echo " • Hostname: $CT_HOSTNAME"
echo " • IP Address: $container_ip"
echo ""
echo "Application Access:"
echo " • Web UI: http://${container_ip}:${OPENWEBUI_PORT}"
echo " • Ollama Host: ${OLLAMA_HOST}"
echo ""
echo "Setup Instructions:"
echo " 1. Access Web UI and create admin account"
echo " 2. First registered user becomes admin"
echo " 3. Configure Ollama connection settings"
echo ""
echo "Service Management:"
echo " • Status: pct exec $CT_ID -- docker ps"
echo " • Logs: pct exec $CT_ID -- docker logs -f open-webui"
echo " • Restart: pct exec $CT_ID -- systemctl restart openwebui"
echo ""
echo "================================================================="
}
#################################################################
# Main Execution
#################################################################
main() {
info "Starting OpenWebUI LXC container creation..."
echo ""
check_root
check_proxmox
check_container_exists
detect_and_download_template
create_container
start_container
configure_autologin
install_dependencies
install_docker
install_openwebui
add_container_notes
display_info
}
main "$@"