Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
b42a8f0a2b Archive old changelog entries 2026-02-15 00:05:09 +00:00
62 changed files with 585 additions and 1633 deletions

3
.github/workflows/autolabeler.yml generated vendored
View File

@@ -100,8 +100,7 @@ jobs:
// If it's an update script PR with json changes and a content label, skip adding website/json // If it's an update script PR with json changes and a content label, skip adding website/json
// The PR should be categorized as update script with the content label // The PR should be categorized as update script with the content label
if (!(hasUpdateScript && hasJson && hasContentLabel)) { if (!(hasUpdateScript && hasJson && hasContentLabel)) {
labelsToAdd.add("website"); labelsToAdd.add(hasJson ? "json" : "website");
if (hasJson) labelsToAdd.add("json");
} }
} }

View File

@@ -404,68 +404,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-02-16
### 🆕 New Scripts
- RomM ([#11987](https://github.com/community-scripts/ProxmoxVE/pull/11987))
- LinkDing ([#11976](https://github.com/community-scripts/ProxmoxVE/pull/11976))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Tududi: Fix sed command for DB_FILE configuration [@tremor021](https://github.com/tremor021) ([#11988](https://github.com/community-scripts/ProxmoxVE/pull/11988))
- slskd: fix exit position [@MickLesk](https://github.com/MickLesk) ([#11963](https://github.com/community-scripts/ProxmoxVE/pull/11963))
- cryptpad: restore config earlier and run onlyoffice upgrade [@MickLesk](https://github.com/MickLesk) ([#11964](https://github.com/community-scripts/ProxmoxVE/pull/11964))
- jellyseerr/overseerr: Migrate update script to Seerr; prompt rerun [@MickLesk](https://github.com/MickLesk) ([#11965](https://github.com/community-scripts/ProxmoxVE/pull/11965))
- #### 🔧 Refactor
- core/vm's: ensure script state is sent on script exit [@MickLesk](https://github.com/MickLesk) ([#11991](https://github.com/community-scripts/ProxmoxVE/pull/11991))
- Vaultwarden: export VW_VERSION as version number [@MickLesk](https://github.com/MickLesk) ([#11966](https://github.com/community-scripts/ProxmoxVE/pull/11966))
- Zabbix: Improve zabbix-agent service detection [@MickLesk](https://github.com/MickLesk) ([#11968](https://github.com/community-scripts/ProxmoxVE/pull/11968))
### 💾 Core
- #### ✨ New Features
- tools.func: ensure /usr/local/bin PATH persists for pct enter sessions [@MickLesk](https://github.com/MickLesk) ([#11970](https://github.com/community-scripts/ProxmoxVE/pull/11970))
- #### 🔧 Refactor
- core: remove duplicate error handler from alpine-install.func [@MickLesk](https://github.com/MickLesk) ([#11971](https://github.com/community-scripts/ProxmoxVE/pull/11971))
### 📂 Github
- github: add "website" label if "json" changed [@MickLesk](https://github.com/MickLesk) ([#11975](https://github.com/community-scripts/ProxmoxVE/pull/11975))
### 🌐 Website
- #### 📝 Script Information
- Update Wishlist LXC webpage to include reverse proxy info [@summoningpixels](https://github.com/summoningpixels) ([#11973](https://github.com/community-scripts/ProxmoxVE/pull/11973))
- Update OpenCloud LXC webpage to include services ports [@summoningpixels](https://github.com/summoningpixels) ([#11969](https://github.com/community-scripts/ProxmoxVE/pull/11969))
## 2026-02-15
### 🆕 New Scripts
- ebusd ([#11942](https://github.com/community-scripts/ProxmoxVE/pull/11942))
- add: seer script and migrations [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11930](https://github.com/community-scripts/ProxmoxVE/pull/11930))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix seerr URL in jellyseerr script [@lucacome](https://github.com/lucacome) ([#11951](https://github.com/community-scripts/ProxmoxVE/pull/11951))
- Fix jellyseer and overseer script replacement [@lucacome](https://github.com/lucacome) ([#11949](https://github.com/community-scripts/ProxmoxVE/pull/11949))
- Tautulli: Add setuptools < 81 [@tremor021](https://github.com/tremor021) ([#11943](https://github.com/community-scripts/ProxmoxVE/pull/11943))
- #### 💥 Breaking Changes
- Refactor: Patchmon [@vhsdream](https://github.com/vhsdream) ([#11888](https://github.com/community-scripts/ProxmoxVE/pull/11888))
## 2026-02-14 ## 2026-02-14
### 🚀 Updated Scripts ### 🚀 Updated Scripts

View File

@@ -27,7 +27,7 @@ function update_script() {
exit exit
fi fi
RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | awk 'NR==1') set +o pipefail && RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | head -1) && set -o pipefail
if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then
msg_info "Updating ${APP} LXC" msg_info "Updating ${APP} LXC"

View File

@@ -39,20 +39,17 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cryptpad" "cryptpad/cryptpad" "tarball" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cryptpad" "cryptpad/cryptpad" "tarball"
msg_info "Restoring configuration"
mv /opt/config.js /opt/cryptpad/config/
msg_ok "Configuration restored"
msg_info "Updating CryptaPad" msg_info "Updating CryptaPad"
cd /opt/cryptpad cd /opt/cryptpad
$STD npm ci $STD npm ci
$STD npm run install:components $STD npm run install:components
if [ -f "/opt/cryptpad/install-onlyoffice.sh" ]; then
$STD bash /opt/cryptpad/install-onlyoffice.sh --accept-license
fi
$STD npm run build $STD npm run build
msg_ok "Updated CryptaPad" msg_ok "Updated CryptaPad"
msg_info "Restoring configuration"
mv /opt/config.js /opt/cryptpad/config/
msg_ok "Configuration restored"
msg_info "Starting Service" msg_info "Starting Service"
systemctl start cryptpad systemctl start cryptpad
msg_ok "Started Service" msg_ok "Started Service"

View File

@@ -1,50 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Joerg Heinemann (heinemannj)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/john30/ebusd
APP="ebusd"
var_tags="${var_tags:-automation}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-2}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /etc/default/ebusd ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "ebusd" "john30/ebusd"; then
msg_info "Stopping Services"
systemctl stop ebusd
msg_ok "Stopped Services"
fetch_and_deploy_gh_release "ebusd" "john30/ebusd" "binary" "latest" "/opt/ebusd" "ebusd-*_amd64-trixie_mqtt1.deb"
msg_info "Starting Services"
systemctl start ebusd
msg_ok "Started Services"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"

View File

@@ -1,6 +0,0 @@
__ __
___ / /_ __ ___________/ /
/ _ \/ __ \/ / / / ___/ __ /
/ __/ /_/ / /_/ (__ ) /_/ /
\___/_.___/\__,_/____/\__,_/

View File

@@ -1,6 +0,0 @@
___ __ ___
/ (_)___ / /______/ (_)___ ____ _
/ / / __ \/ //_/ __ / / __ \/ __ `/
/ / / / / / ,< / /_/ / / / / / /_/ /
/_/_/_/ /_/_/|_|\__,_/_/_/ /_/\__, /
/____/

View File

@@ -1,6 +0,0 @@
____ __ ___
/ __ \____ ____ ___ / |/ /
/ /_/ / __ \/ __ `__ \/ /|_/ /
/ _, _/ /_/ / / / / / / / / /
/_/ |_|\____/_/ /_/ /_/_/ /_/

View File

@@ -1,6 +0,0 @@
_____
/ ___/___ ___ __________
\__ \/ _ \/ _ \/ ___/ ___/
___/ / __/ __/ / / /
/____/\___/\___/_/ /_/

View File

@@ -29,48 +29,47 @@ function update_script() {
exit exit
fi fi
if [[ -f "/opt/jellyseerr/package.json" ]] && [[ "$(grep -m1 '"version"' /opt/jellyseerr/package.json | awk -F'"' '{print $4}')" == "2.7.3" ]]; then if [ "$(node -v | cut -c2-3)" -ne 22 ]; then
echo msg_info "Updating Node.js Repository"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" >/etc/apt/sources.list.d/nodesource.list
echo "Jellyseerr v2.7.3 detected." msg_ok "Updating Node.js Repository"
echo
echo "Seerr is the new unified Jellyseerr and Overseerr."
echo "More info: https://docs.seerr.dev/blog/seerr-release"
echo
read -rp "Do you want to migrate to Seerr now? (y/N): " MIGRATE
echo
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
msg_info "Migration cancelled. Exiting."
exit 0
fi
msg_info "Switching update script to Seerr" msg_info "Updating Packages"
cat <<'EOF' >/usr/bin/update $STD apt-get update
#!/usr/bin/env bash $STD apt-get -y upgrade
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/seerr.sh)" msg_ok "Updating Packages"
EOF fi
chmod +x /usr/bin/update
msg_ok "Switched update script to Seerr" cd /opt/jellyseerr
msg_warn "Please type 'update' again to complete the migration" output=$(git pull --no-rebase)
exit
pnpm_current=$(pnpm --version 2>/dev/null)
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/jellyseerr/package.json)
if [ -z "$pnpm_current" ]; then
msg_error "pnpm not found. Installing version $pnpm_desired..."
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
elif ! node -e "const semver = require('semver'); process.exit(semver.satisfies('$pnpm_current', '$pnpm_desired') ? 0 : 1)"; then
msg_error "Updating pnpm from version $pnpm_current to $pnpm_desired..."
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
else
msg_ok "pnpm is already installed and satisfies version $pnpm_desired."
fi fi
msg_info "Updating Jellyseerr" msg_info "Updating Jellyseerr"
cd /opt/jellyseerr
systemctl stop jellyseerr
output=$(git pull --no-rebase)
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/jellyseerr/package.json)
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
if echo "$output" | grep -q "Already up to date."; then if echo "$output" | grep -q "Already up to date."; then
msg_ok "$APP is already up to date." msg_ok "$APP is already up to date."
exit exit
fi fi
systemctl stop jellyseerr
rm -rf dist .next node_modules rm -rf dist .next node_modules
export CYPRESS_INSTALL_BINARY=0 export CYPRESS_INSTALL_BINARY=0
cd /opt/jellyseerr cd /opt/jellyseerr
$STD pnpm install --frozen-lockfile $STD pnpm install --frozen-lockfile
export NODE_OPTIONS="--max-old-space-size=3072" export NODE_OPTIONS="--max-old-space-size=3072"
$STD pnpm build $STD pnpm build
cat <<EOF >/etc/systemd/system/jellyseerr.service cat <<EOF >/etc/systemd/system/jellyseerr.service
[Unit] [Unit]
Description=jellyseerr Service Description=jellyseerr Service
@@ -86,6 +85,7 @@ ExecStart=/usr/bin/node dist/index.js
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
systemctl daemon-reload systemctl daemon-reload
systemctl start jellyseerr systemctl start jellyseerr
msg_ok "Updated Jellyseerr" msg_ok "Updated Jellyseerr"

View File

@@ -1,79 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (MickLesk)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://linkding.link/
APP="linkding"
var_tags="${var_tags:-bookmarks;management}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-1024}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/linkding ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "linkding" "sissbruecker/linkding"; then
msg_info "Stopping Services"
systemctl stop nginx linkding linkding-tasks
msg_ok "Stopped Services"
msg_info "Backing up Data"
cp -r /opt/linkding/data /opt/linkding_data_backup
cp /opt/linkding/.env /opt/linkding_env_backup
msg_ok "Backed up Data"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "linkding" "sissbruecker/linkding"
msg_info "Restoring Data"
cp -r /opt/linkding_data_backup/. /opt/linkding/data
cp /opt/linkding_env_backup /opt/linkding/.env
rm -rf /opt/linkding_data_backup /opt/linkding_env_backup
ln -sf /usr/lib/x86_64-linux-gnu/mod_icu.so /opt/linkding/libicu.so
msg_ok "Restored Data"
msg_info "Updating LinkDing"
cd /opt/linkding
rm -f bookmarks/settings/dev.py
touch bookmarks/settings/custom.py
$STD npm ci
$STD npm run build
$STD uv sync --no-dev --frozen
$STD uv pip install gunicorn
set -a && source /opt/linkding/.env && set +a
$STD /opt/linkding/.venv/bin/python manage.py migrate
$STD /opt/linkding/.venv/bin/python manage.py collectstatic --no-input
msg_ok "Updated LinkDing"
msg_info "Starting Services"
systemctl start nginx linkding linkding-tasks
msg_ok "Started Services"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}"

View File

@@ -27,33 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!" msg_error "No ${APP} Installation Found!"
exit exit
fi fi
if [[ -f "$HOME/.overseerr" ]] && [[ "$(cat "$HOME/.overseerr")" == "1.34.0" ]]; then
echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Overseerr v1.34.0 detected."
echo
echo "Seerr is the new unified Jellyseerr and Overseerr."
echo "More info: https://docs.seerr.dev/blog/seerr-release"
echo
read -rp "Do you want to migrate to Seerr now? (y/N): " MIGRATE
echo
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
msg_info "Migration cancelled. Exiting."
exit 0
fi
msg_info "Switching update script to Seerr"
cat <<'EOF' >/usr/bin/update
#!/usr/bin/env bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/seerr.sh)"
EOF
chmod +x /usr/bin/update
msg_ok "Switched update script to Seerr"
msg_warn "Please type 'update' again to complete the migration"
exit 0
fi
if check_for_gh_release "overseerr" "sct/overseerr"; then if check_for_gh_release "overseerr" "sct/overseerr"; then
msg_info "Stopping Service" msg_info "Stopping Service"
systemctl stop overseerr systemctl stop overseerr

View File

@@ -43,40 +43,22 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon"
msg_info "Updating PatchMon" msg_info "Updating PatchMon"
VERSION=$(get_latest_github_release "PatchMon/PatchMon")
PROTO="$(sed -n '/SERVER_PROTOCOL/s/[^=]*=//p' /opt/backend.env)"
HOST="$(sed -n '/SERVER_HOST/s/[^=]*=//p' /opt/backend.env)"
[[ "${PROTO:-http}" == "http" ]] && PORT=":3001"
sed -i 's/PORT=3399/PORT=3001/' /opt/backend.env
sed -i -e "s/VERSION=.*/VERSION=$VERSION/" \
-e "\|VITE_API_URL=|s|http.*|${PROTO:-http}://${HOST:-$LOCAL_IP}${PORT:-}/api/v1|" /opt/frontend.env
export NODE_ENV=production
cd /opt/patchmon cd /opt/patchmon
export NODE_ENV=production
$STD npm install --no-audit --no-fund --no-save --ignore-scripts
cd /opt/patchmon/backend
$STD npm install --no-audit --no-fund --no-save --ignore-scripts $STD npm install --no-audit --no-fund --no-save --ignore-scripts
cd /opt/patchmon/frontend cd /opt/patchmon/frontend
mv /opt/frontend.env /opt/patchmon/frontend/.env $STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts
$STD npm install --no-audit --no-fund --no-save --ignore-scripts --include=dev
$STD npm run build $STD npm run build
cd /opt/patchmon/backend cd /opt/patchmon/backend
mv /opt/backend.env /opt/patchmon/backend/.env mv /opt/backend.env /opt/patchmon/backend/.env
$STD npm run db:generate mv /opt/frontend.env /opt/patchmon/frontend/.env
$STD npx prisma migrate deploy $STD npx prisma migrate deploy
cp /opt/patchmon/docker/nginx.conf.template /etc/nginx/sites-available/patchmon.conf $STD npx prisma generate
sed -i -e 's|proxy_pass .*|proxy_pass http://127.0.0.1:3001;|' \
-e '\|try_files |i\ root /opt/patchmon/frontend/dist;' \
-e 's|alias.*|alias /opt/patchmon/frontend/dist/assets;|' \
-e '\|expires 1y|i\ root /opt/patchmon/frontend/dist;' /etc/nginx/sites-available/patchmon.conf
ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
$STD nginx -t
systemctl restart nginx
msg_ok "Updated PatchMon" msg_ok "Updated PatchMon"
msg_info "Starting Service" msg_info "Starting Service"
if grep -q '/usr/bin/node' /etc/systemd/system/patchmon-server.service; then
sed -i 's|ExecStart=.*|ExecStart=/usr/bin/npm run start|' /etc/systemd/system/patchmon-server.service
systemctl daemon-reload
fi
systemctl start patchmon-server systemctl start patchmon-server
msg_ok "Started Service" msg_ok "Started Service"
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
@@ -91,4 +73,4 @@ description
msg_ok "Completed successfully!\n" msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"

View File

@@ -1,74 +0,0 @@
#!/usr/bin/env bash
source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://romm.app
APP="RomM"
var_tags="${var_tags:-emulation}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-20}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/romm ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "romm" "rommapp/romm"; then
msg_info "Stopping Services"
systemctl stop romm-backend romm-worker romm-scheduler romm-watcher
msg_ok "Stopped Services"
msg_info "Backing up configuration"
cp /opt/romm/.env /opt/romm/.env.backup
msg_ok "Backed up configuration"
fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm"
msg_info "Updating ROMM"
cp /opt/romm/.env.backup /opt/romm/.env
cd /opt/romm
$STD uv sync --all-extras
cd /opt/romm/backend
$STD uv run alembic upgrade head
cd /opt/romm/frontend
$STD npm install
$STD npm run build
# Merge static assets into dist folder
cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/
mkdir -p /opt/romm/frontend/dist/assets/romm
ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources
ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets
msg_ok "Updated ROMM"
msg_info "Starting Services"
systemctl start romm-backend romm-worker romm-scheduler romm-watcher
msg_ok "Started Services"
msg_ok "Updated successfully"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: CrazyWolf13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://docs.seerr.dev/
APP="Seerr"
var_tags="${var_tags:-media}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-12}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/seerr && ! -d /opt/jellyseerr && ! -d /opt/overseerr ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
# Start Migration from Jellyseerr
if [[ -f /etc/systemd/system/jellyseerr.service ]]; then
msg_info "Stopping Jellyseerr"
$STD systemctl stop jellyseerr || true
$STD systemctl disable jellyseerr || true
[ -f /etc/systemd/system/jellyseerr.service ] && rm -f /etc/systemd/system/jellyseerr.service
msg_ok "Stopped Jellyseerr"
msg_info "Creating Backup (Patience)"
tar -czf /opt/jellyseerr_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt jellyseerr
msg_ok "Created Backup"
msg_info "Migrating Jellyseerr to seerr"
[ -d /opt/jellyseerr ] && mv /opt/jellyseerr /opt/seerr
[ -d /etc/jellyseerr ] && mv /etc/jellyseerr /etc/seerr
[ -f /etc/seerr/jellyseerr.conf ] && mv /etc/seerr/jellyseerr.conf /etc/seerr/seerr.conf
cat <<EOF >/etc/systemd/system/seerr.service
[Unit]
Description=Seerr Service
Wants=network-online.target
After=network-online.target
[Service]
EnvironmentFile=/etc/seerr/seerr.conf
Environment=NODE_ENV=production
Type=exec
Restart=on-failure
WorkingDirectory=/opt/seerr
ExecStart=/usr/bin/node dist/index.js
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable -q --now seerr
msg_ok "Migrated Jellyserr to Seerr"
fi
# END Jellyseerr Migration
# Start Migration from Overseerr
if [[ -f /etc/systemd/system/overseerr.service ]]; then
msg_info "Stopping Overseerr"
$STD systemctl stop overseerr || true
$STD systemctl disable overseerr || true
[ -f /etc/systemd/system/overseerr.service ] && rm -f /etc/systemd/system/overseerr.service
msg_ok "Stopped Overseerr"
msg_info "Creating Backup (Patience)"
tar -czf /opt/overseerr_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt overseerr
msg_ok "Created Backup"
msg_info "Migrating Overseerr to seerr"
[ -d /opt/overseerr ] && mv /opt/overseerr /opt/seerr
mkdir -p /etc/seerr
cat <<EOF >/etc/seerr/seerr.conf
## Seerr's default port is 5055, if you want to use both, change this.
## specify on which port to listen
PORT=5055
## specify on which interface to listen, by default seerr listens on all interfaces
#HOST=127.0.0.1
## Uncomment if you want to force Node.js to resolve IPv4 before IPv6 (advanced users only)
# FORCE_IPV4_FIRST=true
EOF
cat <<EOF >/etc/systemd/system/seerr.service
[Unit]
Description=Seerr Service
Wants=network-online.target
After=network-online.target
[Service]
EnvironmentFile=/etc/seerr/seerr.conf
Environment=NODE_ENV=production
Type=exec
Restart=on-failure
WorkingDirectory=/opt/seerr
ExecStart=/usr/bin/node dist/index.js
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable -q --now seerr
msg_ok "Migrated Overseerr to Seerr"
fi
# END Overseerr Migration
if check_for_gh_release "seerr" "seerr-team/seerr"; then
msg_info "Stopping Service"
systemctl stop seerr
msg_ok "Stopped Service"
msg_info "Creating Backup"
cp -a /opt/seerr/config /opt/seerr_backup
msg_ok "Created Backup"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "seerr" "seerr-team/seerr" "tarball"
msg_info "Updating PNPM Version"
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/seerr/package.json)
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
msg_ok "Updated PNPM Version"
msg_info "Updating Seerr"
cd /opt/seerr
rm -rf dist .next node_modules
export CYPRESS_INSTALL_BINARY=0
$STD pnpm install --frozen-lockfile
export NODE_OPTIONS="--max-old-space-size=3072"
$STD pnpm build
msg_ok "Updated Seerr"
msg_info "Restoring Backup"
rm -rf /opt/seerr/config
mv /opt/seerr_backup /opt/seerr/config
msg_ok "Restored Backup"
msg_info "Starting Service"
systemctl start seerr
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5055${CL}"

View File

@@ -83,7 +83,6 @@ function update_script() {
msg_ok "Started Soularr Timer" msg_ok "Started Soularr Timer"
msg_ok "Updated Soularr successfully!" msg_ok "Updated Soularr successfully!"
fi fi
exit
} }
start start

View File

@@ -45,8 +45,6 @@ function update_script() {
msg_info "Updating VaultWarden to $VAULT (Patience)" msg_info "Updating VaultWarden to $VAULT (Patience)"
cd /tmp/vaultwarden-src cd /tmp/vaultwarden-src
VW_VERSION="$VAULT"
export VW_VERSION
$STD cargo build --features "sqlite,mysql,postgresql" --release $STD cargo build --features "sqlite,mysql,postgresql" --release
if [[ -f /usr/bin/vaultwarden ]]; then if [[ -f /usr/bin/vaultwarden ]]; then
cp target/release/vaultwarden /usr/bin/ cp target/release/vaultwarden /usr/bin/

View File

@@ -35,18 +35,15 @@ function update_script() {
exit exit
fi fi
if systemctl cat zabbix-agent2.service &>/dev/null; then if systemctl list-unit-files | grep -q zabbix-agent2.service; then
AGENT_SERVICE="zabbix-agent2" AGENT_SERVICE="zabbix-agent2"
elif systemctl cat zabbix-agent.service &>/dev/null; then
AGENT_SERVICE="zabbix-agent"
else else
AGENT_SERVICE="" AGENT_SERVICE="zabbix-agent"
msg_warn "No Zabbix Agent service found, skipping agent actions"
fi fi
msg_info "Stopping Services" msg_info "Stopping Services"
systemctl stop zabbix-server systemctl stop zabbix-server
[[ -n "$AGENT_SERVICE" ]] && systemctl stop "$AGENT_SERVICE" systemctl stop "$AGENT_SERVICE"
msg_ok "Stopped Services" msg_ok "Stopped Services"
read -rp "Choose Zabbix version [1] 7.0 LTS [2] 7.4 (Latest Stable) [3] Latest available (default: 2): " ZABBIX_CHOICE read -rp "Choose Zabbix version [1] 7.0 LTS [2] 7.4 (Latest Stable) [3] Latest available (default: 2): " ZABBIX_CHOICE
@@ -86,13 +83,13 @@ function update_script() {
$STD apt install --only-upgrade zabbix-server-pgsql zabbix-frontend-php php8.4-pgsql $STD apt install --only-upgrade zabbix-server-pgsql zabbix-frontend-php php8.4-pgsql
if [[ "$AGENT_SERVICE" == "zabbix-agent2" ]]; then if [ "$AGENT_SERVICE" = "zabbix-agent2" ]; then
$STD apt install --only-upgrade zabbix-agent2 zabbix-agent2-plugin-postgresql $STD apt install --only-upgrade zabbix-agent2 zabbix-agent2-plugin-postgresql
if [ -f /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf ]; then if [ -f /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf ]; then
sed -i 's|^Plugins.NVIDIA.System.Path=.*|# Plugins.NVIDIA.System.Path=/usr/libexec/zabbix/zabbix-agent2-plugin-nvidia-gpu|' \ sed -i 's|^Plugins.NVIDIA.System.Path=.*|# Plugins.NVIDIA.System.Path=/usr/libexec/zabbix/zabbix-agent2-plugin-nvidia-gpu|' \
/etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf
fi fi
elif [[ "$AGENT_SERVICE" == "zabbix-agent" ]]; then else
$STD apt install --only-upgrade zabbix-agent $STD apt install --only-upgrade zabbix-agent
fi fi
@@ -108,7 +105,7 @@ function update_script() {
msg_info "Starting Services" msg_info "Starting Services"
systemctl start zabbix-server systemctl start zabbix-server
[[ -n "$AGENT_SERVICE" ]] && systemctl start "$AGENT_SERVICE" systemctl start "$AGENT_SERVICE"
systemctl restart apache2 systemctl restart apache2
msg_ok "Started Services" msg_ok "Started Services"
msg_ok "Updated successfully!" msg_ok "Updated successfully!"

View File

@@ -1,40 +0,0 @@
{
"name": "ebusd",
"slug": "ebusd",
"categories": [
16
],
"date_created": "2026-02-15",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": null,
"documentation": "https://github.com/john30/ebusd/wiki",
"website": "https://github.com/john30/ebusd",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ebusd.webp",
"config_path": "/etc/default/ebusd",
"description": "ebusd is a daemon for handling communication with eBUS devices connected to a 2-wire `energy bus` used by numerous heating systems.",
"install_methods": [
{
"type": "default",
"script": "ct/ebusd.sh",
"resources": {
"cpu": 1,
"ram": 512,
"hdd": 2,
"os": "debian",
"version": "13"
}
}
],
"default_credentials": {
"username": "root",
"password": null
},
"notes": [
{
"text": "For required post installation actions, checkout: `https://github.com/community-scripts/ProxmoxVE/discussions/11352`",
"type": "info"
}
]
}

View File

@@ -1,5 +1,5 @@
{ {
"generated": "2026-02-16T12:14:16Z", "generated": "2026-02-14T18:07:29Z",
"versions": [ "versions": [
{ {
"slug": "2fauth", "slug": "2fauth",
@@ -193,16 +193,16 @@
{ {
"slug": "cleanuparr", "slug": "cleanuparr",
"repo": "Cleanuparr/Cleanuparr", "repo": "Cleanuparr/Cleanuparr",
"version": "v2.6.2", "version": "v2.6.1",
"pinned": false, "pinned": false,
"date": "2026-02-15T02:15:19Z" "date": "2026-02-13T10:00:19Z"
}, },
{ {
"slug": "cloudreve", "slug": "cloudreve",
"repo": "cloudreve/cloudreve", "repo": "cloudreve/cloudreve",
"version": "4.14.1", "version": "4.14.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T01:39:24Z" "date": "2026-02-14T06:05:06Z"
}, },
{ {
"slug": "comfyui", "slug": "comfyui",
@@ -256,9 +256,9 @@
{ {
"slug": "dawarich", "slug": "dawarich",
"repo": "Freika/dawarich", "repo": "Freika/dawarich",
"version": "1.2.0", "version": "1.1.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T22:33:56Z" "date": "2026-02-08T14:42:45Z"
}, },
{ {
"slug": "discopanel", "slug": "discopanel",
@@ -298,9 +298,9 @@
{ {
"slug": "donetick", "slug": "donetick",
"repo": "donetick/donetick", "repo": "donetick/donetick",
"version": "v0.1.74", "version": "v0.1.73",
"pinned": false, "pinned": false,
"date": "2026-02-14T23:21:45Z" "date": "2026-02-12T23:42:30Z"
}, },
{ {
"slug": "drawio", "slug": "drawio",
@@ -316,13 +316,6 @@
"pinned": false, "pinned": false,
"date": "2026-01-06T12:05:40Z" "date": "2026-01-06T12:05:40Z"
}, },
{
"slug": "ebusd",
"repo": "john30/ebusd",
"version": "26.1",
"pinned": false,
"date": "2026-02-09T06:09:24Z"
},
{ {
"slug": "elementsynapse", "slug": "elementsynapse",
"repo": "etkecc/synapse-admin", "repo": "etkecc/synapse-admin",
@@ -361,9 +354,9 @@
{ {
"slug": "firefly", "slug": "firefly",
"repo": "firefly-iii/firefly-iii", "repo": "firefly-iii/firefly-iii",
"version": "v6.4.22", "version": "v6.4.20",
"pinned": false, "pinned": false,
"date": "2026-02-15T18:43:08Z" "date": "2026-02-14T12:39:02Z"
}, },
{ {
"slug": "fladder", "slug": "fladder",
@@ -410,9 +403,9 @@
{ {
"slug": "ghostfolio", "slug": "ghostfolio",
"repo": "ghostfolio/ghostfolio", "repo": "ghostfolio/ghostfolio",
"version": "2.239.0", "version": "2.238.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T09:51:16Z" "date": "2026-02-12T18:28:55Z"
}, },
{ {
"slug": "gitea", "slug": "gitea",
@@ -557,9 +550,9 @@
{ {
"slug": "immich-public-proxy", "slug": "immich-public-proxy",
"repo": "alangrainger/immich-public-proxy", "repo": "alangrainger/immich-public-proxy",
"version": "v1.15.2", "version": "v1.15.1",
"pinned": false, "pinned": false,
"date": "2026-02-16T08:54:59Z" "date": "2026-01-26T08:04:27Z"
}, },
{ {
"slug": "inspircd", "slug": "inspircd",
@@ -578,16 +571,16 @@
{ {
"slug": "invoiceninja", "slug": "invoiceninja",
"repo": "invoiceninja/invoiceninja", "repo": "invoiceninja/invoiceninja",
"version": "v5.12.60", "version": "v5.12.59",
"pinned": false, "pinned": false,
"date": "2026-02-15T00:11:31Z" "date": "2026-02-13T02:26:13Z"
}, },
{ {
"slug": "jackett", "slug": "jackett",
"repo": "Jackett/Jackett", "repo": "Jackett/Jackett",
"version": "v0.24.1127", "version": "v0.24.1113",
"pinned": false, "pinned": false,
"date": "2026-02-16T08:43:41Z" "date": "2026-02-14T17:46:58Z"
}, },
{ {
"slug": "jellystat", "slug": "jellystat",
@@ -641,9 +634,9 @@
{ {
"slug": "kimai", "slug": "kimai",
"repo": "kimai/kimai", "repo": "kimai/kimai",
"version": "2.49.0", "version": "2.48.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T20:40:19Z" "date": "2026-01-31T18:10:59Z"
}, },
{ {
"slug": "kitchenowl", "slug": "kitchenowl",
@@ -729,13 +722,6 @@
"pinned": false, "pinned": false,
"date": "2025-11-16T22:40:18Z" "date": "2025-11-16T22:40:18Z"
}, },
{
"slug": "linkding",
"repo": "sissbruecker/linkding",
"version": "v1.45.0",
"pinned": false,
"date": "2026-01-06T20:31:04Z"
},
{ {
"slug": "linkstack", "slug": "linkstack",
"repo": "linkstackorg/linkstack", "repo": "linkstackorg/linkstack",
@@ -900,9 +886,9 @@
{ {
"slug": "nginx-ui", "slug": "nginx-ui",
"repo": "0xJacky/nginx-ui", "repo": "0xJacky/nginx-ui",
"version": "v2.3.3", "version": "v2.3.2",
"pinned": false, "pinned": false,
"date": "2026-02-15T00:58:14Z" "date": "2025-12-09T09:47:15Z"
}, },
{ {
"slug": "nightscout", "slug": "nightscout",
@@ -970,16 +956,23 @@
{ {
"slug": "ots", "slug": "ots",
"repo": "Luzifer/ots", "repo": "Luzifer/ots",
"version": "v1.21.1", "version": "v1.21.0",
"pinned": false, "pinned": false,
"date": "2026-02-16T12:12:23Z" "date": "2026-01-19T23:21:29Z"
}, },
{ {
"slug": "outline", "slug": "outline",
"repo": "outline/outline", "repo": "outline/outline",
"version": "v1.5.0", "version": "v1.4.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T18:04:16Z" "date": "2026-01-27T23:43:03Z"
},
{
"slug": "overseerr",
"repo": "sct/overseerr",
"version": "v1.34.0",
"pinned": false,
"date": "2025-03-26T08:48:34Z"
}, },
{ {
"slug": "owncast", "slug": "owncast",
@@ -1019,9 +1012,9 @@
{ {
"slug": "paperless-gpt", "slug": "paperless-gpt",
"repo": "icereed/paperless-gpt", "repo": "icereed/paperless-gpt",
"version": "v0.25.0", "version": "v0.24.0",
"pinned": false, "pinned": false,
"date": "2026-02-16T08:31:48Z" "date": "2026-01-14T21:28:09Z"
}, },
{ {
"slug": "paperless-ngx", "slug": "paperless-ngx",
@@ -1061,9 +1054,9 @@
{ {
"slug": "pelican-wings", "slug": "pelican-wings",
"repo": "pelican-dev/wings", "repo": "pelican-dev/wings",
"version": "v1.0.0-beta24", "version": "v1.0.0-beta23",
"pinned": false, "pinned": false,
"date": "2026-02-15T16:09:56Z" "date": "2026-02-09T22:10:26Z"
}, },
{ {
"slug": "pf2etools", "slug": "pf2etools",
@@ -1275,13 +1268,6 @@
"pinned": false, "pinned": false,
"date": "2025-03-28T13:00:23Z" "date": "2025-03-28T13:00:23Z"
}, },
{
"slug": "romm",
"repo": "RetroAchievements/RALibretro",
"version": "1.8.2",
"pinned": false,
"date": "2026-01-23T17:03:31Z"
},
{ {
"slug": "rustdeskserver", "slug": "rustdeskserver",
"repo": "rustdesk/rustdesk-server", "repo": "rustdesk/rustdesk-server",
@@ -1324,19 +1310,12 @@
"pinned": false, "pinned": false,
"date": "2025-03-08T10:49:04Z" "date": "2025-03-08T10:49:04Z"
}, },
{
"slug": "seerr",
"repo": "seerr-team/seerr",
"version": "v3.0.1",
"pinned": false,
"date": "2026-02-14T19:30:24Z"
},
{ {
"slug": "semaphore", "slug": "semaphore",
"repo": "semaphoreui/semaphore", "repo": "semaphoreui/semaphore",
"version": "v2.17.2", "version": "v2.17.0",
"pinned": false, "pinned": false,
"date": "2026-02-16T10:27:40Z" "date": "2026-02-13T21:08:30Z"
}, },
{ {
"slug": "shelfmark", "slug": "shelfmark",
@@ -1446,9 +1425,9 @@
{ {
"slug": "tautulli", "slug": "tautulli",
"repo": "Tautulli/Tautulli", "repo": "Tautulli/Tautulli",
"version": "v2.16.1", "version": "v2.16.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T20:40:37Z" "date": "2025-09-09T01:05:45Z"
}, },
{ {
"slug": "teddycloud", "slug": "teddycloud",
@@ -1502,9 +1481,9 @@
{ {
"slug": "tracearr", "slug": "tracearr",
"repo": "connorgallopo/Tracearr", "repo": "connorgallopo/Tracearr",
"version": "v1.4.18", "version": "v1.4.17",
"pinned": false, "pinned": false,
"date": "2026-02-15T19:55:40Z" "date": "2026-02-11T01:33:21Z"
}, },
{ {
"slug": "tracktor", "slug": "tracktor",
@@ -1719,9 +1698,9 @@
{ {
"slug": "yubal", "slug": "yubal",
"repo": "guillevc/yubal", "repo": "guillevc/yubal",
"version": "v0.6.0", "version": "v0.5.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T17:47:56Z" "date": "2026-02-09T22:11:32Z"
}, },
{ {
"slug": "zigbee2mqtt", "slug": "zigbee2mqtt",
@@ -1740,16 +1719,16 @@
{ {
"slug": "zitadel", "slug": "zitadel",
"repo": "zitadel/zitadel", "repo": "zitadel/zitadel",
"version": "v4.11.0", "version": "v4.10.1",
"pinned": false, "pinned": false,
"date": "2026-02-16T09:48:38Z" "date": "2026-01-30T06:52:53Z"
}, },
{ {
"slug": "zoraxy", "slug": "zoraxy",
"repo": "tobychui/zoraxy", "repo": "tobychui/zoraxy",
"version": "v3.3.2-rc1", "version": "v3.3.1",
"pinned": false, "pinned": false,
"date": "2026-02-15T02:16:17Z" "date": "2026-01-28T13:52:02Z"
}, },
{ {
"slug": "zwave-js-ui", "slug": "zwave-js-ui",

View File

@@ -0,0 +1,35 @@
{
"name": "Jellyseerr",
"slug": "jellyseerr",
"categories": [
14
],
"date_created": "2024-05-02",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 5055,
"documentation": "https://docs.jellyseerr.dev/",
"website": "https://github.com/Fallenbagel/jellyseerr",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/jellyseerr.webp",
"config_path": "/etc/jellyseerr/jellyseerr.conf",
"description": "Jellyseerr is a free and open source software application for managing requests for your media library. It is a a fork of Overseerr built to bring support for Jellyfin & Emby media servers.",
"install_methods": [
{
"type": "default",
"script": "ct/jellyseerr.sh",
"resources": {
"cpu": 4,
"ram": 4096,
"hdd": 8,
"os": "debian",
"version": "12"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@@ -1,40 +0,0 @@
{
"name": "linkding",
"slug": "linkding",
"categories": [
12
],
"date_created": "2026-02-16",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 9090,
"documentation": "https://linkding.link/",
"website": "https://linkding.link/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/linkding.webp",
"config_path": "/opt/linkding/.env",
"description": "linkding is a self-hosted bookmark manager that is designed to be minimal, fast, and easy to set up. It features a clean UI, tag-based organization, bulk editing, Markdown notes, read it later functionality, sharing, REST API, and browser extensions for Firefox and Chrome.",
"install_methods": [
{
"type": "default",
"script": "ct/linkding.sh",
"resources": {
"cpu": 2,
"ram": 1024,
"hdd": 4,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": "admin",
"password": null
},
"notes": [
{
"text": "Admin credentials are stored in /opt/linkding/.env",
"type": "info"
}
]
}

View File

@@ -33,7 +33,7 @@
}, },
"notes": [ "notes": [
{ {
"text": "Valid TLS certificates and fully-qualified domain names behind a reverse proxy (Caddy) for 3 services - OpenCloud (port: 9200), Collabora (port: 9980), and WOPI (port: 9300) are **REQUIRED**", "text": "Valid TLS certificates and fully-qualified domain names behind a reverse proxy (Caddy) for 3 services - OpenCloud, Collabora, and WOPI are **REQUIRED**",
"type": "warning" "type": "warning"
}, },
{ {

View File

@@ -0,0 +1,35 @@
{
"name": "Overseerr",
"slug": "overseerr",
"categories": [
14
],
"date_created": "2024-05-02",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 5055,
"documentation": "https://docs.overseerr.dev/",
"website": "https://overseerr.dev/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/overseerr.webp",
"config_path": "/opt/overseerr/config/settings.json",
"description": "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem.",
"install_methods": [
{
"type": "default",
"script": "ct/overseerr.sh",
"resources": {
"cpu": 2,
"ram": 4096,
"hdd": 8,
"os": "debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@@ -8,7 +8,7 @@
"type": "ct", "type": "ct",
"updateable": true, "updateable": true,
"privileged": false, "privileged": false,
"interface_port": 3000, "interface_port": 3399,
"documentation": "https://docs.patchmon.net", "documentation": "https://docs.patchmon.net",
"website": "https://patchmon.net", "website": "https://patchmon.net",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/patchmon.webp", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/patchmon.webp",

View File

@@ -1,35 +0,0 @@
{
"name": "RomM",
"slug": "romm",
"categories": [
24
],
"date_created": "2026-02-16",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 80,
"documentation": "https://docs.romm.app/latest/",
"website": "https://romm.app/",
"config_path": "/opt/romm/.env",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/romm.webp",
"description": "RomM (ROM Manager) allows you to scan, enrich, browse and play your game collection with a clean and responsive interface. Support for multiple platforms, various naming schemes, and custom tags.",
"install_methods": [
{
"type": "default",
"script": "ct/romm.sh",
"resources": {
"cpu": 2,
"ram": 4096,
"hdd": 20,
"os": "debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@@ -1,35 +0,0 @@
{
"name": "Seerr",
"slug": "seerr",
"categories": [
13
],
"date_created": "2026-02-15",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 5055,
"documentation": "https://docs.seerr.dev/",
"website": "https://seerr.dev/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/seerr.webp",
"config_path": "/etc/seerr/seerr.conf",
"description": "Open-source media request and discovery manager for Jellyfin, Plex, and Emby. Unified version of Overseerr and Jellyseerr.",
"install_methods": [
{
"type": "default",
"script": "ct/seerr.sh",
"resources": {
"cpu": 4,
"ram": 4096,
"hdd": 12,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@@ -1,40 +1,35 @@
{ {
"name": "Wishlist", "name": "Wishlist",
"slug": "wishlist", "slug": "wishlist",
"categories": [ "categories": [
12 12
], ],
"date_created": "2026-02-04", "date_created": "2026-02-04",
"type": "ct", "type": "ct",
"updateable": true, "updateable": true,
"privileged": false, "privileged": false,
"interface_port": 3280, "interface_port": 3280,
"documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started", "documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started",
"website": "https://github.com/cmintey/wishlist", "config_path": "/opt/wishlist/.env",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp", "website": "https://github.com/cmintey/wishlist",
"config_path": "/opt/wishlist/.env", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp",
"description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!", "description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!",
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "ct/wishlist.sh", "script": "ct/wishlist.sh",
"resources": { "resources": {
"cpu": 2, "cpu": 2,
"ram": 2048, "ram": 2048,
"hdd": 5, "hdd": 5,
"os": "Debian", "os": "Debian",
"version": "13" "version": "13"
} }
} }
], ],
"default_credentials": { "default_credentials": {
"username": null, "username": null,
"password": null "password": null
}, },
"notes": [ "notes": []
{
"text": "When using a reverse proxy with this script, please edit the`ORIGIN` value in `/opt/wishlist/.env` to point to your new URL, otherwise creating an admin account or logging in will not work.",
"type": "info"
}
]
} }

View File

@@ -20,7 +20,7 @@ $STD apk add --no-cache \
libc6-compat libc6-compat
msg_ok "Installed dependencies" msg_ok "Installed dependencies"
RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | awk 'NR==1') RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | head -1)
msg_info "Installing Teamspeak Server v${RELEASE}" msg_info "Installing Teamspeak Server v${RELEASE}"
mkdir -p /opt/teamspeak-server mkdir -p /opt/teamspeak-server
cd /opt/teamspeak-server cd /opt/teamspeak-server

View File

@@ -26,13 +26,13 @@ msg_info "Setup CryptPad"
cd /opt/cryptpad cd /opt/cryptpad
$STD npm ci $STD npm ci
$STD npm run install:components $STD npm run install:components
if [[ "$onlyoffice" =~ ^[Yy]$ ]]; then $STD npm run build
$STD bash -c "./install-onlyoffice.sh --accept-license"
fi
cp config/config.example.js config/config.js cp config/config.example.js config/config.js
sed -i "51s/localhost/${LOCAL_IP}/g" /opt/cryptpad/config/config.js sed -i "51s/localhost/${LOCAL_IP}/g" /opt/cryptpad/config/config.js
sed -i "80s#//httpAddress: 'localhost'#httpAddress: '0.0.0.0'#g" /opt/cryptpad/config/config.js sed -i "80s#//httpAddress: 'localhost'#httpAddress: '0.0.0.0'#g" /opt/cryptpad/config/config.js
$STD npm run build if [[ "$onlyoffice" =~ ^[Yy]$ ]]; then
$STD bash -c "./install-onlyoffice.sh --accept-license"
fi
msg_ok "Setup CryptPad" msg_ok "Setup CryptPad"
msg_info "Creating Service" msg_info "Creating Service"

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Joerg Heinemann (heinemannj)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/john30/ebusd
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing ebusd"
fetch_and_deploy_gh_release "ebusd" "john30/ebusd" "binary" "latest" "" "ebusd-*_amd64-trixie_mqtt1.deb"
systemctl enable -q ebusd
msg_ok "Installed ebusd"
motd_ssh
customize
cleanup_lxc

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://docs.jellyseerr.dev/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt-get install -y \
git \
build-essential
msg_ok "Installed Dependencies"
git clone -q https://github.com/Fallenbagel/jellyseerr.git /opt/jellyseerr
cd /opt/jellyseerr
$STD git checkout main
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/jellyseerr/package.json)
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
msg_info "Installing Jellyseerr (Patience)"
export CYPRESS_INSTALL_BINARY=0
cd /opt/jellyseerr
$STD pnpm install --frozen-lockfile
export NODE_OPTIONS="--max-old-space-size=3072"
$STD pnpm build
mkdir -p /etc/jellyseerr/
cat <<EOF >/etc/jellyseerr/jellyseerr.conf
PORT=5055
# HOST=0.0.0.0
# JELLYFIN_TYPE=emby
EOF
msg_ok "Installed Jellyseerr"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/jellyseerr.service
[Unit]
Description=jellyseerr Service
After=network.target
[Service]
EnvironmentFile=/etc/jellyseerr/jellyseerr.conf
Environment=NODE_ENV=production
Type=exec
WorkingDirectory=/opt/jellyseerr
ExecStart=/usr/bin/node dist/index.js
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now jellyseerr
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -1,126 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (MickLesk)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://linkding.link/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
build-essential \
pkg-config \
python3-dev \
nginx \
libpq-dev \
libicu-dev \
libsqlite3-dev \
libffi-dev
msg_ok "Installed Dependencies"
NODE_VERSION="22" setup_nodejs
setup_uv
fetch_and_deploy_gh_release "linkding" "sissbruecker/linkding"
msg_info "Building Frontend"
cd /opt/linkding
$STD npm ci
$STD npm run build
ln -sf /usr/lib/x86_64-linux-gnu/mod_icu.so /opt/linkding/libicu.so
msg_ok "Built Frontend"
msg_info "Setting up LinkDing"
rm -f bookmarks/settings/dev.py
touch bookmarks/settings/custom.py
$STD uv sync --no-dev --frozen
$STD uv pip install gunicorn
mkdir -p data/{favicons,previews,assets}
ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)
cat <<EOF >/opt/linkding/.env
LD_SUPERUSER_NAME=admin
LD_SUPERUSER_PASSWORD=${ADMIN_PASS}
LD_CSRF_TRUSTED_ORIGINS=http://${LOCAL_IP}:9090
EOF
set -a && source /opt/linkding/.env && set +a
$STD /opt/linkding/.venv/bin/python manage.py generate_secret_key
$STD /opt/linkding/.venv/bin/python manage.py migrate
$STD /opt/linkding/.venv/bin/python manage.py enable_wal
$STD /opt/linkding/.venv/bin/python manage.py create_initial_superuser
$STD /opt/linkding/.venv/bin/python manage.py collectstatic --no-input
msg_ok "Set up LinkDing"
msg_info "Creating Services"
cat <<EOF >/etc/systemd/system/linkding.service
[Unit]
Description=linkding Bookmark Manager
After=network.target
[Service]
User=root
WorkingDirectory=/opt/linkding
EnvironmentFile=/opt/linkding/.env
ExecStart=/opt/linkding/.venv/bin/gunicorn \
--bind 127.0.0.1:8000 \
--workers 3 \
--threads 2 \
--timeout 120 \
bookmarks.wsgi:application
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/linkding-tasks.service
[Unit]
Description=linkding Background Tasks
After=network.target
[Service]
User=root
WorkingDirectory=/opt/linkding
EnvironmentFile=/opt/linkding/.env
ExecStart=/opt/linkding/.venv/bin/python manage.py run_huey
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<'EOF' >/etc/nginx/sites-available/linkding
server {
listen 9090;
server_name _;
client_max_body_size 20M;
location /static/ {
alias /opt/linkding/static/;
expires 30d;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
EOF
$STD rm -f /etc/nginx/sites-enabled/default
$STD ln -sf /etc/nginx/sites-available/linkding /etc/nginx/sites-enabled/linkding
systemctl enable -q --now nginx linkding linkding-tasks
systemctl restart nginx
msg_ok "Created Services"
motd_ssh
customize
cleanup_lxc

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://overseerr.dev/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
fetch_and_deploy_gh_release "overseerr" "sct/overseerr" "tarball"
msg_info "Configuring Overseerr (Patience)"
cd /opt/overseerr
$STD yarn install
$STD yarn build
msg_ok "Configured Overseerr"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/overseerr.service
[Unit]
Description=Overseerr Service
After=network.target
[Service]
Type=exec
WorkingDirectory=/opt/overseerr
ExecStart=/usr/bin/yarn start
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now overseerr
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -27,42 +27,162 @@ PG_DB_NAME="patchmon_db" PG_DB_USER="patchmon_usr" setup_postgresql_db
fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon" fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon"
msg_info "Configuring PatchMon" msg_info "Configuring PatchMon"
VERSION=$(get_latest_github_release "PatchMon/PatchMon")
export NODE_ENV=production
cd /opt/patchmon cd /opt/patchmon
export NODE_ENV=production
$STD npm install --no-audit --no-fund --no-save --ignore-scripts
cd /opt/patchmon/backend
$STD npm install --no-audit --no-fund --no-save --ignore-scripts $STD npm install --no-audit --no-fund --no-save --ignore-scripts
cd /opt/patchmon/frontend cd /opt/patchmon/frontend
cat <<EOF >./.env $STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts
VITE_API_URL=http://${LOCAL_IP}:3001/api/v1
VITE_APP_NAME=PatchMon
VITE_APP_VERSION=${VERSION}
EOF
$STD npm install --no-audit --no-fund --no-save --ignore-scripts --include=dev
$STD npm run build $STD npm run build
JWT_SECRET="$(openssl rand -base64 64 | tr -d "=+/" | cut -c1-50)"
cat <<EOF >/opt/patchmon/backend/.env
# Database Configuration
DATABASE_URL="postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME"
PY_THRESHOLD=3M_DB_CONN_MAX_ATTEMPTS=30
PM_DB_CONN_WAIT_INTERVAL=2
JWT_SECRET="$(openssl rand -hex 64)" # JWT Configuration
mv /opt/patchmon/backend/env.example /opt/patchmon/backend/.env JWT_SECRET="$JWT_SECRET"
sed -i -e "s|DATABASE_URL=.*|DATABASE_URL=\"postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME\"|" \ JWT_EXPIRES_IN=1h
-e "/JWT_SECRET/s/[=$].*/=$JWT_SECRET/" \ JWT_REFRESH_EXPIRES_IN=7d
-e "\|CORS_ORIGIN|s|localhost|$LOCAL_IP|" \
-e "/PORT=3001/aSERVER_PROTOCOL=http \\ # Server Configuration
SERVER_HOST=$LOCAL_IP \\ PORT=3399
SERVER_PORT=3000" \ NODE_ENV=production
-e '/_ENV=production/aTRUST_PROXY=1' \
-e '/REDIS_USER=.*/,+1d' /opt/patchmon/backend/.env # API Configuration
API_VERSION=v1
# CORS Configuration
CORS_ORIGIN="http://$LOCAL_IP"
# Session Configuration
SESSION_INACTIVITY_TIMEOUT_MINUTES=30
# User Configuration
DEFAULT_USER_ROLE=user
# Rate Limiting (times in milliseconds)
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=5000
AUTH_RATE_LIMIT_WINDOW_MS=600000
AUTH_RATE_LIMIT_MAX=500
AGENT_RATE_LIMIT_WINDOW_MS=60000
AGENT_RATE_LIMIT_MAX=1000
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
# Logging
LOG_LEVEL=info
ENABLE_LOGGING=true
# TFA Configuration
TFA_REMEMBER_ME_EXPIRES_IN=30d
TFA_MAX_REMEMBER_SESSIONS=5
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=3
EOF
cat <<EOF >/opt/patchmon/frontend/.env
VITE_API_URL=http://$LOCAL_IP/api/v1
VITE_APP_NAME=PatchMon
VITE_APP_VERSION=1.3.0
EOF
cd /opt/patchmon/backend cd /opt/patchmon/backend
$STD npm run db:generate
$STD npx prisma migrate deploy $STD npx prisma migrate deploy
$STD npx prisma generate
msg_ok "Configured PatchMon" msg_ok "Configured PatchMon"
msg_info "Configuring Nginx" msg_info "Configuring Nginx"
cp /opt/patchmon/docker/nginx.conf.template /etc/nginx/sites-available/patchmon.conf cat <<EOF >/etc/nginx/sites-available/patchmon.conf
sed -i -e 's|proxy_pass .*|proxy_pass http://127.0.0.1:3001;|' \ map \$http_x_forwarded_proto \$proxy_corrected_scheme {
-e '\|try_files |i\ root /opt/patchmon/frontend/dist;' \ default \$scheme; # Fallback to Nginx's actual connection scheme if no X-Forwarded-Proto header was set
-e 's|alias.*|alias /opt/patchmon/frontend/dist/assets;|' \ https https; # If X-Forwarded-Proto is 'https', use 'https'
-e '\|expires 1y|i\ root /opt/patchmon/frontend/dist;' /etc/nginx/sites-available/patchmon.conf http http; # If X-Forwarded-Proto is 'http', use 'http'
}
server {
# Listen on both IPv4 and IPv6 (with all hostnames)
listen 80;
listen [::]:80;
# Security headers
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Frontend
location / {
root /opt/patchmon/frontend/dist;
try_files \$uri \$uri/ /index.html;
}
# Bull Board proxy
location /bullboard {
proxy_pass http://127.0.0.1:3399;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$proxy_corrected_scheme;
proxy_set_header X-Forwarded-Host \$host;
proxy_set_header Cookie \$http_cookie;
proxy_cache_bypass \$http_upgrade;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
# Enable cookie passthrough
proxy_pass_header Set-Cookie;
proxy_cookie_path / /;
# Preserve original client IP
proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for;
if (\$request_method = 'OPTIONS') {
return 204;
}
}
# API proxy
location /api/ {
proxy_pass http://127.0.0.1:3399;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$proxy_corrected_scheme;
proxy_cache_bypass \$http_upgrade;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
# Preserve original client IP
proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for;
if (\$request_method = 'OPTIONS') {
return 204;
}
}
# Static assets caching (exclude Bull Board assets)
location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
root /opt/patchmon/frontend/dist;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Health check endpoint
location /health {
proxy_pass http://127.0.0.1:3399/health;
access_log off;
}
}
EOF
ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/ ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default rm -f /etc/nginx/sites-enabled/default
$STD nginx -t $STD nginx -t
@@ -78,7 +198,7 @@ After=network.target postgresql.service
[Service] [Service]
Type=simple Type=simple
WorkingDirectory=/opt/patchmon/backend WorkingDirectory=/opt/patchmon/backend
ExecStart=/usr/bin/npm run start ExecStart=/usr/bin/node src/server.js
Restart=always Restart=always
RestartSec=10 RestartSec=10
Environment=NODE_ENV=production Environment=NODE_ENV=production
@@ -95,6 +215,57 @@ EOF
systemctl enable -q --now patchmon-server systemctl enable -q --now patchmon-server
msg_ok "Created and started service" msg_ok "Created and started service"
msg_info "Updating settings"
cat <<EOF >/opt/patchmon/backend/update-settings.js
const { PrismaClient } = require('@prisma/client');
const { v4: uuidv4 } = require('uuid');
const prisma = new PrismaClient();
async function updateSettings() {
try {
const existingSettings = await prisma.settings.findFirst();
const settingsData = {
id: uuidv4(),
server_url: 'http://$LOCAL_IP',
server_protocol: 'http',
server_host: '$LOCAL_IP',
server_port: 3399,
update_interval: 60,
auto_update: true,
signup_enabled: false,
ignore_ssl_self_signed: false,
updated_at: new Date()
};
if (existingSettings) {
// Update existing settings
await prisma.settings.update({
where: { id: existingSettings.id },
data: settingsData
});
} else {
// Create new settings record
await prisma.settings.create({
data: settingsData
});
}
console.log('✅ Database settings updated successfully');
} catch (error) {
console.error('❌ Error updating settings:', error.message);
process.exit(1);
} finally {
await prisma.\$disconnect();
}
}
updateSettings();
EOF
cd /opt/patchmon/backend
$STD node update-settings.js
msg_ok "Settings updated successfully"
motd_ssh motd_ssh
customize customize
cleanup_lxc cleanup_lxc

View File

@@ -1,344 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://romm.app
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
acl \
git \
build-essential \
libssl-dev \
libffi-dev \
libmagic-dev \
python3-dev \
python3-pip \
python3-venv \
libmariadb3 \
libmariadb-dev \
libpq-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
zlib1g-dev \
liblzma-dev \
libncurses5-dev \
libncursesw5-dev \
redis-server \
redis-tools \
p7zip-full \
tzdata \
nginx
msg_ok "Installed Dependencies"
PYTHON_VERSION="3.13" setup_uv
NODE_VERSION="22" setup_nodejs
setup_mariadb
MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db
msg_info "Creating directories"
mkdir -p /opt/romm \
/var/lib/romm/config \
/var/lib/romm/resources \
/var/lib/romm/assets/{saves,states,screenshots} \
/var/lib/romm/library/roms \
/var/lib/romm/library/bios
msg_ok "Created directories"
msg_info "Creating configuration file"
cat <<'EOF' >/var/lib/romm/config/config.yml
# RomM Configuration File
# Documentation: https://docs.romm.app/latest/Getting-Started/Configuration-File/
# Only uncomment the lines you want to use/modify
# exclude:
# platforms:
# - excluded_folder_a
# roms:
# single_file:
# extensions:
# - xml
# - txt
# names:
# - '._*'
# - '*.nfo'
# multi_file:
# names:
# - downloaded_media
# - media
# system:
# platforms:
# gc: ngc
# ps1: psx
# The folder name where your roms are located (relative to library path)
# filesystem:
# roms_folder: 'roms'
# scan:
# priority:
# metadata:
# - "igdb"
# - "moby"
# - "ss"
# - "ra"
# artwork:
# - "igdb"
# - "moby"
# - "ss"
# region:
# - "us"
# - "eu"
# - "jp"
# language:
# - "en"
# media:
# - box2d
# - box3d
# - screenshot
# - manual
# emulatorjs:
# debug: false
# cache_limit: null
EOF
chmod 644 /var/lib/romm/config/config.yml
msg_ok "Created configuration file"
fetch_and_deploy_gh_release "RAHasher" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux-*.zip"
cp /opt/RALibretro/RAHasher /usr/bin/RAHasher
chmod +x /usr/bin/RAHasher
fetch_and_deploy_gh_release "romm" "rommapp/romm"
msg_info "Creating environment file"
sed -i 's/^supervised no/supervised systemd/' /etc/redis/redis.conf
systemctl restart redis-server
systemctl enable -q --now redis-server
AUTH_SECRET_KEY=$(openssl rand -hex 32)
cat <<EOF >/opt/romm/.env
ROMM_BASE_PATH=/var/lib/romm
ROMM_CONFIG_PATH=/var/lib/romm/config/config.yml
WEB_CONCURRENCY=4
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=$MARIADB_DB_NAME
DB_USER=$MARIADB_DB_USER
DB_PASSWD=$MARIADB_DB_PASS
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
ROMM_AUTH_SECRET_KEY=$AUTH_SECRET_KEY
DISABLE_DOWNLOAD_ENDPOINT_AUTH=false
DISABLE_CSRF_PROTECTION=false
ENABLE_RESCAN_ON_FILESYSTEM_CHANGE=true
RESCAN_ON_FILESYSTEM_CHANGE_DELAY=5
ENABLE_SCHEDULED_RESCAN=true
SCHEDULED_RESCAN_CRON=0 3 * * *
ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB=true
SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON=0 4 * * *
LOGLEVEL=INFO
EOF
chmod 600 /opt/romm/.env
msg_ok "Created environment file"
msg_info "Setting up RomM Backend"
cd /opt/romm
export UV_CONCURRENT_DOWNLOADS=1
$STD uv sync --all-extras
cd /opt/romm/backend
$STD uv run alembic upgrade head
msg_ok "Set up RomM Backend"
msg_info "Setting up RomM Frontend"
cd /opt/romm/frontend
$STD npm install
$STD npm run build
cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/
mkdir -p /opt/romm/frontend/dist/assets/romm
ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources
ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets
msg_ok "Set up RomM Frontend"
msg_info "Configuring Nginx"
cat <<'EOF' >/etc/nginx/sites-available/romm
upstream romm_backend {
server 127.0.0.1:5000;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name _;
root /opt/romm/frontend/dist;
client_max_body_size 0;
# Frontend SPA
location / {
try_files $uri $uri/ /index.html;
}
# Static assets
location /assets {
alias /opt/romm/frontend/dist/assets;
try_files $uri $uri/ =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
# EmulatorJS player - requires COOP/COEP headers for SharedArrayBuffer
location ~ ^/rom/.*/ejs$ {
add_header Cross-Origin-Embedder-Policy "require-corp";
add_header Cross-Origin-Opener-Policy "same-origin";
try_files $uri /index.html;
}
# Backend API
location /api {
proxy_pass http://romm_backend;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket and Netplay
location ~ ^/(ws|netplay) {
proxy_pass http://romm_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# OpenAPI docs
location = /openapi.json {
proxy_pass http://romm_backend;
}
# Internal library file serving
location /library/ {
internal;
alias /var/lib/romm/library/;
}
}
EOF
rm -f /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/romm /etc/nginx/sites-enabled/romm
systemctl restart nginx
systemctl enable -q --now nginx
msg_ok "Configured Nginx"
msg_info "Creating Services"
cat <<EOF >/etc/systemd/system/romm-backend.service
[Unit]
Description=RomM Backend
After=network.target mariadb.service redis-server.service
Requires=mariadb.service redis-server.service
[Service]
Type=simple
WorkingDirectory=/opt/romm/backend
EnvironmentFile=/opt/romm/.env
Environment="PYTHONPATH=/opt/romm"
ExecStart=/opt/romm/.venv/bin/python main.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/romm-worker.service
[Unit]
Description=RomM RQ Worker
After=network.target mariadb.service redis-server.service romm-backend.service
Requires=mariadb.service redis-server.service
[Service]
Type=simple
WorkingDirectory=/opt/romm/backend
EnvironmentFile=/opt/romm/.env
Environment="PYTHONPATH=/opt/romm/backend"
ExecStart=/opt/romm/.venv/bin/rq worker --path /opt/romm/backend --url redis://127.0.0.1:6379/0 high default low
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/romm-scheduler.service
[Unit]
Description=RomM RQ Scheduler
After=network.target mariadb.service redis-server.service romm-backend.service
Requires=mariadb.service redis-server.service
[Service]
Type=simple
WorkingDirectory=/opt/romm/backend
EnvironmentFile=/opt/romm/.env
Environment="PYTHONPATH=/opt/romm/backend"
Environment="RQ_REDIS_HOST=127.0.0.1"
Environment="RQ_REDIS_PORT=6379"
ExecStart=/opt/romm/.venv/bin/rqscheduler --path /opt/romm/backend
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/romm-watcher.service
[Unit]
Description=RomM Filesystem Watcher
After=network.target romm-backend.service
Requires=romm-backend.service
[Service]
Type=simple
WorkingDirectory=/opt/romm/backend
EnvironmentFile=/opt/romm/.env
Environment="PYTHONPATH=/opt/romm/backend"
ExecStart=/opt/romm/.venv/bin/watchfiles --target-type command '/opt/romm/.venv/bin/python watcher.py' /var/lib/romm/library
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now romm-backend romm-worker romm-scheduler romm-watcher
msg_ok "Created Services"
motd_ssh
customize
cleanup_lxc

View File

@@ -1,67 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: CrazyWolf13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://docs.seerr.dev/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt-get install -y build-essential
msg_ok "Installed Dependencies"
fetch_and_deploy_gh_release "seerr" "seerr-team/seerr" "tarball"
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/seerr/package.json)
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
msg_info "Installing Seerr (Patience)"
export CYPRESS_INSTALL_BINARY=0
cd /opt/seerr
$STD pnpm install --frozen-lockfile
export NODE_OPTIONS="--max-old-space-size=3072"
$STD pnpm build
mkdir -p /etc/seerr/
cat <<EOF >/etc/seerr/seerr.conf
## Seerr's default port is 5055, if you want to use both, change this.
## specify on which port to listen
PORT=5055
## specify on which interface to listen, by default seerr listens on all interfaces
HOST=0.0.0.0
## Uncomment if you want to force Node.js to resolve IPv4 before IPv6 (advanced users only)
# FORCE_IPV4_FIRST=true
EOF
msg_ok "Installed Seerr"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/seerr.service
[Unit]
Description=Seerr Service
Wants=network-online.target
After=network-online.target
[Service]
EnvironmentFile=/etc/seerr/seerr.conf
Environment=NODE_ENV=production
Type=exec
Restart=on-failure
WorkingDirectory=/opt/seerr
ExecStart=/usr/bin/node dist/index.js
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now seerr
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -29,7 +29,6 @@ $STD uv venv --clear
$STD source /opt/Tautulli/.venv/bin/activate $STD source /opt/Tautulli/.venv/bin/activate
$STD uv pip install -r requirements.txt $STD uv pip install -r requirements.txt
$STD uv pip install pyopenssl $STD uv pip install pyopenssl
$STD uv pip install "setuptools<81"
msg_ok "Installed Tautulli" msg_ok "Installed Tautulli"
msg_info "Creating Service" msg_info "Creating Service"

View File

@@ -38,8 +38,8 @@ SECRET="$(openssl rand -hex 64)"
sed -e '/^NODE_ENV=/s/=.*$/=production/' \ sed -e '/^NODE_ENV=/s/=.*$/=production/' \
-e 's/^TUDUDI_USER/# TUDUDI_USER/g' \ -e 's/^TUDUDI_USER/# TUDUDI_USER/g' \
-e "/_SECRET=/s/=.*$/=${SECRET}/" \ -e "/_SECRET=/s/=.*$/=${SECRET}/" \
-e '/^# DB_FILE=/s/^# //' \ -e "/^# DB_FILE/s/^# //; \
-e "s|^DB_FILE=.*|DB_FILE=${DB_LOCATION}/production.sqlite3|" \ \|DB_FILE|s|/path.*$|${DB_LOCATION}/production.sqlite3|" \
-e "/^# TUDUDI_ALLOWED/s/^# //; \ -e "/^# TUDUDI_ALLOWED/s/^# //; \
\|_ORIGINS=|s|=.*$|=<your tududi IP or FDQN>|" \ \|_ORIGINS=|s|=.*$|=<your tududi IP or FDQN>|" \
-e "/^# TUDUDI_UPLOAD/s/^# //; \ -e "/^# TUDUDI_UPLOAD/s/^# //; \

View File

@@ -29,8 +29,6 @@ fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "l
msg_info "Building Vaultwarden (Patience)" msg_info "Building Vaultwarden (Patience)"
cd /tmp/vaultwarden-src cd /tmp/vaultwarden-src
VW_VERSION=$(get_latest_github_release "dani-garcia/vaultwarden")
export VW_VERSION
$STD cargo build --features "sqlite,mysql,postgresql" --release $STD cargo build --features "sqlite,mysql,postgresql" --release
msg_ok "Built Vaultwarden" msg_ok "Built Vaultwarden"

View File

@@ -34,6 +34,42 @@ EOF
fi fi
} }
set -Eeuo pipefail
trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR
trap on_exit EXIT
trap on_interrupt INT
trap on_terminate TERM
error_handler() {
local exit_code="$1"
local line_number="$2"
local command="$3"
if [[ "$exit_code" -eq 0 ]]; then
return 0
fi
printf "\e[?25h"
echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n"
exit "$exit_code"
}
on_exit() {
local exit_code="$?"
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
exit "$exit_code"
}
on_interrupt() {
echo -e "\n${RD}Interrupted by user (SIGINT)${CL}"
exit 130
}
on_terminate() {
echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}"
exit 143
}
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
setting_up_container() { setting_up_container() {
msg_info "Setting up Container OS" msg_info "Setting up Container OS"

View File

@@ -34,19 +34,11 @@ net_resolves() {
} }
ensure_usr_local_bin_persist() { ensure_usr_local_bin_persist() {
# Login shells: /etc/profile.d/
local PROFILE_FILE="/etc/profile.d/10-localbin.sh" local PROFILE_FILE="/etc/profile.d/10-localbin.sh"
if [ ! -f "$PROFILE_FILE" ]; then if [ ! -f "$PROFILE_FILE" ]; then
echo 'case ":$PATH:" in *:/usr/local/bin:*) ;; *) export PATH="/usr/local/bin:$PATH";; esac' >"$PROFILE_FILE" echo 'case ":$PATH:" in *:/usr/local/bin:*) ;; *) export PATH="/usr/local/bin:$PATH";; esac' >"$PROFILE_FILE"
chmod +x "$PROFILE_FILE" chmod +x "$PROFILE_FILE"
fi fi
# Non-login shells (pct enter): /root/.profile and /root/.bashrc
for rc_file in /root/.profile /root/.bashrc; do
if [ -f "$rc_file" ] && ! grep -q '/usr/local/bin' "$rc_file"; then
echo 'export PATH="/usr/local/bin:$PATH"' >>"$rc_file"
fi
done
} }
download_with_progress() { download_with_progress() {

View File

@@ -91,8 +91,8 @@ detect_repo_source() {
community-scripts/ProxmoxVED) REPO_SOURCE="ProxmoxVED" ;; community-scripts/ProxmoxVED) REPO_SOURCE="ProxmoxVED" ;;
"") "")
# No URL detected — use hardcoded fallback # No URL detected — use hardcoded fallback
# This value must match the repo: ProxmoxVE for production, ProxmoxVED for dev # CI sed transforms this on promotion: ProxmoxVED → ProxmoxVE
REPO_SOURCE="ProxmoxVE" REPO_SOURCE="ProxmoxVED"
;; ;;
*) *)
# Fork or unknown repo # Fork or unknown repo
@@ -135,44 +135,19 @@ explain_exit_code() {
# --- Generic / Shell --- # --- Generic / Shell ---
1) echo "General error / Operation not permitted" ;; 1) echo "General error / Operation not permitted" ;;
2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
10) echo "Docker / privileged mode required (unsupported environment)" ;;
# --- curl / wget errors (commonly seen in downloads) --- # --- curl / wget errors (commonly seen in downloads) ---
4) echo "curl: Feature not supported or protocol error" ;;
5) echo "curl: Could not resolve proxy" ;;
6) echo "curl: DNS resolution failed (could not resolve host)" ;; 6) echo "curl: DNS resolution failed (could not resolve host)" ;;
7) echo "curl: Failed to connect (network unreachable / host down)" ;; 7) echo "curl: Failed to connect (network unreachable / host down)" ;;
8) echo "curl: FTP server reply error" ;;
22) echo "curl: HTTP error returned (404, 429, 500+)" ;; 22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
23) echo "curl: Write error (disk full or permissions)" ;;
25) echo "curl: Upload failed" ;;
28) echo "curl: Operation timeout (network slow or server not responding)" ;; 28) echo "curl: Operation timeout (network slow or server not responding)" ;;
30) echo "curl: FTP port command failed" ;;
35) echo "curl: SSL/TLS handshake failed (certificate error)" ;; 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;
56) echo "curl: Receive error (connection reset by peer)" ;;
75) echo "Temporary failure (retry later)" ;;
78) echo "curl: Remote file not found (404 on FTP/file)" ;;
# --- Package manager / APT / DPKG --- # --- Package manager / APT / DPKG ---
100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
102) echo "APT: Lock held by another process (dpkg/apt still running)" ;; 102) echo "APT: Lock held by another process (dpkg/apt still running)" ;;
# --- BSD sysexits.h (64-78) ---
64) echo "Usage error (wrong arguments)" ;;
65) echo "Data format error (bad input data)" ;;
66) echo "Input file not found (cannot open input)" ;;
67) echo "User not found (addressee unknown)" ;;
68) echo "Host not found (hostname unknown)" ;;
69) echo "Service unavailable" ;;
70) echo "Internal software error" ;;
71) echo "System error (OS-level failure)" ;;
72) echo "Critical OS file missing" ;;
73) echo "Cannot create output file" ;;
74) echo "I/O error" ;;
76) echo "Remote protocol error" ;;
77) echo "Permission denied" ;;
# --- Common shell/system errors --- # --- Common shell/system errors ---
124) echo "Command timed out (timeout command)" ;; 124) echo "Command timed out (timeout command)" ;;
126) echo "Command invoked cannot execute (permission problem?)" ;; 126) echo "Command invoked cannot execute (permission problem?)" ;;
@@ -425,22 +400,26 @@ detect_cpu() {
# - Detects RAM speed using dmidecode # - Detects RAM speed using dmidecode
# - Sets RAM_SPEED global (e.g., "4800" for DDR5-4800) # - Sets RAM_SPEED global (e.g., "4800" for DDR5-4800)
# - Requires root access for dmidecode # - Requires root access for dmidecode
# - Returns empty if not available or if speed is "Unknown" (nested VMs) # - Returns empty if not available
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
detect_ram() { detect_ram() {
RAM_SPEED="" RAM_SPEED=""
if command -v dmidecode &>/dev/null; then if command -v dmidecode &>/dev/null; then
# Get configured memory speed (actual running speed) # Get configured memory speed (actual running speed)
# Use || true to handle "Unknown" values in nested VMs (no numeric match) RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Configured Memory Speed:" | grep -oE "[0-9]+" | head -1 || true)
RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Configured Memory Speed:" | grep -oE "[0-9]+" | head -1) || true
# Fallback to Speed: if Configured not available # Fallback to Speed: if Configured not available
if [[ -z "$RAM_SPEED" ]]; then if [[ -z "$RAM_SPEED" ]]; then
RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Speed:" | grep -oE "[0-9]+" | head -1) || true RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Speed:" | grep -oE "[0-9]+" | head -1 || true)
fi fi
fi fi
# Ensure RAM_SPEED is a valid integer (PocketBase stores it as integer)
if [[ -z "$RAM_SPEED" || ! "$RAM_SPEED" =~ ^[0-9]+$ ]]; then
RAM_SPEED=0
fi
export RAM_SPEED export RAM_SPEED
} }
@@ -535,7 +514,7 @@ post_to_api() {
"gpu_vendor": "${gpu_vendor}", "gpu_vendor": "${gpu_vendor}",
"gpu_model": "${gpu_model}", "gpu_model": "${gpu_model}",
"gpu_passthrough": "${gpu_passthrough}", "gpu_passthrough": "${gpu_passthrough}",
"ram_speed": "${ram_speed}", "ram_speed": ${ram_speed:-0},
"repo_source": "${REPO_SOURCE}" "repo_source": "${REPO_SOURCE}"
} }
EOF EOF
@@ -639,7 +618,7 @@ post_to_api_vm() {
"gpu_vendor": "${gpu_vendor}", "gpu_vendor": "${gpu_vendor}",
"gpu_model": "${gpu_model}", "gpu_model": "${gpu_model}",
"gpu_passthrough": "${gpu_passthrough}", "gpu_passthrough": "${gpu_passthrough}",
"ram_speed": "${ram_speed}", "ram_speed": ${ram_speed:-0},
"repo_source": "${REPO_SOURCE}" "repo_source": "${REPO_SOURCE}"
} }
EOF EOF
@@ -649,8 +628,6 @@ EOF
curl -fsS -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \ curl -fsS -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" &>/dev/null || true -d "$JSON_PAYLOAD" &>/dev/null || true
POST_TO_API_DONE=true
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -775,7 +752,7 @@ post_update_to_api() {
"gpu_vendor": "${gpu_vendor}", "gpu_vendor": "${gpu_vendor}",
"gpu_model": "${gpu_model}", "gpu_model": "${gpu_model}",
"gpu_passthrough": "${gpu_passthrough}", "gpu_passthrough": "${gpu_passthrough}",
"ram_speed": "${ram_speed}", "ram_speed": ${ram_speed:-0},
"repo_source": "${REPO_SOURCE}" "repo_source": "${REPO_SOURCE}"
} }
EOF EOF
@@ -817,7 +794,7 @@ EOF
"gpu_vendor": "${gpu_vendor}", "gpu_vendor": "${gpu_vendor}",
"gpu_model": "${gpu_model}", "gpu_model": "${gpu_model}",
"gpu_passthrough": "${gpu_passthrough}", "gpu_passthrough": "${gpu_passthrough}",
"ram_speed": "${ram_speed}", "ram_speed": ${ram_speed:-0},
"repo_source": "${REPO_SOURCE}" "repo_source": "${REPO_SOURCE}"
} }
EOF EOF

View File

@@ -5253,20 +5253,14 @@ ensure_log_on_host() {
# - Exit trap handler for reporting to API telemetry # - Exit trap handler for reporting to API telemetry
# - Captures exit code and reports to PocketBase using centralized error descriptions # - Captures exit code and reports to PocketBase using centralized error descriptions
# - Uses explain_exit_code() from api.func for consistent error messages # - Uses explain_exit_code() from api.func for consistent error messages
# - For non-zero exit codes: posts "failed" status # - Posts failure status with exit code to API (error description resolved automatically)
# - For zero exit codes where post_update_to_api was never called: # - Only executes on non-zero exit codes
# catches orphaned "installing" records (e.g., script exited cleanly
# but description() was never reached)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
api_exit_script() { api_exit_script() {
local exit_code=$? exit_code=$?
if [ $exit_code -ne 0 ]; then if [ $exit_code -ne 0 ]; then
ensure_log_on_host ensure_log_on_host
post_update_to_api "failed" "$exit_code" post_update_to_api "failed" "$exit_code"
elif [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
# Script exited with 0 but never sent a completion status
# This catches edge cases like early returns after post_to_api()
post_update_to_api "failed" "1"
fi fi
} }
@@ -5274,5 +5268,5 @@ if command -v pveversion >/dev/null 2>&1; then
trap 'api_exit_script' EXIT trap 'api_exit_script' EXIT
fi fi
trap 'ensure_log_on_host; post_update_to_api "failed" "$?"' ERR trap 'ensure_log_on_host; post_update_to_api "failed" "$?"' ERR
trap 'ensure_log_on_host; post_update_to_api "failed" "130"; exit 130' SIGINT trap 'ensure_log_on_host; post_update_to_api "failed" "130"' SIGINT
trap 'ensure_log_on_host; post_update_to_api "failed" "143"; exit 143' SIGTERM trap 'ensure_log_on_host; post_update_to_api "failed" "143"' SIGTERM

View File

@@ -37,34 +37,11 @@ if ! declare -f explain_exit_code &>/dev/null; then
case "$code" in case "$code" in
1) echo "General error / Operation not permitted" ;; 1) echo "General error / Operation not permitted" ;;
2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
10) echo "Docker / privileged mode required (unsupported environment)" ;;
4) echo "curl: Feature not supported or protocol error" ;;
5) echo "curl: Could not resolve proxy" ;;
6) echo "curl: DNS resolution failed (could not resolve host)" ;; 6) echo "curl: DNS resolution failed (could not resolve host)" ;;
7) echo "curl: Failed to connect (network unreachable / host down)" ;; 7) echo "curl: Failed to connect (network unreachable / host down)" ;;
8) echo "curl: FTP server reply error" ;;
22) echo "curl: HTTP error returned (404, 429, 500+)" ;; 22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
23) echo "curl: Write error (disk full or permissions)" ;;
25) echo "curl: Upload failed" ;;
28) echo "curl: Operation timeout (network slow or server not responding)" ;; 28) echo "curl: Operation timeout (network slow or server not responding)" ;;
30) echo "curl: FTP port command failed" ;;
35) echo "curl: SSL/TLS handshake failed (certificate error)" ;; 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;
56) echo "curl: Receive error (connection reset by peer)" ;;
75) echo "Temporary failure (retry later)" ;;
78) echo "curl: Remote file not found (404 on FTP/file)" ;;
64) echo "Usage error (wrong arguments)" ;;
65) echo "Data format error (bad input data)" ;;
66) echo "Input file not found (cannot open input)" ;;
67) echo "User not found (addressee unknown)" ;;
68) echo "Host not found (hostname unknown)" ;;
69) echo "Service unavailable" ;;
70) echo "Internal software error" ;;
71) echo "System error (OS-level failure)" ;;
72) echo "Critical OS file missing" ;;
73) echo "Cannot create output file" ;;
74) echo "I/O error" ;;
76) echo "Remote protocol error" ;;
77) echo "Permission denied" ;;
100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
102) echo "APT: Lock held by another process (dpkg/apt still running)" ;; 102) echo "APT: Lock held by another process (dpkg/apt still running)" ;;

View File

@@ -172,7 +172,7 @@ network_check() {
fi fi
set -e set -e
trap 'error_handler' ERR trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
} }
# ============================================================================== # ==============================================================================

View File

@@ -1851,26 +1851,16 @@ function download_with_progress() {
# Ensures /usr/local/bin is permanently in system PATH. # Ensures /usr/local/bin is permanently in system PATH.
# #
# Description: # Description:
# - Adds to /etc/profile.d for login shells (SSH, noVNC) # - Adds to /etc/profile.d if not present
# - Adds to /root/.bashrc for non-login shells (pct enter)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
function ensure_usr_local_bin_persist() { function ensure_usr_local_bin_persist() {
# Skip on Proxmox host
command -v pveversion &>/dev/null && return
# Login shells: /etc/profile.d/
local PROFILE_FILE="/etc/profile.d/custom_path.sh" local PROFILE_FILE="/etc/profile.d/custom_path.sh"
if [[ ! -f "$PROFILE_FILE" ]]; then
if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then
echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE"
chmod +x "$PROFILE_FILE" chmod +x "$PROFILE_FILE"
fi fi
# Non-login shells (pct enter): /root/.bashrc
local BASHRC="/root/.bashrc"
if [[ -f "$BASHRC" ]] && ! grep -q '/usr/local/bin' "$BASHRC"; then
echo 'export PATH="/usr/local/bin:$PATH"' >>"$BASHRC"
fi
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@@ -529,21 +529,9 @@ cleanup_vmid() {
} }
cleanup() { cleanup() {
local exit_code=$?
if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then
popd >/dev/null || true popd >/dev/null || true
fi fi
# Report final telemetry status if post_to_api_vm was called but no update was sent
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if declare -f post_update_to_api >/dev/null 2>&1; then
if [[ $exit_code -ne 0 ]]; then
post_update_to_api "failed" "$exit_code"
else
# Exited cleanly but description()/success was never called — shouldn't happen
post_update_to_api "failed" "1"
fi
fi
fi
} }
check_root() { check_root() {

View File

@@ -100,15 +100,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -100,15 +100,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -100,15 +100,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -104,16 +104,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
# Only send telemetry if post_to_api_vm was called (installing status was sent) post_update_to_api "done" "none"
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -101,15 +101,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -100,15 +100,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -105,15 +105,7 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -79,15 +79,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -101,15 +101,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -109,15 +109,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -97,15 +97,7 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -100,15 +100,7 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -99,15 +99,7 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }

View File

@@ -99,15 +99,8 @@ function cleanup_vmid() {
} }
function cleanup() { function cleanup() {
local exit_code=$?
popd >/dev/null popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "done" "none"
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR rm -rf $TEMP_DIR
} }