Compare commits

..

6 Commits

Author SHA1 Message Date
Tobias
60af59df24 Update kima-hub-install.sh 2026-02-25 21:50:46 +01:00
Tobias
e370e4bbb3 Update kima-hub.sh 2026-02-25 21:49:00 +01:00
Tobias
3853941765 remove: ai 2026-02-25 21:48:28 +01:00
CanbiZ (MickLesk)
f2a775d30e Modify date_created and hdd size in kima-hub.json
Updated the creation date and HDD size for kima-hub.
2026-02-25 15:22:44 +01:00
CanbiZ (MickLesk)
75a7757310 Change default disk size to 20
Updated the disk size default from 24 to 20.
2026-02-25 15:22:07 +01:00
push-app-to-main[bot]
eebbab4c6e Add kima-hub (ct) 2026-02-25 14:18:16 +00:00
31 changed files with 292 additions and 1179 deletions

View File

@@ -214,12 +214,11 @@ jobs:
total=$((total + 1))
slug=$(basename "$script" | sed 's/-install\.sh$//')
# Extract Source URL (GitHub only) from the "# Source:" line
# Extract Source URL (GitHub only)
# Supports both:
# # Source: https://github.com/owner/repo
# # Source: https://example.com | Github: https://github.com/owner/repo
# NOTE: Must filter for "# Source:" line first to avoid matching the License URL
source_url=$(head -20 "$script" | grep -i '# Source:' | grep -oP 'https://github\.com/[^\s|]+' | head -1 || echo "")
source_url=$(head -20 "$script" | grep -oP 'https://github\.com/[^\s|]+' | head -1 || echo "")
if [[ -z "$source_url" ]]; then
report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ No GitHub source |")
continue

View File

@@ -1,119 +0,0 @@
name: Close Unauthorized New Script PRs
on:
pull_request_target:
branches: ["main"]
types: [opened, labeled]
jobs:
check-new-script:
if: github.repository == 'community-scripts/ProxmoxVE'
runs-on: coolify-runner
permissions:
pull-requests: write
contents: read
steps:
- name: Close PR if unauthorized new script submission
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const prNumber = pr.number;
const author = pr.user.login;
const authorType = pr.user.type; // "User" or "Bot"
const owner = context.repo.owner;
const repo = context.repo.repo;
// --- Only act on PRs with the "new script" label ---
const labels = pr.labels.map(l => l.name);
if (!labels.includes("new script")) {
core.info(`PR #${prNumber} does not have "new script" label — skipping.`);
return;
}
// --- Allow our bots ---
const allowedBots = [
"push-app-to-main[bot]",
"push-app-to-main",
];
if (allowedBots.includes(author)) {
core.info(`PR #${prNumber} by allowed bot "${author}" — skipping.`);
return;
}
// --- Check if author is a member of the contributor team ---
const teamSlug = "contributor";
let isMember = false;
try {
const { status } = await github.rest.teams.getMembershipForUserInOrg({
org: owner,
team_slug: teamSlug,
username: author,
});
// status 200 means the user is a member (active or pending)
isMember = true;
} catch (error) {
if (error.status === 404) {
isMember = false;
} else {
core.warning(`Could not check team membership for ${author}: ${error.message}`);
// Fallback: check org membership
try {
await github.rest.orgs.checkMembershipForUser({
org: owner,
username: author,
});
isMember = true;
} catch {
isMember = false;
}
}
}
if (isMember) {
core.info(`PR #${prNumber} by contributor "${author}" — skipping.`);
return;
}
// --- Unauthorized: close the PR with a comment ---
core.info(`Closing PR #${prNumber} by "${author}" — not a contributor or allowed bot.`);
const comment = [
`👋 Hi @${author},`,
``,
`Thank you for your interest in contributing a new script!`,
``,
`However, **new scripts must first be submitted to our development repository** for testing and review before they can be merged here.`,
``,
`> 🛑 New scripts must be submitted to [**ProxmoxVED**](https://github.com/community-scripts/ProxmoxVED) for testing.`,
`> PRs without prior testing will be closed.`,
``,
`Please open your PR at **https://github.com/community-scripts/ProxmoxVED** instead.`,
`Once your script has been tested and approved there, it will be pushed to this repository automatically.`,
``,
`This PR will now be closed. Thank you for understanding! 🙏`,
].join("\n");
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: comment,
});
await github.rest.pulls.update({
owner,
repo,
pull_number: prNumber,
state: "closed",
});
// Add a label to indicate why it was closed
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: ["not a script issue"],
});

View File

@@ -407,77 +407,12 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-02-28
## 2026-02-27
### 🆕 New Scripts
- Strapi ([#12320](https://github.com/community-scripts/ProxmoxVE/pull/12320))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- TrueNAS VM: filter out new nightlies with MASTER [@juronja](https://github.com/juronja) ([#12355](https://github.com/community-scripts/ProxmoxVE/pull/12355))
### 💾 Core
- #### ✨ New Features
- core: graceful fallback for apt-get update failures [@MickLesk](https://github.com/MickLesk) ([#12386](https://github.com/community-scripts/ProxmoxVE/pull/12386))
- core: Improve error outputs across core functions [@MickLesk](https://github.com/MickLesk) ([#12378](https://github.com/community-scripts/ProxmoxVE/pull/12378))
## 2026-02-26
### 🆕 New Scripts
- Kima-Hub ([#12319](https://github.com/community-scripts/ProxmoxVE/pull/12319))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- tools.func: update glx alternatives / nvidia alternative if nvidia glx are missing [@MickLesk](https://github.com/MickLesk) ([#12372](https://github.com/community-scripts/ProxmoxVE/pull/12372))
- hotfix: overseer version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12366](https://github.com/community-scripts/ProxmoxVE/pull/12366))
- #### ✨ New Features
- Add ffmpeg for booklore (ffprobe) [@MickLesk](https://github.com/MickLesk) ([#12371](https://github.com/community-scripts/ProxmoxVE/pull/12371))
- [QOL] Immich: add warning regarding library compilation time [@vhsdream](https://github.com/vhsdream) ([#12345](https://github.com/community-scripts/ProxmoxVE/pull/12345))
### 🧰 Tools
- #### 🐞 Bug Fixes
- Improves adguardhome-sync addon when running on alpine LXCs [@Darkangeel-hd](https://github.com/Darkangeel-hd) ([#12362](https://github.com/community-scripts/ProxmoxVE/pull/12362))
- #### ✨ New Features
- Add Alpine support and improve Tailscale install [@MickLesk](https://github.com/MickLesk) ([#12370](https://github.com/community-scripts/ProxmoxVE/pull/12370))
### 📚 Documentation
- fix wrong link on contributions README.md [@Darkangeel-hd](https://github.com/Darkangeel-hd) ([#12363](https://github.com/community-scripts/ProxmoxVE/pull/12363))
### 📂 Github
- github: add workflow to autom. close unauthorized new-script PRs [@MickLesk](https://github.com/MickLesk) ([#12356](https://github.com/community-scripts/ProxmoxVE/pull/12356))
## 2026-02-25
### 🆕 New Scripts
- Zerobyte ([#12321](https://github.com/community-scripts/ProxmoxVE/pull/12321))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix: overseer migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12340](https://github.com/community-scripts/ProxmoxVE/pull/12340))
- add: vikunja: daemon reload [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12323](https://github.com/community-scripts/ProxmoxVE/pull/12323))
- opnsense-VM: Use ip link to verify bridge existence [@MickLesk](https://github.com/MickLesk) ([#12329](https://github.com/community-scripts/ProxmoxVE/pull/12329))
- wger: Use $http_host for proxy Host header [@MickLesk](https://github.com/MickLesk) ([#12327](https://github.com/community-scripts/ProxmoxVE/pull/12327))
- Passbolt: Update Nginx config `client_max_body_size` [@tremor021](https://github.com/tremor021) ([#12313](https://github.com/community-scripts/ProxmoxVE/pull/12313))
- Zammad: configure Elasticsearch before zammad start [@MickLesk](https://github.com/MickLesk) ([#12308](https://github.com/community-scripts/ProxmoxVE/pull/12308))
@@ -491,10 +426,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Fix detection of ssh keys [@1-tempest](https://github.com/1-tempest) ([#12230](https://github.com/community-scripts/ProxmoxVE/pull/12230))
- #### ✨ New Features
- tools.func: Improve GitHub/Codeberg API error handling and error output [@MickLesk](https://github.com/MickLesk) ([#12330](https://github.com/community-scripts/ProxmoxVE/pull/12330))
- #### 🔧 Refactor
- core: remove duplicate traps, consolidate error handling and harden signal traps [@MickLesk](https://github.com/MickLesk) ([#12316](https://github.com/community-scripts/ProxmoxVE/pull/12316))

View File

@@ -34,7 +34,6 @@ function update_script() {
NODE_VERSION="22" setup_nodejs
setup_mariadb
setup_yq
ensure_dependencies ffmpeg
msg_info "Stopping Service"
systemctl stop booklore

View File

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

View File

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

View File

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

View File

@@ -97,7 +97,7 @@ EOF
if [[ -f ~/.immich_library_revisions ]]; then
libraries=("libjxl" "libheif" "libraw" "imagemagick" "libvips")
cd "$BASE_DIR"
msg_warn "Checking for updates to custom image-processing libraries (recompile time: 2-15min per library)"
msg_info "Checking for updates to custom image-processing libraries"
$STD git pull
for library in "${libraries[@]}"; do
compile_"$library"

View File

@@ -28,7 +28,7 @@ function update_script() {
exit
fi
if [[ -f "$HOME/.overseerr" ]] && [[ "$(printf '%s\n' "1.35.0" "$(cat "$HOME/.overseerr")" | sort -V | head -n1)" == "1.35.0" ]]; then
if [[ -f "$HOME/.overseerr" ]] && [[ "$(cat "$HOME/.overseerr")" == "1.34.0" ]]; then
echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Overseerr v1.34.0 detected."

View File

@@ -1,61 +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: pespinel
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://strapi.io/
APP="Strapi"
var_tags="${var_tags:-cms}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-8}"
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/systemd/system/strapi.service ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
NODE_VERSION="24" setup_nodejs
msg_info "Stopping Strapi"
systemctl stop strapi
msg_ok "Stopped Strapi"
msg_info "Updating Strapi"
cd /opt/strapi
$STD npx @strapi/upgrade minor --yes
msg_ok "Updated Strapi"
msg_info "Building Strapi"
export NODE_OPTIONS="--max-old-space-size=3072"
$STD npm run build
msg_ok "Built Strapi"
msg_info "Starting Strapi"
systemctl start strapi
msg_ok "Started Strapi"
msg_ok "Updated successfully!"
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}:1337${CL}"

View File

@@ -65,7 +65,6 @@ function update_script() {
msg_ok "Stopped Service"
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary"
$STD systemctl daemon-reload
msg_info "Starting Service"
systemctl start vikunja

View File

@@ -1,71 +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: community-scripts
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/nicotsx/zerobyte
APP="Zerobyte"
var_tags="${var_tags:-backup;encryption;restic}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-6144}"
var_disk="${var_disk:-10}"
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/zerobyte ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "zerobyte" "nicotsx/zerobyte"; then
msg_info "Stopping Service"
systemctl stop zerobyte
msg_ok "Stopped Service"
msg_info "Backing up Configuration"
cp /opt/zerobyte/.env /opt/zerobyte.env.bak
msg_ok "Backed up Configuration"
NODE_VERSION="24" setup_nodejs
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "zerobyte" "nicotsx/zerobyte" "tarball"
msg_info "Building Zerobyte"
export NODE_OPTIONS="--max-old-space-size=3072"
cd /opt/zerobyte
$STD bun install
$STD node ./node_modules/vite/bin/vite.js build
msg_ok "Built Zerobyte"
msg_info "Restoring Configuration"
cp /opt/zerobyte.env.bak /opt/zerobyte/.env
rm -f /opt/zerobyte.env.bak
msg_ok "Restored Configuration"
msg_info "Starting Service"
systemctl start zerobyte
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}:4096${CL}"

View File

@@ -175,7 +175,7 @@ All scripts and configurations must follow our coding standards to ensure consis
### Available Guides
- **[CONTRIBUTING.md](CONTRIBUTING.md)** - Essential coding standards and best practices
- **[CODE-AUDIT.md](CODE-AUDIT.md)** - Code review checklist and audit procedures
- **[CODE_AUDIT.md](CODE_AUDIT.md)** - Code review checklist and audit procedures
- **[GUIDE.md](GUIDE.md)** - Comprehensive contribution guide
- **[HELPER_FUNCTIONS.md](HELPER_FUNCTIONS.md)** - Reference for all tools.func helper functions
- **Container Scripts** - `/ct/` templates and guidelines

View File

@@ -1,5 +1,5 @@
{
"generated": "2026-02-28T00:18:10Z",
"generated": "2026-02-25T12:14:52Z",
"versions": [
{
"slug": "2fauth",
@@ -18,9 +18,9 @@
{
"slug": "adguardhome-sync",
"repo": "bakito/adguardhome-sync",
"version": "v0.9.0",
"version": "v0.8.2",
"pinned": false,
"date": "2026-02-27T18:37:37Z"
"date": "2025-10-24T17:13:47Z"
},
{
"slug": "adventurelog",
@@ -109,16 +109,16 @@
{
"slug": "bazarr",
"repo": "morpheus65535/bazarr",
"version": "v1.5.6",
"version": "v1.5.5",
"pinned": false,
"date": "2026-02-26T11:33:11Z"
"date": "2026-02-01T18:00:34Z"
},
{
"slug": "bentopdf",
"repo": "alam00000/bentopdf",
"version": "v2.3.3",
"version": "v2.3.1",
"pinned": false,
"date": "2026-02-27T08:40:05Z"
"date": "2026-02-21T09:04:27Z"
},
{
"slug": "beszel",
@@ -144,23 +144,23 @@
{
"slug": "blocky",
"repo": "0xERR0R/blocky",
"version": "v0.29.0",
"version": "v0.28.2",
"pinned": false,
"date": "2026-02-27T15:48:56Z"
"date": "2025-11-18T05:51:46Z"
},
{
"slug": "booklore",
"repo": "booklore-app/BookLore",
"version": "v2.0.3",
"version": "v2.0.1",
"pinned": false,
"date": "2026-02-26T19:10:59Z"
"date": "2026-02-24T04:15:33Z"
},
{
"slug": "bookstack",
"repo": "BookStackApp/BookStack",
"version": "v25.12.8",
"version": "v25.12.7",
"pinned": false,
"date": "2026-02-27T10:33:14Z"
"date": "2026-02-19T23:36:55Z"
},
{
"slug": "byparr",
@@ -200,9 +200,9 @@
{
"slug": "cleanuparr",
"repo": "Cleanuparr/Cleanuparr",
"version": "v2.7.6",
"version": "v2.7.5",
"pinned": false,
"date": "2026-02-27T19:32:02Z"
"date": "2026-02-24T17:11:50Z"
},
{
"slug": "cloudreve",
@@ -214,9 +214,9 @@
{
"slug": "comfyui",
"repo": "comfyanonymous/ComfyUI",
"version": "v0.15.1",
"version": "v0.15.0",
"pinned": false,
"date": "2026-02-26T22:01:35Z"
"date": "2026-02-24T20:56:09Z"
},
{
"slug": "commafeed",
@@ -242,9 +242,9 @@
{
"slug": "cosmos",
"repo": "azukaar/Cosmos-Server",
"version": "v0.21.5",
"version": "v0.20.2",
"pinned": false,
"date": "2026-02-27T10:07:11Z"
"date": "2026-01-24T00:12:39Z"
},
{
"slug": "cronicle",
@@ -270,16 +270,16 @@
{
"slug": "databasus",
"repo": "databasus/databasus",
"version": "v3.16.3",
"version": "v3.16.2",
"pinned": false,
"date": "2026-02-25T19:57:26Z"
"date": "2026-02-22T21:10:12Z"
},
{
"slug": "dawarich",
"repo": "Freika/dawarich",
"version": "1.3.1",
"version": "1.2.0",
"pinned": false,
"date": "2026-02-27T19:47:40Z"
"date": "2026-02-15T22:33:56Z"
},
{
"slug": "discopanel",
@@ -291,9 +291,9 @@
{
"slug": "dispatcharr",
"repo": "Dispatcharr/Dispatcharr",
"version": "v0.20.1",
"version": "v0.19.0",
"pinned": false,
"date": "2026-02-26T21:38:19Z"
"date": "2026-02-10T21:18:10Z"
},
{
"slug": "docmost",
@@ -361,9 +361,9 @@
{
"slug": "endurain",
"repo": "endurain-project/endurain",
"version": "v0.17.6",
"version": "v0.17.5",
"pinned": false,
"date": "2026-02-27T23:08:50Z"
"date": "2026-02-24T14:51:03Z"
},
{
"slug": "ersatztv",
@@ -382,9 +382,9 @@
{
"slug": "firefly",
"repo": "firefly-iii/firefly-iii",
"version": "v6.5.1",
"version": "v6.5.0",
"pinned": false,
"date": "2026-02-27T20:55:55Z"
"date": "2026-02-23T19:19:00Z"
},
{
"slug": "fladder",
@@ -452,9 +452,9 @@
{
"slug": "gitea-mirror",
"repo": "RayLabsHQ/gitea-mirror",
"version": "v3.9.6",
"version": "v3.9.4",
"pinned": false,
"date": "2026-02-27T07:15:42Z"
"date": "2026-02-24T06:17:56Z"
},
{
"slug": "glance",
@@ -494,9 +494,9 @@
{
"slug": "grist",
"repo": "gristlabs/grist-core",
"version": "v1.7.11",
"version": "v1.7.10",
"pinned": false,
"date": "2026-02-27T17:13:50Z"
"date": "2026-01-12T20:50:50Z"
},
{
"slug": "grocy",
@@ -550,9 +550,9 @@
{
"slug": "homarr",
"repo": "homarr-labs/homarr",
"version": "v1.54.0",
"version": "v1.53.2",
"pinned": false,
"date": "2026-02-27T19:38:50Z"
"date": "2026-02-20T19:41:55Z"
},
{
"slug": "homebox",
@@ -606,16 +606,16 @@
{
"slug": "invoiceninja",
"repo": "invoiceninja/invoiceninja",
"version": "v5.12.69",
"version": "v5.12.66",
"pinned": false,
"date": "2026-02-26T22:23:32Z"
"date": "2026-02-24T09:12:50Z"
},
{
"slug": "jackett",
"repo": "Jackett/Jackett",
"version": "v0.24.1223",
"version": "v0.24.1205",
"pinned": false,
"date": "2026-02-27T05:51:11Z"
"date": "2026-02-25T05:49:14Z"
},
{
"slug": "jellystat",
@@ -627,9 +627,9 @@
{
"slug": "joplin-server",
"repo": "laurent22/joplin",
"version": "v3.5.13",
"version": "v3.5.12",
"pinned": false,
"date": "2026-02-25T21:19:11Z"
"date": "2026-01-17T14:20:33Z"
},
{
"slug": "jotty",
@@ -666,19 +666,12 @@
"pinned": false,
"date": "2026-02-20T09:19:45Z"
},
{
"slug": "kima-hub",
"repo": "Chevron7Locked/kima-hub",
"version": "v1.5.10",
"pinned": false,
"date": "2026-02-27T19:25:56Z"
},
{
"slug": "kimai",
"repo": "kimai/kimai",
"version": "2.50.0",
"version": "2.49.0",
"pinned": false,
"date": "2026-02-25T20:13:51Z"
"date": "2026-02-15T20:40:19Z"
},
{
"slug": "kitchenowl",
@@ -718,9 +711,9 @@
{
"slug": "kubo",
"repo": "ipfs/kubo",
"version": "v0.40.1",
"version": "v0.39.0",
"pinned": false,
"date": "2026-02-27T17:58:22Z"
"date": "2025-11-27T03:47:38Z"
},
{
"slug": "kutt",
@@ -795,9 +788,9 @@
{
"slug": "lubelogger",
"repo": "hargata/lubelog",
"version": "v1.6.1",
"version": "v1.6.0",
"pinned": false,
"date": "2026-02-26T20:01:24Z"
"date": "2026-02-10T20:16:32Z"
},
{
"slug": "mafl",
@@ -816,9 +809,9 @@
{
"slug": "mail-archiver",
"repo": "s1t5/mail-archiver",
"version": "2602.4",
"version": "2602.3",
"pinned": false,
"date": "2026-02-26T08:43:01Z"
"date": "2026-02-22T20:24:18Z"
},
{
"slug": "managemydamnlife",
@@ -830,9 +823,9 @@
{
"slug": "manyfold",
"repo": "manyfold3d/manyfold",
"version": "v0.133.1",
"version": "v0.133.0",
"pinned": false,
"date": "2026-02-26T15:50:34Z"
"date": "2026-02-25T10:40:26Z"
},
{
"slug": "mealie",
@@ -872,9 +865,9 @@
{
"slug": "metube",
"repo": "alexta69/metube",
"version": "2026.02.27",
"version": "2026.02.22",
"pinned": false,
"date": "2026-02-27T11:47:02Z"
"date": "2026-02-22T00:58:45Z"
},
{
"slug": "miniflux",
@@ -956,9 +949,9 @@
{
"slug": "nodebb",
"repo": "NodeBB/NodeBB",
"version": "v4.9.0",
"version": "v4.8.1",
"pinned": false,
"date": "2026-02-27T19:20:51Z"
"date": "2026-01-28T14:19:11Z"
},
{
"slug": "nodecast-tv",
@@ -970,9 +963,9 @@
{
"slug": "oauth2-proxy",
"repo": "oauth2-proxy/oauth2-proxy",
"version": "v7.14.3",
"version": "v7.14.2",
"pinned": false,
"date": "2026-02-26T14:10:21Z"
"date": "2026-01-18T00:26:09Z"
},
{
"slug": "ombi",
@@ -1047,9 +1040,9 @@
{
"slug": "pangolin",
"repo": "fosrl/pangolin",
"version": "1.16.1",
"version": "1.15.4",
"pinned": false,
"date": "2026-02-27T21:18:53Z"
"date": "2026-02-13T23:01:29Z"
},
{
"slug": "paperless-ai",
@@ -1061,9 +1054,9 @@
{
"slug": "paperless-gpt",
"repo": "icereed/paperless-gpt",
"version": "v0.25.1",
"version": "v0.25.0",
"pinned": false,
"date": "2026-02-26T14:50:11Z"
"date": "2026-02-16T08:31:48Z"
},
{
"slug": "paperless-ngx",
@@ -1173,9 +1166,9 @@
{
"slug": "prometheus",
"repo": "prometheus/prometheus",
"version": "v3.10.0",
"version": "v3.9.1",
"pinned": false,
"date": "2026-02-26T01:19:51Z"
"date": "2026-01-07T17:05:53Z"
},
{
"slug": "prometheus-alertmanager",
@@ -1222,9 +1215,9 @@
{
"slug": "pulse",
"repo": "rcourtman/Pulse",
"version": "v5.1.15",
"version": "v5.1.14",
"pinned": false,
"date": "2026-02-27T15:17:24Z"
"date": "2026-02-25T00:11:58Z"
},
{
"slug": "pve-scripts-local",
@@ -1271,9 +1264,9 @@
{
"slug": "radicale",
"repo": "Kozea/Radicale",
"version": "v3.6.1",
"version": "v3.6.0",
"pinned": false,
"date": "2026-02-24T06:36:23Z"
"date": "2026-01-10T06:56:46Z"
},
{
"slug": "rclone",
@@ -1299,9 +1292,9 @@
{
"slug": "recyclarr",
"repo": "recyclarr/recyclarr",
"version": "v8.3.2",
"version": "v8.3.1",
"pinned": false,
"date": "2026-02-25T22:39:51Z"
"date": "2026-02-25T01:01:31Z"
},
{
"slug": "reitti",
@@ -1376,9 +1369,9 @@
{
"slug": "seerr",
"repo": "seerr-team/seerr",
"version": "v3.1.0",
"version": "v3.0.1",
"pinned": false,
"date": "2026-02-27T17:25:29Z"
"date": "2026-02-14T19:30:24Z"
},
{
"slug": "semaphore",
@@ -1397,9 +1390,9 @@
{
"slug": "signoz",
"repo": "SigNoz/signoz-otel-collector",
"version": "v0.144.2",
"version": "v0.144.1",
"pinned": false,
"date": "2026-02-26T05:57:26Z"
"date": "2026-02-25T05:57:17Z"
},
{
"slug": "silverbullet",
@@ -1558,9 +1551,9 @@
{
"slug": "traccar",
"repo": "traccar/traccar",
"version": "v6.12.2",
"version": "v6.12.1",
"pinned": false,
"date": "2026-02-27T15:08:36Z"
"date": "2026-02-22T18:47:37Z"
},
{
"slug": "tracearr",
@@ -1607,9 +1600,9 @@
{
"slug": "tunarr",
"repo": "chrisbenincasa/tunarr",
"version": "v1.1.18",
"version": "v1.1.16",
"pinned": false,
"date": "2026-02-26T22:09:44Z"
"date": "2026-02-23T21:24:47Z"
},
{
"slug": "uhf",
@@ -1670,9 +1663,9 @@
{
"slug": "vikunja",
"repo": "go-vikunja/vikunja",
"version": "v2.1.0",
"version": "v1.1.0",
"pinned": false,
"date": "2026-02-27T14:26:53Z"
"date": "2026-02-09T10:34:29Z"
},
{
"slug": "wallabag",
@@ -1786,13 +1779,6 @@
"pinned": false,
"date": "2026-02-24T15:15:46Z"
},
{
"slug": "zerobyte",
"repo": "restic/restic",
"version": "v0.18.1",
"pinned": false,
"date": "2025-09-21T18:24:38Z"
},
{
"slug": "zigbee2mqtt",
"repo": "Koenkk/zigbee2mqtt",
@@ -1817,9 +1803,9 @@
{
"slug": "zoraxy",
"repo": "tobychui/zoraxy",
"version": "v3.3.2-rc2",
"version": "v3.3.2-rc1",
"pinned": false,
"date": "2026-02-27T03:31:25Z"
"date": "2026-02-15T02:16:17Z"
},
{
"slug": "zwave-js-ui",

View File

@@ -51,10 +51,6 @@
{
"text": "Logs: `/var/log/immich`",
"type": "info"
},
{
"text": "During first install, 5 custom libraries need to be compiled from source. Depending on your CPU, this can take anywhere between 15 minutes and 2 hours. Please be patient. Touch grass or something.",
"type": "warning"
}
]
}

View File

@@ -1,48 +1,48 @@
{
"name": "Kima-Hub",
"slug": "kima-hub",
"categories": [
13
],
"date_created": "2026-02-26",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3030,
"documentation": "https://github.com/Chevron7Locked/kima-hub#readme",
"website": "https://github.com/Chevron7Locked/kima-hub",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kima-hub.webp",
"config_path": "/opt/kima-hub/backend/.env",
"description": "Self-hosted, on-demand audio streaming platform with AI-powered vibe matching, mood detection, smart playlists, and Lidarr/Audiobookshelf integration.",
"install_methods": [
{
"type": "default",
"script": "ct/kima-hub.sh",
"resources": {
"cpu": 4,
"ram": 8192,
"hdd": 20,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "First user to register becomes the administrator.",
"type": "info"
"name": "Kima-Hub",
"slug": "kima-hub",
"categories": [
13
],
"date_created": "2026-02-25",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3030,
"documentation": "https://github.com/Chevron7Locked/kima-hub#readme",
"website": "https://github.com/Chevron7Locked/kima-hub",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kima-hub.webp",
"config_path": "/opt/kima-hub/backend/.env",
"description": "Self-hosted, on-demand audio streaming platform with AI-powered vibe matching, mood detection, smart playlists, and Lidarr/Audiobookshelf integration.",
"install_methods": [
{
"type": "default",
"script": "ct/kima-hub.sh",
"resources": {
"cpu": 4,
"ram": 8192,
"hdd": 20,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
{
"text": "Mount your music library to /music in the container.",
"type": "warning"
},
{
"text": "Audio analysis (mood/vibe detection) requires significant RAM (2-4GB per worker).",
"type": "info"
}
]
"notes": [
{
"text": "First user to register becomes the administrator.",
"type": "info"
},
{
"text": "Mount your music library to /music in the container.",
"type": "warning"
},
{
"text": "Audio analysis (mood/vibe detection) requires significant RAM (2-4GB per worker).",
"type": "info"
}
]
}

View File

@@ -1,48 +0,0 @@
{
"name": "Strapi",
"slug": "strapi",
"categories": [
12
],
"date_created": "2026-02-27",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 1337,
"documentation": "https://docs.strapi.io/",
"website": "https://strapi.io/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/strapi.webp",
"config_path": "/opt/strapi/.env",
"description": "Strapi is a leading open-source headless CMS that enables developers to build powerful APIs quickly. It features a flexible content structure with customizable content types, supporting both REST and GraphQL APIs. The intuitive admin panel allows non-technical users to manage content easily, while developers can extend functionality through plugins. Built on Node.js, Strapi offers role-based access control, media library management, and internationalization support out of the box.",
"install_methods": [
{
"type": "default",
"script": "ct/strapi.sh",
"resources": {
"cpu": 2,
"ram": 4096,
"hdd": 8,
"os": "debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "First-time setup requires creating an admin account at http://IP:1337/admin",
"type": "info"
},
{
"text": "Default installation uses SQLite. For production use, consider configuring PostgreSQL or MySQL.",
"type": "info"
},
{
"text": "Building the admin panel requires 4GB RAM. Container may take 10-15 minutes to fully initialize.",
"type": "warning"
}
]
}

View File

@@ -1,40 +0,0 @@
{
"name": "Zerobyte",
"slug": "zerobyte",
"categories": [
7
],
"date_created": "2026-02-25",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 4096,
"documentation": "https://github.com/nicotsx/zerobyte#readme",
"website": "https://github.com/nicotsx/zerobyte",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/zerobyte.webp",
"config_path": "/opt/zerobyte/.env",
"description": "Zerobyte is a backup automation tool built on top of Restic that provides a modern web interface to schedule, manage, and monitor encrypted backups across multiple storage backends including NFS, SMB, WebDAV, SFTP, S3, and local directories.",
"install_methods": [
{
"type": "default",
"script": "ct/zerobyte.sh",
"resources": {
"cpu": 2,
"ram": 6144,
"hdd": 10,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "For remote mount support (NFS, SMB, WebDAV, SFTP), enable FUSE device passthrough on the LXC container. (FUSE is pre-configured)",
"type": "info"
}
]
}

View File

@@ -13,10 +13,6 @@ setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y ffmpeg
msg_ok "Installed Dependencies"
JAVA_VERSION="25" setup_java
NODE_VERSION="22" setup_nodejs
setup_mariadb

View File

@@ -154,7 +154,7 @@ sed -i -e "/^#shared_preload/s/^#//;/^shared_preload/s/''/'vchord.so'/" /etc/pos
systemctl restart postgresql.service
PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_GRANT_SUPERUSER="true" PG_DB_SKIP_ALTER_ROLE="true" setup_postgresql_db
msg_warn "Compiling Custom Photo-processing Libraries (can take anywhere from 15min to 2h)"
msg_info "Compiling Custom Photo-processing Library (extreme patience)"
LD_LIBRARY_PATH=/usr/local/lib
export LD_RUN_PATH=/usr/local/lib
STAGING_DIR=/opt/staging

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: pespinel
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://strapi.io/
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 \
python3 \
python3-setuptools \
libvips42
msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs
msg_info "Installing Strapi (Patience)"
mkdir -p /opt/strapi
cd /opt/strapi
$STD npx --yes create-strapi-app@latest . --quickstart --no-run --skip-cloud
msg_ok "Installed Strapi"
msg_info "Building Strapi"
cd /opt/strapi
export NODE_OPTIONS="--max-old-space-size=3072"
$STD npm run build
msg_ok "Built Strapi"
msg_info "Creating Service"
cat <<EOF >/opt/strapi/.env
HOST=0.0.0.0
PORT=1337
APP_KEYS=$(openssl rand -base64 32)
API_TOKEN_SALT=$(openssl rand -base64 32)
ADMIN_JWT_SECRET=$(openssl rand -base64 32)
TRANSFER_TOKEN_SALT=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 32)
EOF
cat <<EOF >/etc/systemd/system/strapi.service
[Unit]
Description=Strapi CMS
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/strapi
EnvironmentFile=/opt/strapi/.env
ExecStart=/usr/bin/npm run start
Restart=on-failure
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now strapi
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -164,7 +164,7 @@ server {
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $http_host;
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;

View File

@@ -1,96 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: community-scripts
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/nicotsx/zerobyte
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
echo "davfs2 davfs2/suid_file boolean false" | debconf-set-selections
$STD apt-get install -y \
bzip2 \
fuse3 \
sshfs \
davfs2 \
openssh-client
msg_ok "Installed Dependencies"
fetch_and_deploy_gh_release "restic" "restic/restic" "singlefile" "latest" "/usr/local/bin" "restic_*_linux_amd64.bz2"
mv /usr/local/bin/restic /usr/local/bin/restic.bz2
bzip2 -d /usr/local/bin/restic.bz2
chmod +x /usr/local/bin/restic
fetch_and_deploy_gh_release "rclone" "rclone/rclone" "prebuild" "latest" "/opt/rclone" "rclone-*-linux-amd64.zip"
ln -sf /opt/rclone/rclone /usr/local/bin/rclone
fetch_and_deploy_gh_release "shoutrrr" "nicholas-fedor/shoutrrr" "prebuild" "latest" "/opt/shoutrrr" "shoutrrr_linux_amd64_*.tar.gz"
ln -sf /opt/shoutrrr/shoutrrr /usr/local/bin/shoutrrr
msg_info "Installing Bun"
export BUN_INSTALL="/root/.bun"
curl -fsSL https://bun.sh/install | $STD bash
ln -sf /root/.bun/bin/bun /usr/local/bin/bun
ln -sf /root/.bun/bin/bunx /usr/local/bin/bunx
msg_ok "Installed Bun"
NODE_VERSION="24" setup_nodejs
fetch_and_deploy_gh_release "zerobyte" "nicotsx/zerobyte" "tarball"
msg_info "Building Zerobyte (Patience)"
cd /opt/zerobyte
export VITE_RESTIC_VERSION=$(cat ~/.restic)
export VITE_RCLONE_VERSION=$(cat ~/.rclone)
export VITE_SHOUTRRR_VERSION=$(cat ~/.shoutrrr)
export NODE_OPTIONS="--max-old-space-size=3072"
$STD bun install
$STD node ./node_modules/vite/bin/vite.js build
msg_ok "Built Zerobyte"
msg_info "Configuring Zerobyte"
mkdir -p /var/lib/zerobyte/{data,restic/cache,repositories,volumes}
APP_SECRET=$(openssl rand -hex 32)
cat <<EOF >/opt/zerobyte/.env
BASE_URL=http://${LOCAL_IP}:4096
APP_SECRET=${APP_SECRET}
PORT=4096
ZEROBYTE_DATABASE_URL=/var/lib/zerobyte/data/zerobyte.db
RESTIC_CACHE_DIR=/var/lib/zerobyte/restic/cache
ZEROBYTE_REPOSITORIES_DIR=/var/lib/zerobyte/repositories
ZEROBYTE_VOLUMES_DIR=/var/lib/zerobyte/volumes
MIGRATIONS_PATH=/opt/zerobyte/app/drizzle
NODE_ENV=production
EOF
msg_ok "Configured Zerobyte"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/zerobyte.service
[Unit]
Description=Zerobyte Backup Automation
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/zerobyte
EnvironmentFile=/opt/zerobyte/.env
ExecStart=/usr/local/bin/bun .output/server/index.mjs
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now zerobyte
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -118,7 +118,7 @@ maxkeys_check() {
# Exit if kernel parameters are unavailable
if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
msg_error "Unable to read kernel key parameters. Ensure proper permissions."
echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}"
exit 1
fi
@@ -135,19 +135,19 @@ maxkeys_check() {
# Check if key or byte usage is near limits
failure=0
if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then
msg_warn "Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys})"
echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}"
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
failure=1
fi
if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then
msg_warn "Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes})"
echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}"
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
failure=1
fi
# Provide next steps if issues are detected
if [[ "$failure" -eq 1 ]]; then
msg_error "Kernel key limits exceeded - see suggestions above"
echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}"
exit 1
fi
@@ -2034,7 +2034,6 @@ advanced_settings() {
((STEP++))
else
whiptail --msgbox "Default bridge 'vmbr0' not found!\n\nPlease configure a network bridge in Proxmox first." 10 58
msg_error "Default bridge 'vmbr0' not found"
exit 1
fi
else
@@ -3050,7 +3049,7 @@ install_script() {
CHOICE=""
;;
*)
msg_error "Invalid option: $CHOICE"
echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}"
exit 1
;;
esac
@@ -3129,12 +3128,12 @@ check_container_resources() {
current_cpu=$(nproc)
if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then
msg_warn "Under-provisioned: Required ${var_cpu} CPU/${var_ram}MB RAM, Current ${current_cpu} CPU/${current_ram}MB RAM"
echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}"
echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n"
echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? <yes/No> "
read -r prompt
if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then
msg_error "Aborted: under-provisioned LXC (${current_cpu} CPU/${current_ram}MB RAM < ${var_cpu} CPU/${var_ram}MB RAM)"
echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}"
exit 1
fi
else
@@ -3153,11 +3152,11 @@ check_container_storage() {
local used_size=$(df /boot --output=used | tail -n 1)
usage=$((100 * used_size / total_size))
if ((usage > 80)); then
msg_warn "Storage is dangerously low (${usage}% used on /boot)"
echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}"
echo -ne "Continue anyway? <y/N> "
read -r prompt
if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then
msg_error "Aborted: storage too low (${usage}% used)"
echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}"
exit 1
fi
fi
@@ -3547,16 +3546,10 @@ build_container() {
# Build PCT_OPTIONS as string for export
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
local _func_url
if [ "$var_os" == "alpine" ]; then
_func_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/alpine-install.func"
export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/alpine-install.func)"
else
_func_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func"
fi
export FUNCTIONS_FILE_PATH="$(curl -fsSL "$_func_url")"
if [[ -z "$FUNCTIONS_FILE_PATH" || ${#FUNCTIONS_FILE_PATH} -lt 100 ]]; then
msg_error "Failed to download install functions from: $_func_url"
exit 1
export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func)"
fi
# Core exports for install.func
@@ -3927,9 +3920,7 @@ EOF
fi
sleep 1
if [ "$i" -eq 10 ]; then
local ct_status
ct_status=$(pct status "$CTID" 2>/dev/null || echo "unknown")
msg_error "LXC Container did not reach running state (status: ${ct_status})"
msg_error "LXC Container did not reach running state"
exit 1
fi
done
@@ -3953,7 +3944,7 @@ EOF
if [ -z "$ip_in_lxc" ]; then
msg_error "No IP assigned to CT $CTID after 20s"
msg_custom "🔧" "${YW}" "Troubleshooting:"
echo -e "${YW}Troubleshooting:${CL}"
echo " • Verify bridge ${BRG} exists and has connectivity"
echo " • Check if DHCP server is reachable (if using DHCP)"
echo " • Verify static IP configuration (if using static IP)"
@@ -3975,7 +3966,8 @@ EOF
done
if [ "$ping_success" = false ]; then
msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed - installation will continue"
msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed"
echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}"
else
msg_ok "Network in LXC is reachable (ping)"
fi
@@ -4019,10 +4011,7 @@ EOF
http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
EOF'
pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" || {
msg_error "Failed to install base packages in Alpine container"
exit 1
}
pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null"
else
sleep 3
LANG=${LANG:-en_US.UTF-8}
@@ -4919,7 +4908,8 @@ create_lxc_container() {
return 0
fi
msg_info "An update for the Proxmox LXC stack is available"
echo
echo "An update for the Proxmox LXC stack is available:"
echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}"
echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}"
echo
@@ -4927,8 +4917,7 @@ create_lxc_container() {
case "${_ans,,}" in
y | yes)
msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)"
apt_update_safe
if $STD apt-get install -y --only-upgrade pve-container lxc-pve; then
if $STD apt-get update && $STD apt-get install -y --only-upgrade pve-container lxc-pve; then
msg_ok "LXC stack upgraded."
if [[ "$do_retry" == "yes" ]]; then
msg_info "Retrying container creation after upgrade"
@@ -4972,6 +4961,7 @@ create_lxc_container() {
exit 205
}
if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then
echo -e "ID '$CTID' is already in use."
unset CTID
msg_error "Cannot use ID that is already in use."
exit 206
@@ -5029,40 +5019,17 @@ create_lxc_container() {
msg_info "Validating storage '$CONTAINER_STORAGE'"
STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1)
if [[ -z "$STORAGE_TYPE" ]]; then
msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg"
exit 213
fi
case "$STORAGE_TYPE" in
iscsidirect)
msg_error "Storage '$CONTAINER_STORAGE' uses iSCSI-direct which does not support container rootfs."
exit 212
;;
iscsi | zfs)
msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) does not support container rootdir content."
exit 213
;;
cephfs)
msg_error "Storage '$CONTAINER_STORAGE' uses CephFS which is not supported for LXC rootfs."
exit 219
;;
pbs)
msg_error "Storage '$CONTAINER_STORAGE' is a Proxmox Backup Server — cannot be used for containers."
exit 224
;;
iscsidirect) exit 212 ;;
iscsi | zfs) exit 213 ;;
cephfs) exit 219 ;;
pbs) exit 224 ;;
linstor | rbd | nfs | cifs)
if ! pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null; then
msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) is not accessible or inactive."
exit 217
fi
pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217
;;
esac
if ! pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE"; then
msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) does not support 'rootdir' content."
exit 213
fi
pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213
msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated"
msg_info "Validating template storage '$TEMPLATE_STORAGE'"
@@ -5135,7 +5102,8 @@ create_lxc_container() {
# If still no template, try to find alternatives
if [[ -z "$TEMPLATE" ]]; then
msg_warn "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..."
echo ""
echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..."
# Get all available versions for this OS type
AVAILABLE_VERSIONS=()
@@ -5409,19 +5377,13 @@ create_lxc_container() {
if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then
msg_info "Template file missing or too small downloading"
rm -f "$TEMPLATE_PATH"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 || {
msg_error "Failed to download template '$TEMPLATE' to storage '$TEMPLATE_STORAGE'"
exit 222
}
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1
msg_ok "Template downloaded"
elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then
if [[ -n "$ONLINE_TEMPLATE" ]]; then
msg_info "Template appears corrupted re-downloading"
rm -f "$TEMPLATE_PATH"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 || {
msg_error "Failed to re-download template '$TEMPLATE'"
exit 222
}
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1
msg_ok "Template re-downloaded"
else
msg_warn "Template appears corrupted, but no online version exists. Skipping re-download."
@@ -5463,17 +5425,20 @@ create_lxc_container() {
if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then
# Local fallback also failed - check for LXC stack version issue
if grep -qiE 'unsupported .* version' "$LOGFILE"; then
msg_warn "pct reported 'unsupported version' LXC stack might be too old for this template"
echo
echo "pct reported 'unsupported ... version' your LXC stack might be too old for this template."
echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically."
offer_lxc_stack_upgrade_and_maybe_retry "yes"
rc=$?
case $rc in
0) : ;; # success - container created, continue
2)
msg_error "Upgrade declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve"
echo "Upgrade was declined. Please update and re-run:
apt update && apt install --only-upgrade pve-container lxc-pve"
exit 231
;;
3)
msg_error "Upgrade and/or retry failed. Please inspect: $LOGFILE"
echo "Upgrade and/or retry failed. Please inspect: $LOGFILE"
exit 231
;;
esac
@@ -5492,17 +5457,20 @@ create_lxc_container() {
else
# Already on local storage and still failed - check LXC stack version
if grep -qiE 'unsupported .* version' "$LOGFILE"; then
msg_warn "pct reported 'unsupported version' LXC stack might be too old for this template"
echo
echo "pct reported 'unsupported ... version' your LXC stack might be too old for this template."
echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically."
offer_lxc_stack_upgrade_and_maybe_retry "yes"
rc=$?
case $rc in
0) : ;; # success - container created, continue
2)
msg_error "Upgrade declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve"
echo "Upgrade was declined. Please update and re-run:
apt update && apt install --only-upgrade pve-container lxc-pve"
exit 231
;;
3)
msg_error "Upgrade and/or retry failed. Please inspect: $LOGFILE"
echo "Upgrade and/or retry failed. Please inspect: $LOGFILE"
exit 231
;;
esac

View File

@@ -276,7 +276,7 @@ shell_check() {
msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell."
echo -e "\nExiting..."
sleep 2
exit 1
exit
fi
}
@@ -293,7 +293,7 @@ root_check() {
msg_error "Please run this script as root."
echo -e "\nExiting..."
sleep 2
exit 1
exit
fi
}
@@ -345,10 +345,11 @@ pve_check() {
# ------------------------------------------------------------------------------
arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
msg_error "This script will not work with PiMox (ARM architecture detected)."
msg_warn "Visit https://github.com/asylumexp/Proxmox for ARM64 support."
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
echo -e "Exiting..."
sleep 2
exit 1
exit
fi
}
@@ -529,9 +530,7 @@ silent() {
if [[ $rc -ne 0 ]]; then
# Source explain_exit_code if needed
if ! declare -f explain_exit_code >/dev/null 2>&1; then
if ! source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func); then
explain_exit_code() { echo "unknown (error_handler.func download failed)"; }
fi
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
fi
local explanation
@@ -552,53 +551,6 @@ silent() {
fi
}
# ------------------------------------------------------------------------------
# apt_update_safe()
#
# - Runs apt-get update with graceful error handling
# - On failure: shows warning with common causes instead of aborting
# - Logs full output to active log file
# - Returns 0 even on failure so the caller can continue
# - Typical cause: enterprise repos returning 401 Unauthorized
#
# Usage:
# apt_update_safe # Warn on failure, continue without aborting
# ------------------------------------------------------------------------------
apt_update_safe() {
local logfile
logfile="$(get_active_logfile)"
local _restore_errexit=false
[[ "$-" == *e* ]] && _restore_errexit=true
set +Eeuo pipefail
trap - ERR
apt-get update >>"$logfile" 2>&1
local rc=$?
if $_restore_errexit; then
set -Eeuo pipefail
trap 'error_handler' ERR
fi
if [[ $rc -ne 0 ]]; then
msg_warn "apt-get update exited with code ${rc} — some repositories may have failed."
# Check log for common 401/403 enterprise repo issues
if grep -qiE '401\s*Unauthorized|403\s*Forbidden|enterprise\.proxmox\.com' "$logfile" 2>/dev/null; then
echo -e "${TAB}${INFO} ${YWB}Hint: Proxmox enterprise repository returned an auth error.${CL}"
echo -e "${TAB} If you don't have a subscription, you can disable the enterprise"
echo -e "${TAB} repo and use the no-subscription repo instead."
fi
echo -e "${TAB}${INFO} ${YWB}Continuing despite partial update failure — packages may still be installable.${CL}"
echo ""
fi
return 0
}
# ------------------------------------------------------------------------------
# spinner()
#
@@ -833,8 +785,8 @@ fatal() {
# ------------------------------------------------------------------------------
exit_script() {
clear
msg_error "User exited script"
exit 0
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
exit
}
# ------------------------------------------------------------------------------
@@ -855,7 +807,6 @@ get_header() {
if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
msg_warn "Failed to download header: $header_url"
return 1
fi
fi
@@ -896,10 +847,10 @@ header_info() {
ensure_tput() {
if ! command -v tput >/dev/null 2>&1; then
if grep -qi 'alpine' /etc/os-release; then
apk add --no-cache ncurses >/dev/null 2>&1 || msg_warn "Failed to install ncurses (tput may be unavailable)"
apk add --no-cache ncurses >/dev/null 2>&1
elif command -v apt-get >/dev/null 2>&1; then
apt-get update -qq >/dev/null
apt-get install -y -qq ncurses-bin >/dev/null 2>&1 || msg_warn "Failed to install ncurses-bin (tput may be unavailable)"
apt-get install -y -qq ncurses-bin >/dev/null 2>&1
fi
fi
}
@@ -1359,7 +1310,6 @@ prompt_select() {
# Validate options
if [[ $num_options -eq 0 ]]; then
msg_warn "prompt_select called with no options"
echo "" >&2
return 1
fi
@@ -1602,30 +1552,22 @@ check_or_create_swap() {
local swap_size_mb
swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60)
if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then
msg_error "Invalid swap size: '${swap_size_mb}' (must be a number in MB)"
msg_error "Invalid size input. Aborting."
return 1
fi
local swap_file="/swapfile"
msg_info "Creating ${swap_size_mb}MB swap file at $swap_file"
if ! dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress; then
msg_error "Failed to allocate swap file (dd failed)"
if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress &&
chmod 600 "$swap_file" &&
mkswap "$swap_file" &&
swapon "$swap_file"; then
msg_ok "Swap file created and activated successfully"
else
msg_error "Failed to create or activate swap"
return 1
fi
if ! chmod 600 "$swap_file"; then
msg_error "Failed to set permissions on $swap_file"
return 1
fi
if ! mkswap "$swap_file"; then
msg_error "Failed to format swap file (mkswap failed)"
return 1
fi
if ! swapon "$swap_file"; then
msg_error "Failed to activate swap (swapon failed)"
return 1
fi
msg_ok "Swap file created and activated successfully"
}
# ------------------------------------------------------------------------------
@@ -1707,7 +1649,7 @@ function get_lxc_ip() {
LOCAL_IP="$(get_current_ip || true)"
if [[ -z "$LOCAL_IP" ]]; then
msg_error "Could not determine LOCAL_IP (checked: eth0, hostname -I, ip route, IPv6 targets)"
msg_error "Could not determine LOCAL_IP"
return 1
fi
fi

View File

@@ -233,7 +233,7 @@ fi
EOF
chmod +x /usr/local/bin/apt-proxy-detect.sh
fi
apt_update_safe
$STD apt-get update
$STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
msg_ok "Updated Container OS"

View File

@@ -201,7 +201,6 @@ install_packages_with_retry() {
fi
done
msg_error "Failed to install packages after $((max_retries + 1)) attempts: ${packages[*]}"
return 1
}
@@ -232,7 +231,6 @@ upgrade_packages_with_retry() {
fi
done
msg_error "Failed to upgrade packages after $((max_retries + 1)) attempts: ${packages[*]}"
return 1
}
@@ -677,7 +675,6 @@ verify_repo_available() {
if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then
return 0
fi
msg_warn "Repository not available: ${repo_url} (suite: ${suite})"
return 1
}
@@ -786,25 +783,16 @@ github_api_call() {
for attempt in $(seq 1 $max_retries); do
local http_code
http_code=$(curl -sSL -w "%{http_code}" -o "$output_file" \
http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${header_args[@]}" \
"$url" 2>/dev/null) || true
"$url" 2>/dev/null || echo "000")
case "$http_code" in
200)
return 0
;;
401)
msg_error "GitHub API authentication failed (HTTP 401)."
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
else
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
fi
return 1
;;
403)
# Rate limit - check if we can retry
if [[ $attempt -lt $max_retries ]]; then
@@ -813,22 +801,11 @@ github_api_call() {
retry_delay=$((retry_delay * 2))
continue
fi
msg_error "GitHub API rate limit exceeded (HTTP 403)."
msg_error "To increase the limit, export a GitHub token before running the script:"
msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\""
msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits."
return 1
;;
404)
msg_error "GitHub repository or release not found (HTTP 404): $url"
return 1
;;
000 | "")
if [[ $attempt -lt $max_retries ]]; then
sleep "$retry_delay"
continue
fi
msg_error "GitHub API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit"
msg_error "GitHub API endpoint not found: $url"
return 1
;;
*)
@@ -836,13 +813,12 @@ github_api_call() {
sleep "$retry_delay"
continue
fi
msg_error "GitHub API call failed (HTTP $http_code)."
msg_error "GitHub API call failed with HTTP $http_code"
return 1
;;
esac
done
msg_error "GitHub API call failed after ${max_retries} attempts: ${url}"
return 1
}
@@ -857,18 +833,14 @@ codeberg_api_call() {
for attempt in $(seq 1 $max_retries); do
local http_code
http_code=$(curl -sSL -w "%{http_code}" -o "$output_file" \
http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \
-H "Accept: application/json" \
"$url" 2>/dev/null) || true
"$url" 2>/dev/null || echo "000")
case "$http_code" in
200)
return 0
;;
401)
msg_error "Codeberg API authentication failed (HTTP 401)."
return 1
;;
403)
# Rate limit - retry
if [[ $attempt -lt $max_retries ]]; then
@@ -877,20 +849,11 @@ codeberg_api_call() {
retry_delay=$((retry_delay * 2))
continue
fi
msg_error "Codeberg API rate limit exceeded (HTTP 403)."
msg_error "Codeberg API rate limit exceeded."
return 1
;;
404)
msg_error "Codeberg repository or release not found (HTTP 404): $url"
return 1
;;
000 | "")
if [[ $attempt -lt $max_retries ]]; then
sleep "$retry_delay"
continue
fi
msg_error "Codeberg API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://codeberg.org"
msg_error "Codeberg API endpoint not found: $url"
return 1
;;
*)
@@ -898,13 +861,12 @@ codeberg_api_call() {
sleep "$retry_delay"
continue
fi
msg_error "Codeberg API call failed (HTTP $http_code)."
msg_error "Codeberg API call failed with HTTP $http_code"
return 1
;;
esac
done
msg_error "Codeberg API call failed after ${max_retries} attempts: ${url}"
return 1
}
@@ -1374,9 +1336,7 @@ setup_deb822_repo() {
[[ -n "$enabled" ]] && echo "Enabled: $enabled"
} >/etc/apt/sources.list.d/${name}.sources
$STD apt update || {
msg_warn "apt update failed after adding repository: ${name}"
}
$STD apt update
}
# ------------------------------------------------------------------------------
@@ -1384,16 +1344,12 @@ setup_deb822_repo() {
# ------------------------------------------------------------------------------
hold_package_version() {
local package="$1"
$STD apt-mark hold "$package" || {
msg_warn "Failed to hold package version: ${package}"
}
$STD apt-mark hold "$package"
}
unhold_package_version() {
local package="$1"
$STD apt-mark unhold "$package" || {
msg_warn "Failed to unhold package version: ${package}"
}
$STD apt-mark unhold "$package"
}
# ------------------------------------------------------------------------------
@@ -1423,7 +1379,6 @@ enable_and_start_service() {
local service="$1"
if ! systemctl enable "$service" &>/dev/null; then
msg_error "Failed to enable service: $service"
return 1
fi
@@ -1466,7 +1421,6 @@ extract_version_from_json() {
version=$(echo "$json" | jq -r ".${field} // empty")
if [[ -z "$version" ]]; then
msg_warn "JSON field '${field}' is empty in API response"
return 1
fi
@@ -1486,7 +1440,6 @@ get_latest_github_release() {
local temp_file=$(mktemp)
if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then
msg_warn "GitHub API call failed for ${repo}"
rm -f "$temp_file"
return 1
fi
@@ -1496,7 +1449,6 @@ get_latest_github_release() {
rm -f "$temp_file"
if [[ -z "$version" ]]; then
msg_error "Could not determine latest version for ${repo}"
return 1
fi
@@ -1513,7 +1465,6 @@ get_latest_codeberg_release() {
# Codeberg API: get all releases and pick the first non-draft/non-prerelease
if ! codeberg_api_call "https://codeberg.org/api/v1/repos/${repo}/releases" "$temp_file"; then
msg_warn "Codeberg API call failed for ${repo}"
rm -f "$temp_file"
return 1
fi
@@ -1529,7 +1480,6 @@ get_latest_codeberg_release() {
rm -f "$temp_file"
if [[ -z "$version" ]]; then
msg_error "Could not determine latest version for ${repo}"
return 1
fi
@@ -1617,34 +1567,13 @@ get_latest_gh_tag() {
"${header_args[@]}" \
"https://api.github.com/repos/${repo}/tags?per_page=100" 2>/dev/null) || true
if [[ "$http_code" == "401" ]]; then
msg_error "GitHub API authentication failed (HTTP 401)."
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
else
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
fi
rm -f /tmp/gh_tags.json
return 1
fi
if [[ "$http_code" == "403" ]]; then
msg_error "GitHub API rate limit exceeded (HTTP 403)."
msg_error "To increase the limit, export a GitHub token before running the script:"
msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\""
rm -f /tmp/gh_tags.json
return 1
fi
if [[ "$http_code" == "000" || -z "$http_code" ]]; then
msg_error "GitHub API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit"
msg_warn "GitHub API rate limit exceeded while fetching tags for ${repo}"
rm -f /tmp/gh_tags.json
return 1
fi
if [[ "$http_code" != "200" ]] || [[ ! -s /tmp/gh_tags.json ]]; then
msg_error "Unable to fetch tags for ${repo} (HTTP ${http_code})"
rm -f /tmp/gh_tags.json
return 1
fi
@@ -1661,7 +1590,6 @@ get_latest_gh_tag() {
sort -V | tail -n1)
if [[ -z "$latest" ]]; then
msg_warn "No matching tags found for ${repo}${prefix:+ (prefix: $prefix)}"
return 1
fi
@@ -1731,15 +1659,6 @@ check_for_gh_release() {
if [[ "$http_code" == "200" ]] && [[ -s /tmp/gh_check.json ]]; then
releases_json="[$(</tmp/gh_check.json)]"
elif [[ "$http_code" == "401" ]]; then
msg_error "GitHub API authentication failed (HTTP 401)."
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
else
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
fi
rm -f /tmp/gh_check.json
return 1
elif [[ "$http_code" == "403" ]]; then
msg_error "GitHub API rate limit exceeded (HTTP 403)."
msg_error "To increase the limit, export a GitHub token before running the script:"
@@ -1760,26 +1679,12 @@ check_for_gh_release() {
if [[ "$http_code" == "200" ]] && [[ -s /tmp/gh_check.json ]]; then
releases_json=$(</tmp/gh_check.json)
elif [[ "$http_code" == "401" ]]; then
msg_error "GitHub API authentication failed (HTTP 401)."
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
else
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
fi
rm -f /tmp/gh_check.json
return 1
elif [[ "$http_code" == "403" ]]; then
msg_error "GitHub API rate limit exceeded (HTTP 403)."
msg_error "To increase the limit, export a GitHub token before running the script:"
msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\""
rm -f /tmp/gh_check.json
return 1
elif [[ "$http_code" == "000" || -z "$http_code" ]]; then
msg_error "GitHub API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit"
rm -f /tmp/gh_check.json
return 1
else
msg_error "Unable to fetch releases for ${app} (HTTP ${http_code})"
rm -f /tmp/gh_check.json
@@ -1897,7 +1802,7 @@ check_for_codeberg_release() {
releases_json=$(curl -fsSL --max-time 20 \
-H 'Accept: application/json' \
"https://codeberg.org/api/v1/repos/${source}/releases" 2>/dev/null) || {
msg_error "Unable to fetch releases for ${app} (codeberg.org/api/v1/repos/${source}/releases)"
msg_error "Unable to fetch releases for ${app}"
return 1
}
@@ -2030,12 +1935,12 @@ function download_with_progress() {
if [[ -z "$content_length" ]]; then
if ! curl -fL# -o "$output" "$url"; then
msg_error "Download failed: $url"
msg_error "Download failed"
return 1
fi
else
if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then
msg_error "Download failed: $url"
msg_error "Download failed"
return 1
fi
fi
@@ -2578,10 +2483,7 @@ _gh_scan_older_releases() {
-H 'Accept: application/vnd.github+json' \
-H 'X-GitHub-Api-Version: 2022-11-28' \
"${header[@]}" \
"https://api.github.com/repos/${repo}/releases?per_page=15" 2>/dev/null) || {
msg_warn "Failed to fetch older releases for ${repo}"
return 1
}
"https://api.github.com/repos/${repo}/releases?per_page=15" 2>/dev/null) || return 1
local count
count=$(echo "$releases_list" | jq 'length')
@@ -2706,22 +2608,12 @@ function fetch_and_deploy_gh_release() {
done
if ! $success; then
if [[ "$http_code" == "401" ]]; then
msg_error "GitHub API authentication failed (HTTP 401)."
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
else
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
fi
elif [[ "$http_code" == "403" ]]; then
if [[ "$http_code" == "403" ]]; then
msg_error "GitHub API rate limit exceeded (HTTP 403)."
msg_error "To increase the limit, export a GitHub token before running the script:"
msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\""
elif [[ "$http_code" == "000" || -z "$http_code" ]]; then
msg_error "GitHub API connection failed (no response)."
msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit"
else
msg_error "Failed to fetch release metadata (HTTP $http_code)"
msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts (HTTP $http_code)"
fi
return 1
fi
@@ -3123,9 +3015,7 @@ function setup_composer() {
# Scenario 1: Already installed - just self-update
if [[ -n "$INSTALLED_VERSION" ]]; then
msg_info "Update Composer $INSTALLED_VERSION"
$STD "$COMPOSER_BIN" self-update --no-interaction || {
msg_warn "Composer self-update failed, continuing with current version"
}
$STD "$COMPOSER_BIN" self-update --no-interaction || true
local UPDATED_VERSION
UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}')
cache_installed_version "composer" "$UPDATED_VERSION"
@@ -3161,9 +3051,7 @@ function setup_composer() {
fi
chmod +x "$COMPOSER_BIN"
$STD "$COMPOSER_BIN" self-update --no-interaction || {
msg_warn "Composer self-update failed after fresh install"
}
$STD "$COMPOSER_BIN" self-update --no-interaction || true
local FINAL_VERSION
FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}')
@@ -4260,18 +4148,6 @@ NVIDIA_PIN
# VA-API for hybrid setups (Intel + NVIDIA)
$STD apt-get -y install va-driver-all vainfo 2>/dev/null || true
# Fix GLX alternatives: nvidia-alternative diverts mesa libs but in LXC
# containers the nvidia GLX libs are typically missing, leaving libGL.so.1
# pointing nowhere. Fall back to mesa if nvidia GLX dir is empty/missing.
if command -v update-glx &>/dev/null; then
local nvidia_glx_dir="/usr/lib/nvidia"
if [[ ! -f "${nvidia_glx_dir}/libGL.so.1" ]] && [[ -d /usr/lib/mesa-diverted ]]; then
msg_info "NVIDIA GLX libs missing in container - falling back to mesa"
$STD update-glx --set glx /usr/lib/mesa-diverted 2>/dev/null || true
ldconfig 2>/dev/null || true
fi
fi
msg_ok "NVIDIA GPU configured"
}
@@ -5246,9 +5122,7 @@ function setup_mysql() {
ensure_apt_working || return 1
# Perform upgrade with retry logic (non-fatal if fails)
upgrade_packages_with_retry "mysql-server" "mysql-client" || {
msg_warn "MySQL package upgrade had issues, continuing with current version"
}
upgrade_packages_with_retry "mysql-server" "mysql-client" || true
cache_installed_version "mysql" "$MYSQL_VERSION"
msg_ok "Update MySQL $MYSQL_VERSION"
@@ -5438,9 +5312,7 @@ function setup_nodejs() {
}
# Force APT cache refresh after repository setup
$STD apt update || {
msg_warn "apt update failed after Node.js repository setup"
}
$STD apt update
ensure_dependencies curl ca-certificates gnupg
@@ -5683,10 +5555,7 @@ EOF
if [[ "$DISTRO_ID" == "ubuntu" ]]; then
# Ubuntu: Use ondrej/php PPA
msg_info "Adding ondrej/php PPA for Ubuntu"
$STD apt install -y software-properties-common || {
msg_error "Failed to install software-properties-common"
return 1
}
$STD apt install -y software-properties-common
# Don't use $STD for add-apt-repository as it uses background processes
add-apt-repository -y ppa:ondrej/php >>"$(get_active_logfile)" 2>&1
else
@@ -5697,9 +5566,7 @@ EOF
}
fi
ensure_apt_working || return 1
$STD apt update || {
msg_warn "apt update failed after PHP repository setup"
}
$STD apt update
# Get available PHP version from repository
local AVAILABLE_PHP_VERSION=""
@@ -5994,9 +5861,7 @@ function setup_postgresql() {
}
fi
$STD systemctl enable --now postgresql 2>/dev/null || {
msg_warn "Failed to enable/start PostgreSQL service"
}
$STD systemctl enable --now postgresql 2>/dev/null || true
# Add PostgreSQL binaries to PATH
if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then
@@ -6010,9 +5875,7 @@ function setup_postgresql() {
if [[ -n "$PG_MODULES" ]]; then
IFS=',' read -ra MODULES <<<"$PG_MODULES"
for module in "${MODULES[@]}"; do
$STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || {
msg_warn "Failed to install PostgreSQL module: ${module}"
}
$STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true
done
fi
}
@@ -6671,9 +6534,7 @@ function setup_clickhouse() {
ensure_apt_working || return 1
# Perform upgrade with retry logic (non-fatal if fails)
upgrade_packages_with_retry "clickhouse-server" "clickhouse-client" || {
msg_warn "ClickHouse package upgrade had issues, continuing with current version"
}
upgrade_packages_with_retry "clickhouse-server" "clickhouse-client" || true
cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION"
msg_ok "Update ClickHouse $CLICKHOUSE_VERSION"
return 0
@@ -6808,9 +6669,7 @@ function setup_rust() {
}
# Update to latest patch version
$STD rustup update "$RUST_TOOLCHAIN" </dev/null || {
msg_warn "Rust toolchain update had issues"
}
$STD rustup update "$RUST_TOOLCHAIN" </dev/null || true
# Ensure PATH is updated for current shell session
export PATH="$CARGO_BIN:$PATH"
@@ -7212,10 +7071,7 @@ function setup_docker() {
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin || {
msg_error "Failed to update Docker packages"
return 1
}
docker-compose-plugin
msg_ok "Updated Docker to $DOCKER_LATEST_VERSION"
else
msg_ok "Docker is up-to-date ($DOCKER_CURRENT_VERSION)"
@@ -7227,10 +7083,7 @@ function setup_docker() {
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin || {
msg_error "Failed to install Docker packages"
return 1
}
docker-compose-plugin
DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1)
msg_ok "Installed Docker $DOCKER_CURRENT_VERSION"

View File

@@ -76,90 +76,70 @@ grep -q "lxc.mount.entry: /dev/net/tun" "$CTID_CONFIG_PATH" || echo "lxc.mount.e
header_info
msg_info "Installing Tailscale in CT $CTID"
pct exec "$CTID" -- sh -c '
pct exec "$CTID" -- bash -c '
set -e
export DEBIAN_FRONTEND=noninteractive
# Detect OS inside container
if [ -f /etc/alpine-release ]; then
# ── Alpine Linux ──
echo "[INFO] Alpine Linux detected, installing Tailscale via apk..."
# Source os-release properly (handles quoted values)
source /etc/os-release
# Enable community repo if not already enabled
if ! grep -q "^[^#].*community" /etc/apk/repositories 2>/dev/null; then
ALPINE_VERSION=$(cat /etc/alpine-release | cut -d. -f1,2)
echo "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/community" >> /etc/apk/repositories
fi
# Fallback if DNS is poisoned or blocked
ORIG_RESOLV="/etc/resolv.conf"
BACKUP_RESOLV="/tmp/resolv.conf.backup"
apk update
apk add --no-cache tailscale
# Enable and start Tailscale service
rc-update add tailscale default 2>/dev/null || true
rc-service tailscale start 2>/dev/null || true
else
# ── Debian / Ubuntu ──
export DEBIAN_FRONTEND=noninteractive
# Source os-release properly (handles quoted values)
. /etc/os-release
# Fallback if DNS is poisoned or blocked
ORIG_RESOLV="/etc/resolv.conf"
BACKUP_RESOLV="/tmp/resolv.conf.backup"
# Check DNS resolution using multiple methods (dig may not be installed)
dns_check_failed=true
if command -v dig >/dev/null 2>&1; then
if dig +short pkgs.tailscale.com 2>/dev/null | grep -qvE "^127\.|^0\.0\.0\.0$|^$"; then
dns_check_failed=false
fi
elif command -v host >/dev/null 2>&1; then
if host pkgs.tailscale.com 2>/dev/null | grep -q "has address"; then
dns_check_failed=false
fi
elif command -v nslookup >/dev/null 2>&1; then
if nslookup pkgs.tailscale.com 2>/dev/null | grep -q "Address:"; then
dns_check_failed=false
fi
elif command -v getent >/dev/null 2>&1; then
if getent hosts pkgs.tailscale.com >/dev/null 2>&1; then
dns_check_failed=false
fi
else
# No DNS tools available, try curl directly and assume DNS works
# Check DNS resolution using multiple methods (dig may not be installed)
dns_check_failed=true
if command -v dig &>/dev/null; then
if dig +short pkgs.tailscale.com 2>/dev/null | grep -qvE "^127\.|^0\.0\.0\.0$|^$"; then
dns_check_failed=false
fi
if $dns_check_failed; then
echo "[INFO] DNS resolution for pkgs.tailscale.com failed (blocked or redirected)."
echo "[INFO] Temporarily overriding /etc/resolv.conf with Cloudflare DNS (1.1.1.1)"
cp "$ORIG_RESOLV" "$BACKUP_RESOLV"
echo "nameserver 1.1.1.1" >"$ORIG_RESOLV"
elif command -v host &>/dev/null; then
if host pkgs.tailscale.com 2>/dev/null | grep -q "has address"; then
dns_check_failed=false
fi
if ! command -v curl >/dev/null 2>&1; then
echo "[INFO] curl not found, installing..."
apt-get update -qq
apt-get install -y curl >/dev/null
elif command -v nslookup &>/dev/null; then
if nslookup pkgs.tailscale.com 2>/dev/null | grep -q "Address:"; then
dns_check_failed=false
fi
elif command -v getent &>/dev/null; then
if getent hosts pkgs.tailscale.com &>/dev/null; then
dns_check_failed=false
fi
else
# No DNS tools available, try curl directly and assume DNS works
dns_check_failed=false
fi
# Ensure keyrings directory exists
mkdir -p /usr/share/keyrings
curl -fsSL "https://pkgs.tailscale.com/stable/${ID}/${VERSION_CODENAME}.noarmor.gpg" \
| tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/${ID} ${VERSION_CODENAME} main" \
>/etc/apt/sources.list.d/tailscale.list
if $dns_check_failed; then
echo "[INFO] DNS resolution for pkgs.tailscale.com failed (blocked or redirected)."
echo "[INFO] Temporarily overriding /etc/resolv.conf with Cloudflare DNS (1.1.1.1)"
cp "$ORIG_RESOLV" "$BACKUP_RESOLV"
echo "nameserver 1.1.1.1" >"$ORIG_RESOLV"
fi
if ! command -v curl &>/dev/null; then
echo "[INFO] curl not found, installing..."
apt-get update -qq
apt-get install -y tailscale >/dev/null
apt update -qq
apt install -y curl >/dev/null
fi
if [ -f /tmp/resolv.conf.backup ]; then
echo "[INFO] Restoring original /etc/resolv.conf"
mv /tmp/resolv.conf.backup /etc/resolv.conf
fi
# Ensure keyrings directory exists
mkdir -p /usr/share/keyrings
curl -fsSL "https://pkgs.tailscale.com/stable/${ID}/${VERSION_CODENAME}.noarmor.gpg" \
| tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/${ID} ${VERSION_CODENAME} main" \
>/etc/apt/sources.list.d/tailscale.list
apt-get update -qq
apt update -qq
apt install -y tailscale >/dev/null
if [[ -f /tmp/resolv.conf.backup ]]; then
echo "[INFO] Restoring original /etc/resolv.conf"
mv /tmp/resolv.conf.backup /etc/resolv.conf
fi
'

View File

@@ -7,12 +7,8 @@
if ! command -v curl &>/dev/null; then
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
if [[ -f "/etc/alpine-release" ]]; then
apk -U add curl >/dev/null 2>&1
else
apt-get update >/dev/null 2>&1
apt-get install -y curl >/dev/null 2>&1
fi
apt-get update >/dev/null 2>&1
apt-get install -y curl >/dev/null 2>&1
fi
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
@@ -55,7 +51,7 @@ EOF
# HELPER FUNCTIONS
# ==============================================================================
get_ip() {
ifconfig | grep -v '127.0.0.1' | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -m1 -Eo '([0-9]*\.){3}[0-9]*' || echo "127.0.0.1"
hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1"
}
# ==============================================================================
@@ -72,16 +68,6 @@ else
exit 1
fi
# ==============================================================================
# DEPENDENCY CHECK
# ==============================================================================
if ! command -v jq &>/dev/null; then
printf "\r\e[2K%b" '\033[93m Installing jq \033[m' >&2
if [[ "$OS" == "Alpine" ]]; then
apk -U add jq >/dev/null 2>&1
fi
fi
# ==============================================================================
# UNINSTALL
# ==============================================================================

View File

@@ -288,8 +288,8 @@ function default_settings() {
echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}"
if ! ip link show "${BRG}" &>/dev/null; then
msg_error "Bridge '${BRG}' does not exist"
if ! grep -q "^iface ${BRG}" /etc/network/interfaces; then
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces"
exit
else
echo -e "${DGN}Using LAN Bridge: ${BGN}${BRG}${CL}"
@@ -305,8 +305,8 @@ function default_settings() {
if [ "$NETWORK_MODE" = "dual" ]; then
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
if ! ip link show "${WAN_BRG}" &>/dev/null; then
msg_error "Bridge '${WAN_BRG}' does not exist"
if ! grep -q "^iface ${WAN_BRG}" /etc/network/interfaces; then
msg_error "Bridge '${WAN_BRG}' does not exist in /etc/network/interfaces"
exit
else
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
@@ -424,8 +424,8 @@ function advanced_settings() {
if [ -z $BRG ]; then
BRG="vmbr0"
fi
if ! ip link show "${BRG}" &>/dev/null; then
msg_error "Bridge '${BRG}' does not exist"
if ! grep -q "^iface ${BRG}" /etc/network/interfaces; then
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces"
exit
fi
echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}"
@@ -474,8 +474,8 @@ function advanced_settings() {
if [ -z $WAN_BRG ]; then
WAN_BRG="vmbr1"
fi
if ! ip link show "${WAN_BRG}" &>/dev/null; then
msg_error "WAN Bridge '${WAN_BRG}' does not exist"
if ! grep -q "^iface ${WAN_BRG}" /etc/network/interfaces; then
msg_error "WAN Bridge '${WAN_BRG}' does not exist in /etc/network/interfaces"
exit
fi
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"

View File

@@ -88,7 +88,7 @@ function truenas_iso_lookup() {
curl -sL "$BASE_URL" |
grep -oE 'href="[^"]+\.iso"' |
sed 's/href="//; s/"$//' |
grep -vE '(MASTER|ALPHA)' |
grep -vE '(nightly|ALPHA)' |
grep -E "$year_pattern"
)