- proxmox-lxc-immich.sh: 사진/동영상 관리 (Google Photos 대안) - proxmox-lxc-outline.sh: 팀 위키/문서 관리 (Notion 대안) - proxmox-lxc-gitea.sh: 경량 Git 서버 (GitHub/GitLab 대안) 모든 스크립트: Docker-in-LXC, PostgreSQL, systemd 서비스 등록 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
313 lines
9.7 KiB
Bash
Executable File
313 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
# Outline LXC Installation Script
|
||
# Description: Modern team knowledge base and wiki (Notion alternative)
|
||
# OS: Debian 12 (Bookworm) - Auto-detected latest version
|
||
# Ports: Web UI: 3000
|
||
# Repository: https://github.com/outline/outline
|
||
# Last Updated: 2026-01-05
|
||
|
||
set -euo pipefail
|
||
|
||
#################################################################
|
||
# Configuration Variables
|
||
#################################################################
|
||
|
||
CT_ID=${CT_ID:-24005}
|
||
CT_HOSTNAME=${CT_HOSTNAME:-"outline"}
|
||
CT_CORES=${CT_CORES:-2}
|
||
CT_MEMORY=${CT_MEMORY:-4096}
|
||
CT_SWAP=${CT_SWAP:-1024}
|
||
CT_DISK_SIZE=${CT_DISK_SIZE:-20}
|
||
|
||
CT_IP=${CT_IP:-"dhcp"}
|
||
CT_GATEWAY=${CT_GATEWAY:-""}
|
||
CT_BRIDGE=${CT_BRIDGE:-"vmbr0"}
|
||
CT_NAMESERVER=${CT_NAMESERVER:-"8.8.8.8"}
|
||
|
||
CT_STORAGE=${CT_STORAGE:-"local-lvm"}
|
||
TEMPLATE_STORAGE=${TEMPLATE_STORAGE:-"local"}
|
||
DEBIAN_VERSION="12"
|
||
TEMPLATE_NAME=""
|
||
|
||
# Application Configuration
|
||
OUTLINE_PORT=${OUTLINE_PORT:-3000}
|
||
SECRET_KEY=${SECRET_KEY:-"$(openssl rand -hex 32)"}
|
||
UTILS_SECRET=${UTILS_SECRET:-"$(openssl rand -hex 32)"}
|
||
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 20)"}
|
||
|
||
# Container Options
|
||
CT_ONBOOT=${CT_ONBOOT:-1}
|
||
CT_UNPRIVILEGED=${CT_UNPRIVILEGED:-0}
|
||
CT_FEATURES=${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"; }
|
||
|
||
#################################################################
|
||
# Functions
|
||
#################################################################
|
||
|
||
check_root() {
|
||
[[ $EUID -ne 0 ]] && error "This script must be run as root" && exit 1
|
||
}
|
||
|
||
check_proxmox() {
|
||
command -v pct &> /dev/null || { error "Run on Proxmox VE host"; exit 1; }
|
||
}
|
||
|
||
check_container_exists() {
|
||
pct status "$CT_ID" &> /dev/null && { error "Container $CT_ID exists"; exit 1; }
|
||
}
|
||
|
||
detect_and_download_template() {
|
||
info "Updating template database..."
|
||
pveam update 2>&1 || true
|
||
|
||
local available_template
|
||
available_template=$(pveam available --section system 2>/dev/null | grep "debian-${DEBIAN_VERSION}" | grep "standard" | tail -1 | awk '{print $2}')
|
||
[[ -z "$available_template" ]] && error "No Debian ${DEBIAN_VERSION} template found" && exit 1
|
||
|
||
TEMPLATE_NAME="$available_template"
|
||
info "Found template: $TEMPLATE_NAME"
|
||
|
||
pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE_NAME" || pveam download "$TEMPLATE_STORAGE" "$TEMPLATE_NAME" 2>&1
|
||
success "Template ready"
|
||
}
|
||
|
||
create_container() {
|
||
info "Creating LXC container $CT_ID ($CT_HOSTNAME)..."
|
||
local net_config="name=eth0,bridge=${CT_BRIDGE},ip=${CT_IP}"
|
||
[[ "$CT_IP" != "dhcp" ]] && [[ -n "$CT_GATEWAY" ]] && net_config="${net_config},gw=${CT_GATEWAY}"
|
||
|
||
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 || exit 1
|
||
success "Container created"
|
||
}
|
||
|
||
start_container() {
|
||
info "Starting container..."
|
||
pct start "$CT_ID" && sleep 5
|
||
success "Container started"
|
||
}
|
||
|
||
install_docker() {
|
||
info "Installing Docker..."
|
||
pct exec "$CT_ID" -- bash -c "apt-get update -qq"
|
||
pct exec "$CT_ID" -- bash -c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ca-certificates curl gnupg"
|
||
pct exec "$CT_ID" -- bash -c "install -m 0755 -d /etc/apt/keyrings"
|
||
pct exec "$CT_ID" -- bash -c "curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc"
|
||
pct exec "$CT_ID" -- bash -c "chmod a+r /etc/apt/keyrings/docker.asc"
|
||
pct exec "$CT_ID" -- bash -c 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null'
|
||
pct exec "$CT_ID" -- bash -c "apt-get update -qq"
|
||
pct exec "$CT_ID" -- bash -c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin"
|
||
pct exec "$CT_ID" -- bash -c "systemctl enable docker && systemctl start docker"
|
||
sleep 3
|
||
success "Docker installed"
|
||
}
|
||
|
||
install_outline() {
|
||
info "Installing Outline..."
|
||
pct exec "$CT_ID" -- bash -c "mkdir -p /opt/outline/{data,postgres,redis}"
|
||
|
||
# Create docker-compose.yml
|
||
pct exec "$CT_ID" -- bash -c "cat > /opt/outline/docker-compose.yml << 'EOF'
|
||
services:
|
||
outline:
|
||
image: docker.getoutline.com/outlinewiki/outline:latest
|
||
container_name: outline
|
||
restart: always
|
||
env_file: ./.env
|
||
ports:
|
||
- \"${OUTLINE_PORT}:3000\"
|
||
volumes:
|
||
- ./data:/var/lib/outline/data
|
||
depends_on:
|
||
- postgres
|
||
- redis
|
||
networks:
|
||
- outline-network
|
||
|
||
postgres:
|
||
image: postgres:15-alpine
|
||
container_name: outline-postgres
|
||
restart: always
|
||
environment:
|
||
POSTGRES_USER: outline
|
||
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
|
||
POSTGRES_DB: outline
|
||
volumes:
|
||
- ./postgres:/var/lib/postgresql/data
|
||
healthcheck:
|
||
test: [\"CMD-SHELL\", \"pg_isready -U outline -d outline\"]
|
||
interval: 10s
|
||
timeout: 5s
|
||
retries: 5
|
||
networks:
|
||
- outline-network
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
container_name: outline-redis
|
||
restart: always
|
||
volumes:
|
||
- ./redis:/data
|
||
healthcheck:
|
||
test: [\"CMD\", \"redis-cli\", \"ping\"]
|
||
interval: 10s
|
||
timeout: 5s
|
||
retries: 5
|
||
networks:
|
||
- outline-network
|
||
|
||
networks:
|
||
outline-network:
|
||
driver: bridge
|
||
EOF"
|
||
|
||
# Create .env file
|
||
pct exec "$CT_ID" -- bash -c "cat > /opt/outline/.env << 'EOF'
|
||
# –––––––––––––––– REQUIRED ––––––––––––––––
|
||
NODE_ENV=production
|
||
SECRET_KEY=${SECRET_KEY}
|
||
UTILS_SECRET=${UTILS_SECRET}
|
||
|
||
# Database
|
||
DATABASE_URL=postgres://outline:\${POSTGRES_PASSWORD}@postgres:5432/outline
|
||
DATABASE_CONNECTION_POOL_MIN=1
|
||
DATABASE_CONNECTION_POOL_MAX=10
|
||
PGSSLMODE=disable
|
||
|
||
# Redis
|
||
REDIS_URL=redis://redis:6379
|
||
|
||
# URL (update this for production)
|
||
URL=http://localhost:${OUTLINE_PORT}
|
||
PORT=3000
|
||
FORCE_HTTPS=false
|
||
|
||
# Storage
|
||
FILE_STORAGE=local
|
||
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
|
||
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400
|
||
|
||
# Authentication (configure at least one)
|
||
# For testing, you can use username/password
|
||
# SMTP is required for email login
|
||
# OIDC_CLIENT_ID=
|
||
# OIDC_CLIENT_SECRET=
|
||
# OIDC_AUTH_URI=
|
||
# OIDC_TOKEN_URI=
|
||
# OIDC_USERINFO_URI=
|
||
|
||
# Optional
|
||
DEFAULT_LANGUAGE=ko_KR
|
||
ENABLE_UPDATES=true
|
||
DEBUG=
|
||
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||
EOF"
|
||
|
||
info "Starting Outline containers..."
|
||
pct exec "$CT_ID" -- bash -c "cd /opt/outline && docker compose up -d"
|
||
|
||
info "Waiting for Outline to start (1-2 minutes)..."
|
||
local max_attempts=30
|
||
local attempt=1
|
||
while [[ $attempt -le $max_attempts ]]; do
|
||
if pct exec "$CT_ID" -- bash -c "curl -s -o /dev/null -w '%{http_code}' http://localhost:${OUTLINE_PORT}/" | grep -q "200\|302"; then
|
||
success "Outline is running!"
|
||
break
|
||
fi
|
||
echo -n "."
|
||
sleep 5
|
||
((attempt++))
|
||
done
|
||
echo ""
|
||
success "Outline installed"
|
||
}
|
||
|
||
create_outline_service() {
|
||
pct exec "$CT_ID" -- bash -c 'cat > /etc/systemd/system/outline.service << EOF
|
||
[Unit]
|
||
Description=Outline Wiki
|
||
Requires=docker.service
|
||
After=docker.service
|
||
|
||
[Service]
|
||
Type=oneshot
|
||
RemainAfterExit=yes
|
||
WorkingDirectory=/opt/outline
|
||
ExecStart=/usr/bin/docker compose up -d
|
||
ExecStop=/usr/bin/docker compose down
|
||
TimeoutStartSec=180
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF'
|
||
pct exec "$CT_ID" -- bash -c "systemctl daemon-reload && systemctl enable outline"
|
||
success "Outline service created"
|
||
}
|
||
|
||
verify_installation() {
|
||
local container_ip
|
||
container_ip=$(pct exec "$CT_ID" -- bash -c "hostname -I | awk '{print \$1}'" 2>/dev/null || echo "unknown")
|
||
|
||
echo ""
|
||
echo "================================================================"
|
||
echo -e "${GREEN}Outline Installation Complete!${NC}"
|
||
echo "================================================================"
|
||
echo "Container ID: $CT_ID | Hostname: $CT_HOSTNAME | IP: $container_ip"
|
||
echo ""
|
||
echo "Web UI: http://${container_ip}:${OUTLINE_PORT}"
|
||
echo ""
|
||
echo -e "${YELLOW}IMPORTANT: Configure authentication before use!${NC}"
|
||
echo ""
|
||
echo "Authentication options:"
|
||
echo " 1. OIDC (Keycloak, Auth0, etc.)"
|
||
echo " 2. SAML"
|
||
echo " 3. Google/GitHub OAuth"
|
||
echo ""
|
||
echo "Edit /opt/outline/.env to configure authentication,"
|
||
echo "then restart: docker compose restart outline"
|
||
echo ""
|
||
echo "Commands:"
|
||
echo " pct exec $CT_ID -- docker compose -f /opt/outline/docker-compose.yml logs -f"
|
||
echo " pct exec $CT_ID -- docker compose -f /opt/outline/docker-compose.yml ps"
|
||
echo ""
|
||
echo "Data location: /opt/outline/data"
|
||
echo "================================================================"
|
||
}
|
||
|
||
main() {
|
||
echo "================================================================"
|
||
echo "Outline LXC Installation Script"
|
||
echo "================================================================"
|
||
|
||
check_root
|
||
check_proxmox
|
||
check_container_exists
|
||
detect_and_download_template
|
||
create_container
|
||
start_container
|
||
install_docker
|
||
install_outline
|
||
create_outline_service
|
||
verify_installation
|
||
}
|
||
|
||
main "$@"
|