fix: split LXC install into host creator + container installer
- install-proxmox.sh: runs on PVE host, creates Debian 12 LXC via pct, then calls lxc-install.sh inside the container (tteck community-scripts pattern) - lxc-install.sh: runs inside the container, fixed to use .env instead of config.yml, correct bcrypt hash, proper CORS_ORIGINS with container IP - README: clarify that install-proxmox.sh runs on the PVE host
This commit is contained in:
@@ -53,13 +53,24 @@ docker compose up -d
|
||||
|
||||
## Proxmox LXC Install
|
||||
|
||||
Run inside a Debian/Ubuntu LXC container:
|
||||
Run this **on the Proxmox host** — it creates a Debian 12 LXC container and installs Homelable inside automatically:
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh)
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/install-proxmox.sh)
|
||||
```
|
||||
|
||||
This installs the backend as a systemd service and serves the frontend via nginx.
|
||||
Default container settings: 2 cores, 1 GB RAM, 8 GB disk, DHCP on `vmbr0`. Override before running:
|
||||
|
||||
```bash
|
||||
CTID=150 RAM=2048 STORAGE=local-zfs bash <(curl -fsSL .../install-proxmox.sh)
|
||||
```
|
||||
|
||||
The backend runs as a systemd service, the frontend is served via nginx on port 80.
|
||||
|
||||
> To install manually inside an existing Debian/Ubuntu machine or LXC:
|
||||
> ```bash
|
||||
> bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh)
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
# Homelable — Proxmox VE LXC creator
|
||||
# Run this on the Proxmox HOST (not inside a container):
|
||||
# bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/install-proxmox.sh)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||
info() { echo -e "${GREEN}[homelable]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[homelable]${NC} $*"; }
|
||||
error() { echo -e "${RED}[homelable]${NC} $*"; exit 1; }
|
||||
step() { echo -e "\n${CYAN}▶ $*${NC}"; }
|
||||
|
||||
# ── Must run on a Proxmox VE host ─────────────────────────────────────────────
|
||||
[[ $EUID -ne 0 ]] && error "Run as root on the Proxmox host"
|
||||
command -v pct &>/dev/null || error "pct not found — run this on a Proxmox VE host, not inside a container"
|
||||
|
||||
# ── Settings (override via env vars) ──────────────────────────────────────────
|
||||
CTID="${CTID:-$(pvesh get /cluster/nextid 2>/dev/null || echo 200)}"
|
||||
HOSTNAME="${HOSTNAME:-homelable}"
|
||||
STORAGE="${STORAGE:-local-lvm}"
|
||||
DISK_SIZE="${DISK_SIZE:-8}" # GB
|
||||
RAM="${RAM:-1024}" # MB
|
||||
CORES="${CORES:-2}"
|
||||
BRIDGE="${BRIDGE:-vmbr0}"
|
||||
RAW="https://raw.githubusercontent.com/Pouzor/homelable/main"
|
||||
|
||||
step "Creating Homelable LXC (CTID=$CTID, hostname=$HOSTNAME)"
|
||||
|
||||
# ── Download Debian 12 template if needed ─────────────────────────────────────
|
||||
TEMPLATE_STORAGE=$(pvesm status --content vztmpl | awk 'NR>1 {print $1; exit}')
|
||||
TEMPLATE=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep "debian-12" | tail -1 | awk '{print $1}')
|
||||
|
||||
if [[ -z "$TEMPLATE" ]]; then
|
||||
info "Downloading Debian 12 LXC template..."
|
||||
pveam update
|
||||
TEMPLATE_NAME=$(pveam available --section system | grep "debian-12" | tail -1 | awk '{print $2}')
|
||||
[[ -z "$TEMPLATE_NAME" ]] && error "Could not find a Debian 12 template"
|
||||
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE_NAME"
|
||||
TEMPLATE="$TEMPLATE_STORAGE:vztmpl/$TEMPLATE_NAME"
|
||||
fi
|
||||
|
||||
info "Using template: $TEMPLATE"
|
||||
|
||||
# ── Create the container ───────────────────────────────────────────────────────
|
||||
pct create "$CTID" "$TEMPLATE" \
|
||||
--hostname "$HOSTNAME" \
|
||||
--storage "$STORAGE" \
|
||||
--rootfs "${STORAGE}:${DISK_SIZE}" \
|
||||
--memory "$RAM" \
|
||||
--cores "$CORES" \
|
||||
--net0 "name=eth0,bridge=${BRIDGE},ip=dhcp" \
|
||||
--ostype debian \
|
||||
--unprivileged 1 \
|
||||
--features "nesting=1" \
|
||||
--start 1
|
||||
|
||||
info "Container $CTID created and started"
|
||||
|
||||
# ── Wait for network ───────────────────────────────────────────────────────────
|
||||
info "Waiting for container network..."
|
||||
for i in $(seq 1 20); do
|
||||
if pct exec "$CTID" -- ping -c1 -W1 8.8.8.8 &>/dev/null; then
|
||||
break
|
||||
fi
|
||||
[[ $i -eq 20 ]] && error "Container has no network after 20s — check bridge $BRIDGE"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# ── Grant NET_RAW for nmap (ping-based checks) ─────────────────────────────────
|
||||
# lxc.cap.keep must include net_raw for nmap SYN scan in unprivileged containers
|
||||
pct set "$CTID" --mp0 "" 2>/dev/null || true
|
||||
echo "lxc.cap.keep = net_raw net_bind_service" >> /etc/pve/lxc/${CTID}.conf 2>/dev/null || true
|
||||
|
||||
# ── Run the installer inside the container ────────────────────────────────────
|
||||
step "Running Homelable installer inside container $CTID..."
|
||||
pct exec "$CTID" -- bash -c "curl -fsSL ${RAW}/scripts/lxc-install.sh | bash"
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||
IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "<container-ip>")
|
||||
echo ""
|
||||
echo -e " ${GREEN}✓ Homelable installed in LXC $CTID${NC}"
|
||||
echo -e " ${GREEN}✓ Open http://${IP}${NC}"
|
||||
echo -e " Default login: ${YELLOW}admin / admin${NC}"
|
||||
echo -e " ${YELLOW}⚠ Change the password: edit /opt/homelable/backend/.env (AUTH_PASSWORD_HASH)${NC}"
|
||||
echo ""
|
||||
+40
-51
@@ -1,13 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# Homelable — LXC/VM bootstrap installer
|
||||
# Compatible with Proxmox VE (Debian/Ubuntu LXC containers)
|
||||
# Usage: bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh)
|
||||
# Homelable — in-container installer
|
||||
# Runs INSIDE a Debian/Ubuntu LXC container (called automatically by install-proxmox.sh)
|
||||
# Can also be run manually inside any Debian/Ubuntu machine:
|
||||
# bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR=/opt/homelable
|
||||
DATA_DIR=/opt/homelable/data
|
||||
SERVICE_USER=homelable
|
||||
REPO_URL="https://github.com/Pouzor/homelable.git"
|
||||
RAW="https://raw.githubusercontent.com/Pouzor/homelable/main"
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
info() { echo -e "${GREEN}[homelable]${NC} $*"; }
|
||||
@@ -16,45 +19,36 @@ error() { echo -e "${RED}[homelable]${NC} $*"; exit 1; }
|
||||
|
||||
[[ $EUID -ne 0 ]] && error "Run as root (sudo bash ...)"
|
||||
|
||||
# ── Detect OS ────────────────────────────────────────────────────────────────
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
OS=$ID
|
||||
else
|
||||
error "Cannot detect OS"
|
||||
fi
|
||||
|
||||
# ── Detect OS ─────────────────────────────────────────────────────────────────
|
||||
[[ -f /etc/os-release ]] && . /etc/os-release || error "Cannot detect OS"
|
||||
info "Detected: $PRETTY_NAME"
|
||||
[[ "$OS" =~ ^(debian|ubuntu)$ ]] || error "Requires Debian or Ubuntu"
|
||||
[[ "$ID" =~ ^(debian|ubuntu)$ ]] || error "Requires Debian or Ubuntu"
|
||||
|
||||
# ── System deps ──────────────────────────────────────────────────────────────
|
||||
# ── System deps ───────────────────────────────────────────────────────────────
|
||||
info "Installing system dependencies..."
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq \
|
||||
python3 python3-pip python3-venv \
|
||||
nmap curl git nginx
|
||||
apt-get install -y -qq python3 python3-pip python3-venv nmap curl git nginx
|
||||
|
||||
# ── Node.js (for frontend build) ─────────────────────────────────────────────
|
||||
# ── Node.js 20 ────────────────────────────────────────────────────────────────
|
||||
if ! command -v node &>/dev/null; then
|
||||
info "Installing Node.js 20..."
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||||
apt-get install -y -qq nodejs
|
||||
fi
|
||||
|
||||
# ── Service user ─────────────────────────────────────────────────────────────
|
||||
# ── Service user ──────────────────────────────────────────────────────────────
|
||||
if ! id "$SERVICE_USER" &>/dev/null; then
|
||||
useradd --system --shell /sbin/nologin "$SERVICE_USER"
|
||||
info "Created user: $SERVICE_USER"
|
||||
info "Created service user: $SERVICE_USER"
|
||||
fi
|
||||
|
||||
# ── Clone / update repo ───────────────────────────────────────────────────────
|
||||
REPO_URL="https://github.com/Pouzor/homelable.git"
|
||||
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
||||
info "Updating existing installation..."
|
||||
git -C "$INSTALL_DIR" pull
|
||||
git -C "$INSTALL_DIR" pull --quiet
|
||||
else
|
||||
info "Cloning repository..."
|
||||
git clone "$REPO_URL" "$INSTALL_DIR"
|
||||
git clone --quiet "$REPO_URL" "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
mkdir -p "$DATA_DIR"
|
||||
@@ -65,38 +59,31 @@ cd "$INSTALL_DIR/backend"
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install --quiet -r requirements.txt
|
||||
|
||||
# Generate config.yml if missing
|
||||
if [[ ! -f config.yml ]]; then
|
||||
cp config.yml.example config.yml 2>/dev/null || cat > config.yml <<'CONF'
|
||||
auth:
|
||||
username: admin
|
||||
password_hash: "$2b$12$o/LWyvmBc978CNpSsHxcveXN0WqjAGW/gBR0.U.HURWbaYD3GCDqS"
|
||||
|
||||
scanner:
|
||||
ranges:
|
||||
- "192.168.1.0/24"
|
||||
|
||||
status_checker:
|
||||
interval_seconds: 60
|
||||
CONF
|
||||
warn "Created default config.yml — change admin password!"
|
||||
fi
|
||||
|
||||
# Generate .env if missing
|
||||
if [[ ! -f .env ]]; then
|
||||
SECRET=$(python3 -c "import secrets; print(secrets.token_hex(32))")
|
||||
SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(32))")
|
||||
# Default hash = bcrypt of "admin" (same as .env.example)
|
||||
cat > .env <<EOF
|
||||
SECRET_KEY=$SECRET
|
||||
SQLITE_PATH=$DATA_DIR/homelab.db
|
||||
CONFIG_PATH=$INSTALL_DIR/backend/config.yml
|
||||
CORS_ORIGINS=http://localhost
|
||||
CORS_ORIGINS=["http://localhost","http://$(hostname -I | awk '{print $1}')"]
|
||||
|
||||
# Auth — default credentials: admin / admin
|
||||
# Change AUTH_PASSWORD_HASH before exposing on a network.
|
||||
# Generate: python3 -c "from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('yourpassword'))"
|
||||
AUTH_USERNAME=admin
|
||||
AUTH_PASSWORD_HASH='\$2b\$12\$RtMbyw17l4N5UGzeXMNAWuzCaVV.XFBY7ZetWheQhxcBDcxahapkG'
|
||||
|
||||
SCANNER_RANGES=["192.168.1.0/24"]
|
||||
STATUS_CHECKER_INTERVAL=60
|
||||
EOF
|
||||
warn "Created .env with default admin/admin — change AUTH_PASSWORD_HASH before exposing on a network!"
|
||||
fi
|
||||
|
||||
chown -R "$SERVICE_USER":"$SERVICE_USER" "$DATA_DIR"
|
||||
chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR/backend/.venv"
|
||||
|
||||
# ── systemd: backend ─────────────────────────────────────────────────────────
|
||||
# ── systemd: backend ──────────────────────────────────────────────────────────
|
||||
cat > /etc/systemd/system/homelable-backend.service <<EOF
|
||||
[Unit]
|
||||
Description=Homelable Backend
|
||||
@@ -119,17 +106,19 @@ EOF
|
||||
info "Building frontend..."
|
||||
cd "$INSTALL_DIR/frontend"
|
||||
npm ci --silent
|
||||
VITE_API_BASE=/api npm run build
|
||||
npm run build
|
||||
|
||||
# ── nginx ─────────────────────────────────────────────────────────────────────
|
||||
cp "$INSTALL_DIR/docker/nginx.conf" /etc/nginx/sites-available/homelable
|
||||
# Adjust for local backend (not docker network)
|
||||
sed -i 's/http:\/\/backend:8000/http:\/\/127.0.0.1:8000/g' /etc/nginx/sites-available/homelable
|
||||
# Adjust root to dist
|
||||
sed -i "s|/usr/share/nginx/html|$INSTALL_DIR/frontend/dist|g" /etc/nginx/sites-available/homelable
|
||||
info "Configuring nginx..."
|
||||
# Use the project nginx config, adjusted for local backend
|
||||
sed \
|
||||
-e 's|http://backend:8000|http://127.0.0.1:8000|g' \
|
||||
-e "s|/usr/share/nginx/html|$INSTALL_DIR/frontend/dist|g" \
|
||||
"$INSTALL_DIR/docker/nginx.conf" > /etc/nginx/sites-available/homelable
|
||||
|
||||
ln -sf /etc/nginx/sites-available/homelable /etc/nginx/sites-enabled/homelable
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t && systemctl reload nginx
|
||||
nginx -t && systemctl reload nginx || systemctl start nginx
|
||||
|
||||
# ── Enable & start ────────────────────────────────────────────────────────────
|
||||
systemctl daemon-reload
|
||||
@@ -140,5 +129,5 @@ info "Done!"
|
||||
echo ""
|
||||
echo -e " ${GREEN}Homelable is running at http://$(hostname -I | awk '{print $1}')${NC}"
|
||||
echo -e " Default login: admin / admin"
|
||||
echo -e " ${YELLOW}Change the password in $INSTALL_DIR/backend/config.yml${NC}"
|
||||
echo -e " ${YELLOW}⚠ Change the password: edit $INSTALL_DIR/backend/.env (AUTH_PASSWORD_HASH)${NC}"
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user