feat: rootless podman, session hardening, boot stability, sidebar fix

Rootless podman migration (TASK-11):
- Remove sudo from all podman calls in PodmanClient + 8 backend files
- Remove sudo from all podman/docker calls in deploy script
- Restore full systemd security hardening: NoNewPrivileges,
  RestrictAddressFamilies, MemoryDenyWriteExecute, RestrictRealtime,
  RestrictNamespaces, RestrictSUIDSGID, SystemCallFilter, ProtectSystem=strict
- Enable loginctl linger for rootless container persistence
- Remove Ollama from auto-deploy (marketplace-only)

Session & auth hardening:
- Increase MAX_CONCURRENT_SESSIONS 20→50 (prevents eviction storms)
- Debounced 401 redirect in rpc-client.ts (prevents redirect storms)

Boot stability:
- optimize-debian.sh: adds chrony, swap, removes policy-rc.d
- deploy script: pre-restart chrony + swap setup
- ISO build: chrony package, swap file creation
- BootScreen: no longer clears localStorage (prevents splash replay)
- RootRedirect: sole owner of localStorage clearing on server ready

UI fixes:
- Sidebar opacity default changed from 0→visible (fixes missing sidebar
  after page-persistence login without entrance animation)
- Console.log/error wrapped in import.meta.env.DEV guards
- Remove unused route import from RootRedirect

Beta tracking:
- CLAUDE.md: beta freeze protocol added
- MASTER_PLAN.md: TASK-11, TASK-17, phase structure
- BETA-PROGRESS.md: initial tracking doc
- Tagged v1.2.0-alpha.1 as pre-rootless baseline

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-18 13:53:27 +00:00
parent 934d120243
commit 870ff095d8
48 changed files with 2979 additions and 2196 deletions

View File

@@ -340,13 +340,13 @@ if [ "$BOTH" = true ]; then
fi
# FileBrowser fix
DOCKER=podman; command -v podman >/dev/null 2>&1 || DOCKER=docker
FB=$(sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -x filebrowser || true)
FB=$($DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -x filebrowser || true)
if [ -n "$FB" ]; then
RO=$(sudo $DOCKER inspect filebrowser 2>/dev/null | grep -oP "\"ReadonlyRootfs\":\s*\K\w+" || echo "false")
RO=$($DOCKER inspect filebrowser 2>/dev/null | grep -oP "\"ReadonlyRootfs\":\s*\K\w+" || echo "false")
if [ "$RO" = "true" ]; then
sudo $DOCKER stop filebrowser 2>/dev/null; sudo $DOCKER rm filebrowser 2>/dev/null
$DOCKER stop filebrowser 2>/dev/null; $DOCKER rm filebrowser 2>/dev/null
sudo mkdir -p /var/lib/archipelago/filebrowser
sudo $DOCKER run -d --name filebrowser --restart=always -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv docker.io/filebrowser/filebrowser:v2.27.0 2>/dev/null
$DOCKER run -d --name filebrowser --restart=always -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv docker.io/filebrowser/filebrowser:v2.27.0 2>/dev/null
fi
fi
' 2>/dev/null || true
@@ -677,15 +677,15 @@ PYEOF
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
FB_EXISTS=$(sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -x filebrowser || true)
FB_EXISTS=$($DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -x filebrowser || true)
if [ -n "$FB_EXISTS" ]; then
RO=$(sudo $DOCKER inspect filebrowser 2>/dev/null | grep -oP "\"ReadonlyRootfs\":\s*\K\w+" || echo "false")
RO=$($DOCKER inspect filebrowser 2>/dev/null | grep -oP "\"ReadonlyRootfs\":\s*\K\w+" || echo "false")
if [ "$RO" = "true" ]; then
echo " FileBrowser has read-only root — recreating..."
sudo $DOCKER stop filebrowser 2>/dev/null
sudo $DOCKER rm filebrowser 2>/dev/null
$DOCKER stop filebrowser 2>/dev/null
$DOCKER rm filebrowser 2>/dev/null
sudo mkdir -p /var/lib/archipelago/filebrowser
sudo $DOCKER run -d --name filebrowser --restart=always -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv docker.io/filebrowser/filebrowser:v2.27.0 2>&1 | tail -1
$DOCKER run -d --name filebrowser --restart=always -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv docker.io/filebrowser/filebrowser:v2.27.0 2>&1 | tail -1
echo " FileBrowser recreated"
else
echo " FileBrowser OK"
@@ -693,7 +693,7 @@ PYEOF
else
echo " Creating FileBrowser..."
sudo mkdir -p /var/lib/archipelago/filebrowser
sudo $DOCKER run -d --name filebrowser --restart=always -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv docker.io/filebrowser/filebrowser:v2.27.0 2>&1 | tail -1
$DOCKER run -d --name filebrowser --restart=always -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv docker.io/filebrowser/filebrowser:v2.27.0 2>&1 | tail -1
echo " FileBrowser created"
fi
' 2>/dev/null || true
@@ -713,6 +713,30 @@ PYEOF
}
MANIFEST_EOF
# Ensure NTP and swap are configured (prevents OOM kills and clock drift)
progress "Ensuring NTP + swap"
ssh $SSH_OPTS "$TARGET_HOST" '
# NTP via chrony
if ! dpkg -l chrony >/dev/null 2>&1; then
sudo rm -f /usr/sbin/policy-rc.d
sudo apt-get update -qq && sudo apt-get install -y chrony 2>/dev/null
fi
sudo systemctl enable chrony 2>/dev/null
sudo systemctl start chrony 2>/dev/null
sudo timedatectl set-ntp true 2>/dev/null
# Swap
if [ ! -f /swapfile ]; then
TOTAL_KB=$(grep MemTotal /proc/meminfo | awk "{print \$2}")
SZ=$((TOTAL_KB / 1024 / 1024))
[ "$SZ" -gt 8 ] && SZ=8
[ "$SZ" -lt 2 ] && SZ=2
sudo fallocate -l ${SZ}G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
grep -q "/swapfile" /etc/fstab || echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab
echo " Created ${SZ}G swap"
fi
sudo swapon /swapfile 2>/dev/null || true
' 2>&1 | tail -5 | sed 's/^/ /' || true
# Restart services
progress "Restarting services"
ssh $SSH_OPTS "$TARGET_HOST" "sudo systemctl start archipelago && sudo systemctl restart nginx"
@@ -726,44 +750,44 @@ MANIFEST_EOF
else
# Rebuild and recreate LND UI container (port 8081 so Launch from UI and http://host:8081 both work)
progress "Rebuilding LND UI"
if ssh $SSH_OPTS "$TARGET_HOST" "cd $TARGET_DIR/docker/lnd-ui && (command -v podman >/dev/null 2>&1 && sudo podman build --no-cache -t lnd-ui:latest . || sudo docker build --no-cache -t lnd-ui:latest .)" 2>&1 | tail -12 | sed 's/^/ /'; then
if ssh $SSH_OPTS "$TARGET_HOST" "cd $TARGET_DIR/docker/lnd-ui && (command -v podman >/dev/null 2>&1 && podman build --no-cache -t lnd-ui:latest . || docker build --no-cache -t lnd-ui:latest .)" 2>&1 | tail -12 | sed 's/^/ /'; then
echo " Recreating LND UI container (port 8081)..."
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
for c in $(sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -i lnd-ui); do
[ -n "$c" ] && sudo $DOCKER stop "$c" 2>/dev/null; sudo $DOCKER rm -f "$c" 2>/dev/null
for c in $($DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -i lnd-ui); do
[ -n "$c" ] && $DOCKER stop "$c" 2>/dev/null; $DOCKER rm -f "$c" 2>/dev/null
done
sudo $DOCKER run -d --name archy-lnd-ui -p 8081:80 --restart unless-stopped lnd-ui:latest
$DOCKER run -d --name archy-lnd-ui -p 8081:80 --restart unless-stopped lnd-ui:latest
' 2>&1 | sed 's/^/ /' || true
fi
# Rebuild and recreate ElectrumX UI container (port 50002)
progress "Rebuilding ElectrumX UI"
if ssh $SSH_OPTS "$TARGET_HOST" "cd $TARGET_DIR/docker/electrs-ui && (command -v podman >/dev/null 2>&1 && sudo podman build --no-cache -t electrs-ui:latest . || sudo docker build --no-cache -t electrs-ui:latest .)" 2>&1 | tail -12 | sed 's/^/ /'; then
if ssh $SSH_OPTS "$TARGET_HOST" "cd $TARGET_DIR/docker/electrs-ui && (command -v podman >/dev/null 2>&1 && podman build --no-cache -t electrs-ui:latest . || docker build --no-cache -t electrs-ui:latest .)" 2>&1 | tail -12 | sed 's/^/ /'; then
echo " Recreating ElectrumX UI container (port 50002, host network)..."
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
for c in $(sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -i electrs-ui); do
[ -n "$c" ] && sudo $DOCKER stop "$c" 2>/dev/null; sudo $DOCKER rm -f "$c" 2>/dev/null
for c in $($DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -i electrs-ui); do
[ -n "$c" ] && $DOCKER stop "$c" 2>/dev/null; $DOCKER rm -f "$c" 2>/dev/null
done
sudo $DOCKER run -d --name archy-electrs-ui --network host --restart unless-stopped electrs-ui:latest
$DOCKER run -d --name archy-electrs-ui --network host --restart unless-stopped electrs-ui:latest
' 2>&1 | sed 's/^/ /' || true
fi
# Rebuild and recreate Bitcoin UI container (host network, port 8334 in nginx.conf)
# Host network required: bitcoin-ui proxies Bitcoin RPC at 127.0.0.1:8332
progress "Rebuilding Bitcoin UI"
if ssh $SSH_OPTS "$TARGET_HOST" "cd $TARGET_DIR/docker/bitcoin-ui && (command -v podman >/dev/null 2>&1 && sudo podman build --no-cache -t bitcoin-ui:latest . || sudo docker build --no-cache -t bitcoin-ui:latest .)" 2>&1 | tail -12 | sed 's/^/ /'; then
if ssh $SSH_OPTS "$TARGET_HOST" "cd $TARGET_DIR/docker/bitcoin-ui && (command -v podman >/dev/null 2>&1 && podman build --no-cache -t bitcoin-ui:latest . || docker build --no-cache -t bitcoin-ui:latest .)" 2>&1 | tail -12 | sed 's/^/ /'; then
echo " Recreating Bitcoin UI container (port 8334, host network)..."
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
for c in $(sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -i bitcoin-ui); do
[ -n "$c" ] && sudo $DOCKER stop "$c" 2>/dev/null; sudo $DOCKER rm -f "$c" 2>/dev/null
for c in $($DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -i bitcoin-ui); do
[ -n "$c" ] && $DOCKER stop "$c" 2>/dev/null; $DOCKER rm -f "$c" 2>/dev/null
done
sudo $DOCKER run -d --name archy-bitcoin-ui --network host --restart unless-stopped bitcoin-ui:latest
$DOCKER run -d --name archy-bitcoin-ui --network host --restart unless-stopped bitcoin-ui:latest
' 2>&1 | sed 's/^/ /' || true
fi
@@ -825,9 +849,9 @@ MANIFEST_EOF
ssh $SSH_OPTS "$TARGET_HOST" "
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
sudo \$DOCKER network create archy-net 2>/dev/null || true
\$DOCKER network create archy-net 2>/dev/null || true
NET_OPT='--network archy-net'
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qE 'bitcoin-knots|archy-bitcoin-knots'; then
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qE 'bitcoin-knots|archy-bitcoin-knots'; then
echo ' Creating Bitcoin Knots (mainnet, archipelago RPC)...'
sudo mkdir -p /var/lib/archipelago/bitcoin
# Demo mode: prune=550 saves ~194GB disk, but disables txindex (incompatible with electrumx)
@@ -838,7 +862,7 @@ MANIFEST_EOF
BTC_EXTRA_ARGS="-txindex=1"
BTC_DBCACHE=4096
fi
sudo \$DOCKER run -d --name bitcoin-knots --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name bitcoin-knots --restart unless-stopped \$NET_OPT \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8332:8332 -p 8333:8333 \
@@ -850,7 +874,7 @@ MANIFEST_EOF
-dbcache=\$BTC_DBCACHE
echo ' Bitcoin Knots started (sync may take hours)'
else
sudo \$DOCKER network connect archy-net bitcoin-knots 2>/dev/null || true
\$DOCKER network connect archy-net bitcoin-knots 2>/dev/null || true
fi
" 2>&1 | sed 's/^/ /' || true
@@ -864,14 +888,14 @@ MANIFEST_EOF
# Clean any duplicate/old mempool containers (user may have two versions)
# EXCLUDE electrumx/mempool-electrs - indexing takes days, do not recreate on every deploy
for c in mempool mempool-api mempool-web archy-mempool-api archy-mempool-web; do
sudo \$DOCKER stop \$c 2>/dev/null
sudo \$DOCKER rm -f \$c 2>/dev/null
\$DOCKER stop \$c 2>/dev/null
\$DOCKER rm -f \$c 2>/dev/null
done
# Create mysql-mempool if missing
if ! sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'mysql-mempool|archy-mempool-db'; then
if ! \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'mysql-mempool|archy-mempool-db'; then
echo ' Creating mysql-mempool...'
sudo mkdir -p /var/lib/archipelago/mysql-mempool
sudo \$DOCKER run -d --name archy-mempool-db --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name archy-mempool-db --restart unless-stopped \$NET_OPT \
-v /var/lib/archipelago/mysql-mempool:/var/lib/mysql \
-e MYSQL_DATABASE=mempool \
-e MYSQL_USER=mempool \
@@ -880,25 +904,25 @@ MANIFEST_EOF
docker.io/mariadb:10.11
sleep 3
fi
MYSQL_CNT=\$(sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'mysql-mempool|archy-mempool-db' | head -1)
MYSQL_CNT=\$(\$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'mysql-mempool|archy-mempool-db' | head -1)
MYSQL_CNT=\${MYSQL_CNT:-archy-mempool-db}
# Ensure DB is on archy-net so mempool-api can resolve it
sudo \$DOCKER network connect archy-net \$MYSQL_CNT 2>/dev/null || true
\$DOCKER network connect archy-net \$MYSQL_CNT 2>/dev/null || true
# Stop and remove old mempool-electrs if present (replaced by electrumx)
if sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q mempool-electrs; then
if \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q mempool-electrs; then
echo ' Removing old mempool-electrs (replaced by ElectrumX)...'
sudo \$DOCKER stop mempool-electrs 2>/dev/null
sudo \$DOCKER rm -f mempool-electrs 2>/dev/null
\$DOCKER stop mempool-electrs 2>/dev/null
\$DOCKER rm -f mempool-electrs 2>/dev/null
fi
# Create electrumx ONLY if missing - do NOT recreate (indexing takes days)
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q electrumx; then
if sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q electrumx; then
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q electrumx; then
if \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q electrumx; then
echo ' Starting existing electrumx (preserving index)...'
sudo \$DOCKER start electrumx 2>/dev/null || true
\$DOCKER start electrumx 2>/dev/null || true
else
echo ' Creating electrumx (indexer - may take days to sync, do not recreate)...'
sudo mkdir -p /var/lib/archipelago/electrumx
sudo \$DOCKER run -d --name electrumx --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name electrumx --restart unless-stopped \$NET_OPT \
-p 50001:50001 \
-v /var/lib/archipelago/electrumx:/data \
-e DAEMON_URL=http://$BITCOIN_RPC_USER:$BITCOIN_RPC_PASS@bitcoin-knots:8332/ \
@@ -909,15 +933,15 @@ MANIFEST_EOF
fi
fi
# Create/recreate mempool-api (backend on 8999) - required for mempool to work
for c in \$(sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'mempool-api|archy-mempool-api'); do
for c in \$(\$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'mempool-api|archy-mempool-api'); do
echo ' Recreating mempool-api (backend)...'
sudo \$DOCKER stop \"\$c\" 2>/dev/null
sudo \$DOCKER rm -f \"\$c\" 2>/dev/null
\$DOCKER stop \"\$c\" 2>/dev/null
\$DOCKER rm -f \"\$c\" 2>/dev/null
done
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q mempool-api; then
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q mempool-api; then
echo ' Creating mempool-api (backend)...'
sudo mkdir -p /var/lib/archipelago/mempool
sudo \$DOCKER run -d --name mempool-api --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name mempool-api --restart unless-stopped \$NET_OPT \
-p 8999:8999 \
-v /var/lib/archipelago/mempool:/data \
-e MEMPOOL_BACKEND=electrum \
@@ -936,15 +960,15 @@ MANIFEST_EOF
docker.io/mempool/backend:v2.5.0
fi
# Recreate mempool frontend - handle both 'mempool' and 'mempool-web' (frontend was on wrong port 8999)
for c in \$(sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E '^mempool\$|mempool-web|archy-mempool-web'); do
for c in \$(\$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E '^mempool\$|mempool-web|archy-mempool-web'); do
echo ' Recreating mempool frontend on 4080...'
sudo \$DOCKER stop \"\$c\" 2>/dev/null
sudo \$DOCKER rm -f \"\$c\" 2>/dev/null
\$DOCKER stop \"\$c\" 2>/dev/null
\$DOCKER rm -f \"\$c\" 2>/dev/null
break
done
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q archy-mempool-web; then
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q archy-mempool-web; then
echo ' Creating mempool frontend on 4080...'
sudo \$DOCKER run -d --name archy-mempool-web --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name archy-mempool-web --restart unless-stopped \$NET_OPT \
-p 4080:8080 \
-e FRONTEND_HTTP_PORT=8080 \
-e BACKEND_MAINNET_HTTP_HOST=mempool-api \
@@ -959,15 +983,15 @@ MANIFEST_EOF
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
TARGET_IP='$TARGET_IP'
sudo \$DOCKER network create archy-net 2>/dev/null || true
\$DOCKER network create archy-net 2>/dev/null || true
NET_OPT='--network archy-net'
# Ensure bitcoin-knots is on archy-net for NBXplorer/BTCPay to reach it
sudo \$DOCKER network connect archy-net bitcoin-knots 2>/dev/null || true
\$DOCKER network connect archy-net bitcoin-knots 2>/dev/null || true
# Create PostgreSQL for BTCPay if missing
if ! sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-btcpay-db|postgres-btcpay'; then
if ! \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-btcpay-db|postgres-btcpay'; then
echo ' Creating archy-btcpay-db (PostgreSQL)...'
sudo mkdir -p /var/lib/archipelago/postgres-btcpay
sudo \$DOCKER run -d --name archy-btcpay-db --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name archy-btcpay-db --restart unless-stopped \$NET_OPT \
-v /var/lib/archipelago/postgres-btcpay:/var/lib/postgresql/data \
-e POSTGRES_DB=btcpay \
-e POSTGRES_USER=btcpay \
@@ -976,16 +1000,16 @@ MANIFEST_EOF
sleep 3
fi
# Create NBXplorer database in PostgreSQL (NBXplorer needs its own DB)
sudo \$DOCKER exec archy-btcpay-db psql -U postgres -tc \"SELECT 1 FROM pg_database WHERE datname='nbxplorer'\" 2>/dev/null | grep -q 1 || \
sudo \$DOCKER exec -e PGPASSWORD=$BTCPAY_DB_PASS archy-btcpay-db psql -U postgres -c \"CREATE DATABASE nbxplorer;\" 2>/dev/null || true
\$DOCKER exec archy-btcpay-db psql -U postgres -tc \"SELECT 1 FROM pg_database WHERE datname='nbxplorer'\" 2>/dev/null | grep -q 1 || \
\$DOCKER exec -e PGPASSWORD=$BTCPAY_DB_PASS archy-btcpay-db psql -U postgres -c \"CREATE DATABASE nbxplorer;\" 2>/dev/null || true
# Create NBXplorer (required by BTCPay - indexes blocks for payment tracking)
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q archy-nbxplorer; then
if sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q archy-nbxplorer; then
sudo \$DOCKER start archy-nbxplorer 2>/dev/null || true
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q archy-nbxplorer; then
if \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q archy-nbxplorer; then
\$DOCKER start archy-nbxplorer 2>/dev/null || true
else
echo ' Creating archy-nbxplorer...'
sudo mkdir -p /var/lib/archipelago/nbxplorer
sudo \$DOCKER run -d --name archy-nbxplorer --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name archy-nbxplorer --restart unless-stopped \$NET_OPT \
-p 32838:32838 \
-v /var/lib/archipelago/nbxplorer:/data \
-e NBXPLORER_DATADIR=/data \
@@ -1002,16 +1026,16 @@ MANIFEST_EOF
fi
# Recreate btcpay-server with PostgreSQL, NBXplorer URL, and Bitcoin RPC
for c in btcpay-server archy-btcpay; do
if sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qx \"\$c\"; then
if \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qx \"\$c\"; then
echo ' Recreating btcpay-server with NBXplorer...'
sudo \$DOCKER stop \"\$c\" 2>/dev/null
sudo \$DOCKER rm -f \"\$c\" 2>/dev/null
\$DOCKER stop \"\$c\" 2>/dev/null
\$DOCKER rm -f \"\$c\" 2>/dev/null
fi
done
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q btcpay-server; then
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q btcpay-server; then
echo ' Creating btcpay-server on 23000...'
sudo mkdir -p /var/lib/archipelago/btcpay
sudo \$DOCKER run -d --name btcpay-server --restart unless-stopped \$NET_OPT \
\$DOCKER run -d --name btcpay-server --restart unless-stopped \$NET_OPT \
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 23000:49392 \
@@ -1035,30 +1059,30 @@ MANIFEST_EOF
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
# Remove old single-container 'immich' if present (wrong port mapping, conflicts with immich_server)
if sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qx immich; then
if \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qx immich; then
echo ' Removing old immich container (use immich_server)...'
sudo \$DOCKER stop immich 2>/dev/null
sudo \$DOCKER rm -f immich 2>/dev/null
sudo \$DOCKER start immich_server 2>/dev/null || true
\$DOCKER stop immich 2>/dev/null
\$DOCKER rm -f immich 2>/dev/null
\$DOCKER start immich_server 2>/dev/null || true
fi
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q immich_server; then
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q immich_server; then
echo ' Creating Immich stack...'
sudo mkdir -p /var/lib/archipelago/immich /var/lib/archipelago/immich-db
sudo \$DOCKER network create immich-net 2>/dev/null || true
if ! sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q immich_postgres; then
sudo \$DOCKER run -d --name immich_postgres --restart unless-stopped --network immich-net \
\$DOCKER network create immich-net 2>/dev/null || true
if ! \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q immich_postgres; then
\$DOCKER run -d --name immich_postgres --restart unless-stopped --network immich-net \
-v /var/lib/archipelago/immich-db:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=$IMMICH_DB_PASS -e POSTGRES_USER=postgres -e POSTGRES_DB=immich \
ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 2>/dev/null || true
sleep 5
fi
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q immich_redis; then
sudo \$DOCKER run -d --name immich_redis --restart unless-stopped --network immich-net \
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q immich_redis; then
\$DOCKER run -d --name immich_redis --restart unless-stopped --network immich-net \
docker.io/valkey/valkey:7-alpine 2>/dev/null || true
sleep 2
fi
if ! sudo \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q immich_server; then
sudo \$DOCKER run -d --name immich_server --restart unless-stopped --network immich-net \
if ! \$DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q immich_server; then
\$DOCKER run -d --name immich_server --restart unless-stopped --network immich-net \
-p 2283:2283 -v /var/lib/archipelago/immich:/usr/src/app/upload \
-e DB_HOSTNAME=immich_postgres -e DB_USERNAME=postgres -e DB_PASSWORD=$IMMICH_DB_PASS \
-e DB_DATABASE_NAME=immich -e REDIS_HOSTNAME=immich_redis \
@@ -1125,9 +1149,9 @@ print("torrc generated with %d services" % (len(lines) // 3))
'
# Remove any old Tor container (system Tor is preferred)
for c in \$(sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'archy-tor|^tor\$'); do
sudo \$DOCKER stop \"\$c\" 2>/dev/null
sudo \$DOCKER rm -f \"\$c\" 2>/dev/null
for c in \$(\$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'archy-tor|^tor\$'); do
\$DOCKER stop \"\$c\" 2>/dev/null
\$DOCKER rm -f \"\$c\" 2>/dev/null
done
# Use system Tor (preferred — no AppArmor issues with default paths)
@@ -1174,11 +1198,11 @@ print("torrc generated with %d services" % (len(lines) // 3))
($TIMEOUT_CMD ssh $SSH_OPTS "$TARGET_HOST" "
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
for c in \$(sudo \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E '^fedimint\$'); do
for c in \$(\$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E '^fedimint\$'); do
echo ' Recreating fedimint with FM_API_URL...'
sudo \$DOCKER stop \"\$c\" 2>/dev/null
sudo \$DOCKER rm -f \"\$c\" 2>/dev/null
sudo \$DOCKER run -d --name fedimint --restart unless-stopped \
\$DOCKER stop \"\$c\" 2>/dev/null
\$DOCKER rm -f \"\$c\" 2>/dev/null
\$DOCKER run -d --name fedimint --restart unless-stopped \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8173:8173 -p 8174:8174 -p 8175:8175 \
@@ -1199,15 +1223,15 @@ print("torrc generated with %d services" % (len(lines) // 3))
# Ensure Fedimint Gateway companion container
# Auto-detect LND: if running with credentials, use lnd mode; otherwise use ldk (built-in)
sudo \$DOCKER rm -f fedimint-gateway 2>/dev/null || true
\$DOCKER rm -f fedimint-gateway 2>/dev/null || true
echo ' Creating fedimint-gateway...'
sudo mkdir -p /var/lib/archipelago/fedimint-gateway
LND_CERT=/var/lib/archipelago/lnd/tls.cert
LND_MACAROON=/var/lib/archipelago/lnd/data/chain/bitcoin/mainnet/admin.macaroon
GW_COMMON=\"-p 8176:8176 -v /var/lib/archipelago/fedimint-gateway:/data docker.io/fedimint/gatewayd:v0.10.0 gatewayd --data-dir /data --listen 0.0.0.0:8176 --bcrypt-password-hash '$FEDI_HASH' --network bitcoin --bitcoind-url http://$TARGET_IP:8332 --bitcoind-username $BITCOIN_RPC_USER --bitcoind-password $BITCOIN_RPC_PASS\"
if sudo \$DOCKER ps --format '{{.Names}}' | grep -q '^lnd\$' && sudo test -f \$LND_CERT && sudo test -f \$LND_MACAROON; then
if \$DOCKER ps --format '{{.Names}}' | grep -q '^lnd\$' && sudo test -f \$LND_CERT && sudo test -f \$LND_MACAROON; then
echo ' LND detected — using lnd mode'
sudo \$DOCKER run -d --name fedimint-gateway --restart unless-stopped \
\$DOCKER run -d --name fedimint-gateway --restart unless-stopped \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8176:8176 \
@@ -1222,7 +1246,7 @@ print("torrc generated with %d services" % (len(lines) // 3))
lnd --lnd-rpc-host $TARGET_IP:10009 --lnd-tls-cert /lnd/tls.cert --lnd-macaroon /lnd/admin.macaroon
else
echo ' No LND found — using ldk (built-in Lightning)'
sudo \$DOCKER run -d --name fedimint-gateway --restart unless-stopped \
\$DOCKER run -d --name fedimint-gateway --restart unless-stopped \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8176:8176 -p 9737:9737 \
@@ -1242,9 +1266,9 @@ print("torrc generated with %d services" % (len(lines) // 3))
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx lnd; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx lnd; then
sudo $DOCKER start lnd 2>/dev/null || true
if ! $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx lnd; then
if $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx lnd; then
$DOCKER start lnd 2>/dev/null || true
echo " LND started (existing)"
else
echo " Creating LND..."
@@ -1275,7 +1299,7 @@ autopilot.active=false
LNDCONF
sudo cp /tmp/lnd.conf /var/lib/archipelago/lnd/lnd.conf
fi
sudo $DOCKER run -d --name lnd --restart unless-stopped --network archy-net \
$DOCKER run -d --name lnd --restart unless-stopped --network archy-net \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 9735:9735 -p 10009:10009 -p 8080:8080 \
@@ -1293,13 +1317,13 @@ LNDCONF
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx homeassistant; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx homeassistant; then
sudo $DOCKER start homeassistant 2>/dev/null || true
if ! $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx homeassistant; then
if $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx homeassistant; then
$DOCKER start homeassistant 2>/dev/null || true
else
echo " Creating Home Assistant..."
sudo mkdir -p /var/lib/archipelago/home-assistant
sudo $DOCKER run -d --name homeassistant --restart unless-stopped \
$DOCKER run -d --name homeassistant --restart unless-stopped \
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8123:8123 -v /var/lib/archipelago/home-assistant:/config \
@@ -1316,14 +1340,14 @@ LNDCONF
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx grafana; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx grafana; then
sudo $DOCKER start grafana 2>/dev/null || true
if ! $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx grafana; then
if $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx grafana; then
$DOCKER start grafana 2>/dev/null || true
else
echo " Creating Grafana..."
sudo mkdir -p /var/lib/archipelago/grafana
sudo chown 472:472 /var/lib/archipelago/grafana 2>/dev/null || true
sudo $DOCKER run -d --name grafana --restart unless-stopped \
$DOCKER run -d --name grafana --restart unless-stopped \
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
--security-opt no-new-privileges:true \
-p 3000:3000 -v /var/lib/archipelago/grafana:/var/lib/grafana \
@@ -1340,13 +1364,13 @@ LNDCONF
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx jellyfin; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx jellyfin; then
sudo $DOCKER start jellyfin 2>/dev/null || true
if ! $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx jellyfin; then
if $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx jellyfin; then
$DOCKER start jellyfin 2>/dev/null || true
else
echo " Creating Jellyfin..."
sudo mkdir -p /var/lib/archipelago/jellyfin/config /var/lib/archipelago/jellyfin/cache
sudo $DOCKER run -d --name jellyfin --restart unless-stopped \
$DOCKER run -d --name jellyfin --restart unless-stopped \
--cap-drop ALL --security-opt no-new-privileges:true \
-p 8096:8096 \
-v /var/lib/archipelago/jellyfin/config:/config \
@@ -1363,13 +1387,13 @@ LNDCONF
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx vaultwarden; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx vaultwarden; then
sudo $DOCKER start vaultwarden 2>/dev/null || true
if ! $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx vaultwarden; then
if $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx vaultwarden; then
$DOCKER start vaultwarden 2>/dev/null || true
else
echo " Creating Vaultwarden..."
sudo mkdir -p /var/lib/archipelago/vaultwarden
sudo $DOCKER run -d --name vaultwarden --restart unless-stopped \
$DOCKER run -d --name vaultwarden --restart unless-stopped \
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges:true \
-p 8082:80 -v /var/lib/archipelago/vaultwarden:/data \
@@ -1385,12 +1409,12 @@ LNDCONF
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx searxng; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx searxng; then
sudo $DOCKER start searxng 2>/dev/null || true
if ! $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx searxng; then
if $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx searxng; then
$DOCKER start searxng 2>/dev/null || true
else
echo " Creating SearXNG..."
sudo $DOCKER run -d --name searxng --restart unless-stopped \
$DOCKER run -d --name searxng --restart unless-stopped \
--cap-drop ALL --security-opt no-new-privileges:true \
-p 8888:8080 \
docker.io/searxng/searxng:latest
@@ -1400,26 +1424,8 @@ LNDCONF
fi
' 2>&1 | sed 's/^/ /' || true
# Ollama (local LLM inference — used by AIUI)
progress "Ensuring Ollama"
ssh $SSH_OPTS "$TARGET_HOST" '
DOCKER=podman
command -v podman >/dev/null 2>&1 || DOCKER=docker
if ! sudo $DOCKER ps --format "{{.Names}}" 2>/dev/null | grep -qx ollama; then
if sudo $DOCKER ps -a --format "{{.Names}}" 2>/dev/null | grep -qx ollama; then
sudo $DOCKER start ollama 2>/dev/null || true
else
echo " Creating Ollama..."
sudo mkdir -p /var/lib/archipelago/ollama
sudo $DOCKER run -d --name ollama --restart unless-stopped \
--cap-drop ALL --security-opt no-new-privileges:true \
-p 11434:11434 -v /var/lib/archipelago/ollama:/root/.ollama \
docker.io/ollama/ollama:latest
fi
else
echo " Ollama already running"
fi
' 2>&1 | sed 's/^/ /' || true
# Ollama — optional, install from marketplace if needed
# (removed from auto-deploy: large image, not needed for core functionality)
fi # end FRONTEND_ONLY guard
@@ -1437,31 +1443,31 @@ LNDCONF
# resolve container IPs for nginx proxy (DNS resolver 127.0.0.11 is unreliable in podman)
progress "Fixing IndeedHub for NIP-07"
ssh $SSH_OPTS "$TARGET_HOST" '
if sudo podman ps --format "{{.Names}}" 2>/dev/null | grep -q "^indeedhub$"; then
if podman ps --format "{{.Names}}" 2>/dev/null | grep -q "^indeedhub$"; then
CHANGED=false
NETWORK=$(sudo podman inspect indeedhub --format "{{range \$k, \$v := .NetworkSettings.Networks}}{{\$k}}{{end}}" 2>/dev/null)
NETWORK=$(podman inspect indeedhub --format "{{range \$k, \$v := .NetworkSettings.Networks}}{{\$k}}{{end}}" 2>/dev/null)
# Remove X-Frame-Options so iframe works
if sudo podman exec indeedhub grep -q "X-Frame-Options" /etc/nginx/conf.d/default.conf 2>/dev/null; then
sudo podman exec indeedhub sed -i "/X-Frame-Options/d" /etc/nginx/conf.d/default.conf
if podman exec indeedhub grep -q "X-Frame-Options" /etc/nginx/conf.d/default.conf 2>/dev/null; then
podman exec indeedhub sed -i "/X-Frame-Options/d" /etc/nginx/conf.d/default.conf
CHANGED=true
echo " Removed X-Frame-Options from IndeedHub"
fi
# Inject nostr-provider.js for NIP-07 signing
if ! sudo podman exec indeedhub test -f /usr/share/nginx/html/nostr-provider.js 2>/dev/null; then
sudo podman cp /opt/archipelago/web-ui/nostr-provider.js indeedhub:/usr/share/nginx/html/nostr-provider.js 2>/dev/null
if ! podman exec indeedhub test -f /usr/share/nginx/html/nostr-provider.js 2>/dev/null; then
podman cp /opt/archipelago/web-ui/nostr-provider.js indeedhub:/usr/share/nginx/html/nostr-provider.js 2>/dev/null
echo " Copied nostr-provider.js into IndeedHub"
fi
# Add nostr-provider.js + sub_filter to nginx config
if ! sudo podman exec indeedhub grep -q "nostr-provider" /etc/nginx/conf.d/default.conf 2>/dev/null; then
sudo podman exec indeedhub cat /etc/nginx/conf.d/default.conf > /tmp/ih-nginx.conf 2>/dev/null
if ! podman exec indeedhub grep -q "nostr-provider" /etc/nginx/conf.d/default.conf 2>/dev/null; then
podman exec indeedhub cat /etc/nginx/conf.d/default.conf > /tmp/ih-nginx.conf 2>/dev/null
# Add nostr-provider location block before sw.js block
sed -i "/location = \/sw.js {/i\\ location = /nostr-provider.js {\n add_header Cache-Control \"no-cache, no-store, must-revalidate\";\n expires off;\n }\n" /tmp/ih-nginx.conf
# Add sub_filter for nostr-provider injection
sed -i "/try_files.*index.html/a\\ sub_filter_once on;\n sub_filter '"'"'</head>'"'"' '"'"'<script src=\"/nostr-provider.js\"></script></head>'"'"';" /tmp/ih-nginx.conf
sudo podman cp /tmp/ih-nginx.conf indeedhub:/etc/nginx/conf.d/default.conf 2>/dev/null
podman cp /tmp/ih-nginx.conf indeedhub:/etc/nginx/conf.d/default.conf 2>/dev/null
rm -f /tmp/ih-nginx.conf
CHANGED=true
echo " Injected nostr-provider.js into IndeedHub nginx"
@@ -1469,26 +1475,26 @@ LNDCONF
# Replace DNS-based upstream resolution with hardcoded container IPs
# (podman DNS resolver 127.0.0.11 is unreliable, causing 502 errors)
API_IP=$(sudo podman inspect indeedhub-build_api_1 --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
MINIO_IP=$(sudo podman inspect indeedhub-minio --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
RELAY_IP=$(sudo podman inspect indeedhub-relay --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
API_IP=$(podman inspect indeedhub-build_api_1 --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
MINIO_IP=$(podman inspect indeedhub-minio --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
RELAY_IP=$(podman inspect indeedhub-relay --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
if [ -n "$API_IP" ] && [ -n "$MINIO_IP" ] && [ -n "$RELAY_IP" ]; then
sudo podman exec indeedhub cat /etc/nginx/conf.d/default.conf > /tmp/ih-nginx.conf 2>/dev/null
podman exec indeedhub cat /etc/nginx/conf.d/default.conf > /tmp/ih-nginx.conf 2>/dev/null
# Remove DNS resolver lines and replace upstream variables with hardcoded IPs
sed -i "s|resolver 127.0.0.11 valid=30s ipv6=off;||g" /tmp/ih-nginx.conf
sed -i "s|set \$api_upstream http://api:4000;|set \$api_upstream http://$API_IP:4000;|g" /tmp/ih-nginx.conf
sed -i "s|set \$minio_upstream http://minio:9000;|set \$minio_upstream http://$MINIO_IP:9000;|g" /tmp/ih-nginx.conf
sed -i "s|set \$relay_upstream http://relay:8080;|set \$relay_upstream http://$RELAY_IP:8080;|g" /tmp/ih-nginx.conf
sed -i "s|proxy_set_header Host \$host;|proxy_set_header Host \$http_host;|g" /tmp/ih-nginx.conf
sudo podman cp /tmp/ih-nginx.conf indeedhub:/etc/nginx/conf.d/default.conf 2>/dev/null
podman cp /tmp/ih-nginx.conf indeedhub:/etc/nginx/conf.d/default.conf 2>/dev/null
rm -f /tmp/ih-nginx.conf
CHANGED=true
echo " Patched IndeedHub nginx with container IPs (API=$API_IP MINIO=$MINIO_IP RELAY=$RELAY_IP)"
fi
if [ "$CHANGED" = true ]; then
sudo podman exec indeedhub nginx -s reload 2>/dev/null
podman exec indeedhub nginx -s reload 2>/dev/null
fi
fi
' 2>&1 | sed 's/^/ /' || true

View File

@@ -53,4 +53,35 @@ EOF
# Apply sysctl settings
sysctl --system >/dev/null 2>&1 || true
# Remove policy-rc.d if present — leftover from chroot build, blocks service starts
rm -f /usr/sbin/policy-rc.d 2>/dev/null || true
# Ensure NTP time sync via chrony (more reliable than systemd-timesyncd)
if ! dpkg -l chrony >/dev/null 2>&1; then
echo "🕐 Installing chrony for NTP time sync..."
apt-get update -qq && apt-get install -y chrony 2>/dev/null || true
fi
systemctl enable chrony 2>/dev/null || true
systemctl start chrony 2>/dev/null || true
timedatectl set-ntp true 2>/dev/null || true
# Ensure swap exists — prevents OOM kills on memory-constrained nodes
TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
TOTAL_MEM_GB=$((TOTAL_MEM_KB / 1024 / 1024))
SWAP_SIZE_GB=$((TOTAL_MEM_GB > 8 ? 8 : TOTAL_MEM_GB))
if [ ! -f /swapfile ]; then
echo "💾 Creating ${SWAP_SIZE_GB}G swap file..."
fallocate -l ${SWAP_SIZE_GB}G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
if ! grep -q '/swapfile' /etc/fstab; then
echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi
echo "✅ Swap created: ${SWAP_SIZE_GB}G"
else
echo "✅ Swap file already exists"
swapon /swapfile 2>/dev/null || true
fi
echo "✅ Debian optimization complete!"