Files
proxmox-lxc-shell-commands/proxmox-lxc-mailcow.sh
DevOps 741aa997fa feat: Batch 5 LXC 설치 스크립트 추가 (Mailcow, Metabase, FlowiseAI)
- proxmox-lxc-mailcow.sh: Docker 기반 메일 서버 (4GB+ RAM)
- proxmox-lxc-metabase.sh: BI/데이터 분석 도구 (PostgreSQL 포함)
- proxmox-lxc-flowiseai.sh: LangChain 기반 AI 워크플로우 빌더

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 23:16:37 +09:00

454 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
# Mailcow LXC Installation Script
# Description: Mailcow Dockerized - Complete mail server suite
# OS: Debian 12 (Bookworm) - Auto-detected latest version
# Ports: SMTP: 25, 465, 587 | IMAP: 143, 993 | POP3: 110, 995 | Web: 80, 443
# Repository: https://github.com/mailcow/mailcow-dockerized
# Last Updated: 2026-01-05
set -euo pipefail
#################################################################
# Configuration Variables
#################################################################
# Container Configuration
CT_ID=${CT_ID:-26002} # Container ID
CT_HOSTNAME=${CT_HOSTNAME:-"mailcow"} # Container hostname
CT_CORES=${CT_CORES:-4} # CPU cores (mailcow needs more)
CT_MEMORY=${CT_MEMORY:-4096} # RAM in MB (minimum 4GB for mailcow)
CT_SWAP=${CT_SWAP:-1024} # Swap in MB
CT_DISK_SIZE=${CT_DISK_SIZE:-50} # Root disk size in GB (email storage)
# Network Configuration
CT_IP=${CT_IP:-"dhcp"} # IP address (dhcp or static like 192.168.1.100/24)
CT_GATEWAY=${CT_GATEWAY:-""} # Gateway (required for static IP)
CT_BRIDGE=${CT_BRIDGE:-"vmbr0"} # Network bridge
CT_NAMESERVER=${CT_NAMESERVER:-"8.8.8.8"} # DNS server
# Storage Configuration
CT_STORAGE=${CT_STORAGE:-"local-lvm"} # Storage pool for container
TEMPLATE_STORAGE=${TEMPLATE_STORAGE:-"local"} # Storage pool for templates
# Debian Template (will be auto-detected)
DEBIAN_VERSION="12" # Debian version
TEMPLATE_NAME="" # Auto-detected
# Mailcow Configuration
MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME:-"mail.example.com"} # Mail server FQDN
MAILCOW_TIMEZONE=${MAILCOW_TIMEZONE:-"Asia/Seoul"} # Timezone
MAILCOW_BRANCH=${MAILCOW_BRANCH:-"master"} # Git branch
# Container Options - IMPORTANT: Privileged required for Docker-in-LXC
CT_ONBOOT=${CT_ONBOOT:-1} # Start on boot (1=yes, 0=no)
CT_UNPRIVILEGED=${CT_UNPRIVILEGED:-0} # Must be 0 (privileged) for Docker
CT_FEATURES=${CT_FEATURES:-"keyctl=1,nesting=1"} # Container features for Docker
#################################################################
# Color Output Functions
#################################################################
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
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"
info "Please choose a different CT_ID or remove the existing container"
exit 1
fi
}
detect_and_download_template() {
info "Updating template database..."
if ! pveam update 2>&1 | grep -q "update successful\|already up to date"; then
warn "Template database update encountered issues, continuing anyway..."
fi
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 in available templates"
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 (this may take a few minutes)..."
if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE_NAME" 2>&1; then
success "Template downloaded successfully"
else
error "Failed to download template"
exit 1
fi
}
#################################################################
# 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
success "Container started successfully"
}
configure_autologin() {
info "Configuring automatic console login..."
pct exec "$CT_ID" -- bash -c "mkdir -p /etc/systemd/system/container-getty@1.service.d"
pct exec "$CT_ID" -- bash -c 'cat > /etc/systemd/system/container-getty@1.service.d/override.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM
EOF'
pct exec "$CT_ID" -- bash -c "systemctl daemon-reload"
pct exec "$CT_ID" -- bash -c "systemctl restart container-getty@1.service" 2>/dev/null || true
success "Automatic console login configured"
}
#################################################################
# Docker Installation
#################################################################
install_docker() {
info "Installing Docker in container $CT_ID..."
info "Updating package list..."
pct exec "$CT_ID" -- bash -c "apt-get update -qq"
info "Installing required packages..."
pct exec "$CT_ID" -- bash -c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
ca-certificates \
curl \
gnupg \
lsb-release \
git"
info "Adding Docker GPG key..."
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"
info "Adding Docker repository..."
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'
info "Installing Docker Engine..."
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"
pct exec "$CT_ID" -- bash -c "systemctl start docker"
sleep 3
if pct exec "$CT_ID" -- bash -c "docker --version" &>/dev/null; then
success "Docker installed successfully"
else
error "Docker installation failed"
exit 1
fi
}
#################################################################
# Mailcow Installation
#################################################################
install_mailcow() {
info "Installing Mailcow in container $CT_ID..."
# Clone mailcow repository
info "Cloning Mailcow repository..."
pct exec "$CT_ID" -- bash -c "cd /opt && git clone https://github.com/mailcow/mailcow-dockerized.git"
# Generate configuration
info "Generating Mailcow configuration..."
pct exec "$CT_ID" -- bash -c "cd /opt/mailcow-dockerized && \
MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} \
MAILCOW_TZ=${MAILCOW_TIMEZONE} \
./generate_config.sh"
# Start mailcow
info "Starting Mailcow (this may take several minutes)..."
pct exec "$CT_ID" -- bash -c "cd /opt/mailcow-dockerized && docker compose pull"
pct exec "$CT_ID" -- bash -c "cd /opt/mailcow-dockerized && docker compose up -d"
info "Waiting for Mailcow to initialize (60 seconds)..."
sleep 60
if pct exec "$CT_ID" -- bash -c "docker compose -f /opt/mailcow-dockerized/docker-compose.yml ps | grep -q 'Up'"; then
success "Mailcow installed and running"
else
warn "Mailcow may still be starting, please check status manually"
fi
}
create_mailcow_service() {
info "Creating Mailcow systemd service..."
pct exec "$CT_ID" -- bash -c 'cat > /etc/systemd/system/mailcow.service << EOF
[Unit]
Description=Mailcow Dockerized
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/mailcow-dockerized
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
EOF'
pct exec "$CT_ID" -- bash -c "systemctl daemon-reload"
pct exec "$CT_ID" -- bash -c "systemctl enable mailcow"
success "Mailcow service created"
}
#################################################################
# Container Notes
#################################################################
add_container_notes() {
info "Adding container notes with access information..."
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="Mailcow Dockerized - Complete Mail Server
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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
MAILCOW ACCESS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Admin UI: https://${container_ip}
SOGo Webmail: https://${container_ip}/SOGo
Mail Hostname: ${MAILCOW_HOSTNAME}
Default Admin: admin / moohoo (CHANGE IMMEDIATELY!)
PORTS USED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SMTP: 25 (MTA), 465 (Submission SSL), 587 (Submission)
IMAP: 143 (IMAP), 993 (IMAP SSL)
POP3: 110 (POP3), 995 (POP3 SSL)
Web: 80, 443
DOCKER COMMANDS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Status: pct exec ${CT_ID} -- docker compose -f /opt/mailcow-dockerized/docker-compose.yml ps
Logs: pct exec ${CT_ID} -- docker compose -f /opt/mailcow-dockerized/docker-compose.yml logs -f
Restart: pct exec ${CT_ID} -- docker compose -f /opt/mailcow-dockerized/docker-compose.yml restart
Update: pct exec ${CT_ID} -- bash -c 'cd /opt/mailcow-dockerized && ./update.sh'
CONTAINER MANAGEMENT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Enter: pct enter ${CT_ID}
Start: pct start ${CT_ID}
Stop: pct stop ${CT_ID}
Restart: pct restart ${CT_ID}
Delete: pct destroy ${CT_ID}
IMPORTANT SETUP STEPS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Configure DNS records (MX, SPF, DKIM, DMARC)
2. Change admin password immediately
3. Configure SSL certificates (auto via Let's Encrypt)
4. Add your mail domains and mailboxes
AUTO-GENERATED BY: proxmox-lxc-mailcow.sh"
if pct set "$CT_ID" -description "$notes" 2>/dev/null; then
success "Container notes added successfully"
else
warn "Failed to add container notes (not critical)"
fi
}
#################################################################
# Information Display Functions
#################################################################
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 "Mailcow 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 " - CPU Cores: $CT_CORES"
echo " - Memory: ${CT_MEMORY}MB"
echo " - Disk Size: ${CT_DISK_SIZE}GB"
echo ""
echo "Mailcow Access:"
echo " - Admin UI: https://${container_ip}"
echo " - SOGo Webmail: https://${container_ip}/SOGo"
echo " - Mail Hostname: ${MAILCOW_HOSTNAME}"
echo ""
warn "Default credentials: admin / moohoo"
warn "CHANGE THE ADMIN PASSWORD IMMEDIATELY!"
echo ""
echo "Docker Commands:"
echo " - Status: pct exec $CT_ID -- docker compose -f /opt/mailcow-dockerized/docker-compose.yml ps"
echo " - Logs: pct exec $CT_ID -- docker compose -f /opt/mailcow-dockerized/docker-compose.yml logs -f"
echo ""
echo "================================================================="
}
#################################################################
# Main Execution
#################################################################
main() {
info "Starting Mailcow LXC container creation..."
echo ""
check_root
check_proxmox
check_container_exists
detect_and_download_template
create_container
start_container
configure_autologin
install_docker
install_mailcow
create_mailcow_service
add_container_notes
display_info
}
main "$@"