Compare commits

..

1 Commits

Author SHA1 Message Date
MickLesk f3b7b64f97 build.func: fix pveam arch filter for extra whitespace column 2026-08-06 20:57:47 +02:00
40 changed files with 251 additions and 1622 deletions
-139
View File
@@ -1,142 +1,3 @@
## 2026-08-08
### 🆕 New Scripts
- Ignis ([#16330](https://github.com/community-scripts/ProxmoxVE/pull/16330))
### 💾 Core
- #### ✨ New Features
- core - setup_nodejs: bypass npm allowScripts policy globally on npm >=11 [@MickLesk](https://github.com/MickLesk) ([#16280](https://github.com/community-scripts/ProxmoxVE/pull/16280))
## 2026-08-07
### 🆕 New Scripts
- NetworkOptimizer ([#16328](https://github.com/community-scripts/ProxmoxVE/pull/16328))
- Orb ([#16329](https://github.com/community-scripts/ProxmoxVE/pull/16329))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- HortusFox: start applying db migrations [@tomfrenzel](https://github.com/tomfrenzel) ([#16324](https://github.com/community-scripts/ProxmoxVE/pull/16324))
- Docker: report container updates instead of prompting, stop auto-updating Portainer Agent, fix addon detection of legacy Portainer [@MickLesk](https://github.com/MickLesk) ([#16298](https://github.com/community-scripts/ProxmoxVE/pull/16298))
- Immich: retry mise install on transient npm download failures [@MickLesk](https://github.com/MickLesk) ([#16301](https://github.com/community-scripts/ProxmoxVE/pull/16301))
- changedetection: remove msttcorefonts to fix install hang [@austinpilz](https://github.com/austinpilz) ([#16319](https://github.com/community-scripts/ProxmoxVE/pull/16319))
- #### 🔧 Refactor
- core: extend new vars and pocketbase values [@MickLesk](https://github.com/MickLesk) ([#16313](https://github.com/community-scripts/ProxmoxVE/pull/16313))
## 2026-08-06
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- paperless: change update abort message (endless spinner) [@MickLesk](https://github.com/MickLesk) ([#16299](https://github.com/community-scripts/ProxmoxVE/pull/16299))
- #### ✨ New Features
- Vikunja: remove version pin, v2.5.0 fixes the systemd syscall filter crash [@MickLesk](https://github.com/MickLesk) ([#16300](https://github.com/community-scripts/ProxmoxVE/pull/16300))
### 💾 Core
- #### 🔧 Refactor
- build.func: filter templates by host architecture during search [@MickLesk](https://github.com/MickLesk) ([#16302](https://github.com/community-scripts/ProxmoxVE/pull/16302))
## 2026-08-05
### 🚀 Updated Scripts
- #### ✨ New Features
- NextcloudPI: Bump to Debian Trixie & Tweak broken SSH [@MickLesk](https://github.com/MickLesk) ([#15957](https://github.com/community-scripts/ProxmoxVE/pull/15957))
### 🧰 Tools
- #### 🐞 Bug Fixes
- fix(monitor-all): reduce polling CPU overhead [@jomplox](https://github.com/jomplox) ([#15907](https://github.com/community-scripts/ProxmoxVE/pull/15907))
## 2026-08-04
### 🆕 New Scripts
- Hister ([#16259](https://github.com/community-scripts/ProxmoxVE/pull/16259))
- Obsidian-LiveSync ([#16233](https://github.com/community-scripts/ProxmoxVE/pull/16233))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Actual Budget: allow native module install scripts under npm 11 (Node24) [@MickLesk](https://github.com/MickLesk) ([#16277](https://github.com/community-scripts/ProxmoxVE/pull/16277))
- Heimdall-Dashboard: run full composer install on update, force production env [@MickLesk](https://github.com/MickLesk) ([#16278](https://github.com/community-scripts/ProxmoxVE/pull/16278))
- xyOps: rebuild xySat satellite during update [@MickLesk](https://github.com/MickLesk) ([#16279](https://github.com/community-scripts/ProxmoxVE/pull/16279))
- HAOS: remove non-ASCII dash from CPU model dialog text [@MickLesk](https://github.com/MickLesk) ([#16276](https://github.com/community-scripts/ProxmoxVE/pull/16276))
- fix: back up Termix db/data so the update stops destroying encryption keys [@LukeGus](https://github.com/LukeGus) ([#16275](https://github.com/community-scripts/ProxmoxVE/pull/16275))
- passwordpusher: dynamically read Ruby version from .ruby-version file [@Copilot](https://github.com/Copilot) ([#16242](https://github.com/community-scripts/ProxmoxVE/pull/16242))
- fix(salt): write version cache to ~/.salt, not /~.salt [@TowyTowy](https://github.com/TowyTowy) ([#16265](https://github.com/community-scripts/ProxmoxVE/pull/16265))
- AFFiNE: fix version and build type reported by the app itself [@MickLesk](https://github.com/MickLesk) ([#16251](https://github.com/community-scripts/ProxmoxVE/pull/16251))
- Passbolt: fetch GPG key from Passbolt's own host instead of the flaky keyserver [@MickLesk](https://github.com/MickLesk) ([#16248](https://github.com/community-scripts/ProxmoxVE/pull/16248))
- #### ✨ New Features
- Opencloud: Pin version to 7.4.0 [@vhsdream](https://github.com/vhsdream) ([#16257](https://github.com/community-scripts/ProxmoxVE/pull/16257))
- #### 🔧 Refactor
- Bump NodeJS Versions of several scripts [@MickLesk](https://github.com/MickLesk) ([#16230](https://github.com/community-scripts/ProxmoxVE/pull/16230))
### 💾 Core
- #### 🐞 Bug Fixes
- tools.func: never delete ubuntu.sources (or other distro base sources) as orphaned [@MickLesk](https://github.com/MickLesk) ([#16250](https://github.com/community-scripts/ProxmoxVE/pull/16250))
- tools.func: complete a stale/partial backup manifest instead of trusting it as-is [@MickLesk](https://github.com/MickLesk) ([#16252](https://github.com/community-scripts/ProxmoxVE/pull/16252))
- #### 🔧 Refactor
- Refactor: harmonize tools/addon and move boilerplate into core.func [@MickLesk](https://github.com/MickLesk) ([#16202](https://github.com/community-scripts/ProxmoxVE/pull/16202))
## 2026-08-03
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Correction to link in Paperless-NGX update script [@jsspen](https://github.com/jsspen) ([#16241](https://github.com/community-scripts/ProxmoxVE/pull/16241))
- fix restore env in split pro [@johanngrobe](https://github.com/johanngrobe) ([#16228](https://github.com/community-scripts/ProxmoxVE/pull/16228))
### 💾 Core
- #### 🔧 Refactor
- core: read all interactive prompts from /dev/tty, drain leftover input on read failure (hotfix) [@MickLesk](https://github.com/MickLesk) ([#16235](https://github.com/community-scripts/ProxmoxVE/pull/16235))
## 2026-08-02
### 🆕 New Scripts
- Portainer as Addon [@MickLesk](https://github.com/MickLesk) ([#16200](https://github.com/community-scripts/ProxmoxVE/pull/16200))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- LibreNMS: Ensure ipmitool dependency is installed [@tremor021](https://github.com/tremor021) ([#16219](https://github.com/community-scripts/ProxmoxVE/pull/16219))
- Bitfocus Companion: add libatomic1 dependency [@tremor021](https://github.com/tremor021) ([#16212](https://github.com/community-scripts/ProxmoxVE/pull/16212))
- fix(dispatcharr): add recordings and dvr queues to celery worker [@eXistC](https://github.com/eXistC) ([#16204](https://github.com/community-scripts/ProxmoxVE/pull/16204))
- add retry for uv - same as immich [@asylumexp](https://github.com/asylumexp) ([#16203](https://github.com/community-scripts/ProxmoxVE/pull/16203))
- Fix for cross-seed node version regression [@TorinFrancis](https://github.com/TorinFrancis) ([#16209](https://github.com/community-scripts/ProxmoxVE/pull/16209))
- #### 🔧 Refactor
- chore: remove portainer setup from several scripts (moved to addon) [@MickLesk](https://github.com/MickLesk) ([#16201](https://github.com/community-scripts/ProxmoxVE/pull/16201))
## 2026-08-01
### 🚀 Updated Scripts
+2 -18
View File
@@ -314,17 +314,12 @@ jobs:
// ── Allow-lists (mirror the slash bot) ─────────────────────────────
const ALLOWED_FIELDS = {
name: 'string', description: 'string', logo: 'string', documentation: 'string',
website: 'string', project_url: 'string', repository: 'string', config_path: 'string',
website: 'string', project_url: 'string', github: 'string', config_path: 'string',
tags: 'string', port: 'number', default_user: 'nullable_string', default_passwd: 'nullable_string',
unprivileged: 'number', updateable: 'boolean', privileged: 'boolean',
architectures: 'select_list', platforms: 'select_list',
unprivileged: 'number', updateable: 'boolean', privileged: 'boolean', has_arm: 'boolean',
is_dev: 'boolean', is_disabled: 'boolean', disable_message: 'string',
is_deleted: 'boolean', deleted_message: 'string'
};
const SELECT_VALUES = {
architectures: ['amd64', 'arm64'],
platforms: ['pve', 'incus'],
};
const FIELD_TO_CT_VAR = { tags: 'var_tags', unprivileged: 'var_unprivileged' };
const RESOURCE_KEYS = { cpu: 'number', ram: 'number', hdd: 'number', os: 'string', version: 'string' };
const METHOD_KEYS = { config_path: 'string', script: 'string' };
@@ -344,17 +339,6 @@ jobs:
if (isNaN(n)) return { error: '`' + key + '` must be a number' };
return { value: n };
}
if (type === 'select_list') {
// architectures/platforms are multi-selects over a closed set,
// so an unknown value is rejected rather than stored. The model
// may hand back an array or a comma-separated string.
const allowed = SELECT_VALUES[key] || [];
const raw = Array.isArray(rawVal) ? rawVal : String(rawVal == null ? '' : rawVal).split(',');
const values = raw.map(function (v) { return String(v).trim(); }).filter(Boolean);
const bad = values.filter(function (v) { return allowed.indexOf(v) === -1; });
if (bad.length > 0) return { error: '`' + key + '` accepts ' + allowed.join(', ') + ' — got: ' + bad.join(', ') };
return { value: values };
}
if (type === 'nullable_string') return { value: rawVal === '' || rawVal == null ? null : String(rawVal) };
return { value: String(rawVal) };
}
+6 -25
View File
@@ -323,10 +323,9 @@ jobs:
'/pocketbase <slug> method remove <type>\n' +
'```\n' +
'Method fields: `cpu` `ram` `hdd` `os` `version` `config_path` `script`\n\n' +
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `repository` ' +
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` ' +
'`config_path` `port` `default_user` `default_passwd` ' +
'`updateable` `privileged` `is_dev` ' +
'`architectures` (amd64,arm64) `platforms` (pve,incus) ' +
'`updateable` `privileged` `has_arm` `is_dev` ' +
'`is_disabled` `disable_message` `is_deleted` `deleted_message`';
if (!withoutCmd) {
@@ -505,8 +504,7 @@ jobs:
out.push('- **Port:** ' + (record.port != null ? '`' + record.port + '`' : '—'));
out.push('- **Updateable:** ' + (record.updateable ? 'Yes' : 'No'));
out.push('- **Privileged:** ' + (record.privileged ? 'Yes' : 'No'));
out.push('- **Architectures:** ' + ((record.architectures || []).join(', ') || 'amd64'));
out.push('- **Platforms:** ' + ((record.platforms || []).join(', ') || 'pve'));
out.push('- **ARM:** ' + (record.has_arm ? 'Yes' : 'No'));
if (record.is_dev) out.push('- **Dev:** Yes');
if (record.is_disabled) out.push('- **Disabled:** Yes' + (record.disable_message ? ' — ' + record.disable_message : ''));
if (record.is_deleted) out.push('- **Deleted:** Yes' + (record.deleted_message ? ' — ' + record.deleted_message : ''));
@@ -840,7 +838,7 @@ jobs:
const fieldName = setMatch[1].toLowerCase();
const SET_ALLOWED = {
name: 'string', description: 'string', logo: 'string',
documentation: 'string', website: 'string', project_url: 'string', repository: 'string',
documentation: 'string', website: 'string', project_url: 'string', github: 'string',
config_path: 'string', disable_message: 'string', deleted_message: 'string'
};
if (!SET_ALLOWED[fieldName]) {
@@ -890,7 +888,7 @@ jobs:
documentation: 'string',
website: 'string',
project_url: 'string',
repository: 'string',
github: 'string',
config_path: 'string',
tags: 'string',
port: 'number',
@@ -899,8 +897,7 @@ jobs:
unprivileged: 'number',
updateable: 'boolean',
privileged: 'boolean',
architectures: 'select_list',
platforms: 'select_list',
has_arm: 'boolean',
is_dev: 'boolean',
is_disabled: 'boolean',
disable_message: 'string',
@@ -927,10 +924,6 @@ jobs:
}
// Cast values to correct types
const SELECT_VALUES = {
architectures: ['amd64', 'arm64'],
platforms: ['pve', 'incus'],
};
const payload = {};
for (const [key, rawVal] of Object.entries(parsedFields)) {
const type = ALLOWED_FIELDS[key];
@@ -950,18 +943,6 @@ jobs:
process.exit(0);
}
payload[key] = n;
} else if (type === 'select_list') {
// architectures/platforms are multi-selects over a closed set,
// so an unknown value is rejected rather than stored.
const allowed = SELECT_VALUES[key] || [];
const values = rawVal.split(',').map(function (v) { return v.trim(); }).filter(Boolean);
const bad = values.filter(function (v) { return allowed.indexOf(v) === -1; });
if (bad.length > 0) {
await addReaction('-1');
await postComment('❌ **PocketBase Bot**: `' + key + '` accepts ' + allowed.join(', ') + ' — got: `' + bad.join(', ') + '`');
process.exit(0);
}
payload[key] = values;
} else if (type === 'nullable_string') {
payload[key] = rawVal === '' ? null : rawVal;
} else {
+1 -13
View File
@@ -179,19 +179,7 @@ jobs:
if (resolvedType) payload.type = resolvedType;
var resolvedCats = (data.categories || []).map(function(n) { return categoryNameToPbId[categoryIdToName[n]]; }).filter(Boolean);
if (resolvedCats.length) payload.categories = resolvedCats;
// architectures replaces has_arm: a boolean could say "also ARM" but
// not "ARM only" or "amd64 only". A record written before the field
// existed is read through the old key so nothing silently drops.
if (Array.isArray(data.architectures)) {
payload.architectures = data.architectures.filter(function (a) { return a === 'amd64' || a === 'arm64'; });
} else if (data.has_arm !== undefined) {
payload.architectures = (data.has_arm === true || data.has_arm === 'true') ? ['amd64', 'arm64'] : ['amd64'];
}
if (Array.isArray(data.platforms)) {
payload.platforms = data.platforms.filter(function (p) { return p === 'pve' || p === 'incus'; });
}
if (Array.isArray(data.app_vars)) payload.app_vars = data.app_vars;
if (data.repository !== undefined) payload.repository = data.repository;
if (data.has_arm !== undefined) payload.has_arm = data.has_arm === true || data.has_arm === 'true';
if (data.version !== undefined) payload.version = data.version;
if (data.changelog !== undefined) payload.changelog = data.changelog;
if (data.screenshots !== undefined) payload.screenshots = data.screenshots;
+92 -66
View File
@@ -86,9 +86,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
@@ -102,7 +99,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
<details>
<summary><h4>August (8 entries)</h4></summary>
<summary><h4>August (1 entries)</h4></summary>
[View August 2026 Changelog](.github/changelogs/2026/08.md)
@@ -521,67 +518,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-08-10
### 🧰 Tools
- post-pdm-install ([#16368](https://github.com/community-scripts/ProxmoxVE/pull/16368))
## 2026-08-09
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix: CouchDB Erlang cookie breaks vm.args (use hex) [@fudianchn](https://github.com/fudianchn) ([#16361](https://github.com/community-scripts/ProxmoxVE/pull/16361))
- SparkyFitness: Install Git for frontend builds [@MickLesk](https://github.com/MickLesk) ([#16352](https://github.com/community-scripts/ProxmoxVE/pull/16352))
- Byparr: Use invisible-playwright browser setup [@MickLesk](https://github.com/MickLesk) ([#16353](https://github.com/community-scripts/ProxmoxVE/pull/16353))
- Crafty Controller: Restore ownership after updates [@MickLesk](https://github.com/MickLesk) ([#16354](https://github.com/community-scripts/ProxmoxVE/pull/16354))
- Webtrees: Fix schema check in silent mode [@MickLesk](https://github.com/MickLesk) ([#16355](https://github.com/community-scripts/ProxmoxVE/pull/16355))
- SigNoz: Allow legacy metadata schema on ClickHouse 26 [@MickLesk](https://github.com/MickLesk) ([#16356](https://github.com/community-scripts/ProxmoxVE/pull/16356))
### 🧰 Tools
- #### ✨ New Features
- Portainer: Update existing CE and BE installations [@MickLesk](https://github.com/MickLesk) ([#16357](https://github.com/community-scripts/ProxmoxVE/pull/16357))
### 📚 Documentation
- github: update Star History links and image sources [@MickLesk](https://github.com/MickLesk) ([#16367](https://github.com/community-scripts/ProxmoxVE/pull/16367))
## 2026-08-08
### 🆕 New Scripts
- Ignis ([#16330](https://github.com/community-scripts/ProxmoxVE/pull/16330))
### 💾 Core
- #### ✨ New Features
- core - setup_nodejs: bypass npm allowScripts policy globally on npm >=11 [@MickLesk](https://github.com/MickLesk) ([#16280](https://github.com/community-scripts/ProxmoxVE/pull/16280))
## 2026-08-07
### 🆕 New Scripts
- NetworkOptimizer ([#16328](https://github.com/community-scripts/ProxmoxVE/pull/16328))
- Orb ([#16329](https://github.com/community-scripts/ProxmoxVE/pull/16329))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- HortusFox: start applying db migrations [@tomfrenzel](https://github.com/tomfrenzel) ([#16324](https://github.com/community-scripts/ProxmoxVE/pull/16324))
- Docker: report container updates instead of prompting, stop auto-updating Portainer Agent, fix addon detection of legacy Portainer [@MickLesk](https://github.com/MickLesk) ([#16298](https://github.com/community-scripts/ProxmoxVE/pull/16298))
- Immich: retry mise install on transient npm download failures [@MickLesk](https://github.com/MickLesk) ([#16301](https://github.com/community-scripts/ProxmoxVE/pull/16301))
- changedetection: remove msttcorefonts to fix install hang [@austinpilz](https://github.com/austinpilz) ([#16319](https://github.com/community-scripts/ProxmoxVE/pull/16319))
- #### 🔧 Refactor
- core: extend new vars and pocketbase values [@MickLesk](https://github.com/MickLesk) ([#16313](https://github.com/community-scripts/ProxmoxVE/pull/16313))
## 2026-08-06
### 🚀 Updated Scripts
@@ -1198,4 +1134,94 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Adapt to new artifact filename format for pocket id [@wollew](https://github.com/wollew) ([#15689](https://github.com/community-scripts/ProxmoxVE/pull/15689))
- Fireshare: Fix for install and upgrade to v1.7.3 [@tremor021](https://github.com/tremor021) ([#15673](https://github.com/community-scripts/ProxmoxVE/pull/15673))
- Endurain: Fix update procedure [@tremor021](https://github.com/tremor021) ([#15674](https://github.com/community-scripts/ProxmoxVE/pull/15674))
- Endurain: Fix update procedure [@tremor021](https://github.com/tremor021) ([#15674](https://github.com/community-scripts/ProxmoxVE/pull/15674))
## 2026-07-09
### 🚀 Updated Scripts
- fix(pihole): repair Unbound DNS-over-TLS (DoT) forwarding config [@TowyTowy](https://github.com/TowyTowy) ([#15654](https://github.com/community-scripts/ProxmoxVE/pull/15654))
## 2026-07-08
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Opencloud: Bump version to 7.2.1 [@vhsdream](https://github.com/vhsdream) ([#15655](https://github.com/community-scripts/ProxmoxVE/pull/15655))
- BabyBuddy: Harden update script [@MickLesk](https://github.com/MickLesk) ([#15642](https://github.com/community-scripts/ProxmoxVE/pull/15642))
## 2026-07-07
### 🆕 New Scripts
- Forgejo-Runner ([#15046](https://github.com/community-scripts/ProxmoxVE/pull/15046))
## 2026-07-06
### 🚀 Updated Scripts
- Fix alignment in various ct end messages [@tremor021](https://github.com/tremor021) ([#15632](https://github.com/community-scripts/ProxmoxVE/pull/15632))
- Immich: Update libvips to 8.18.4 [@vhsdream](https://github.com/vhsdream) ([#15619](https://github.com/community-scripts/ProxmoxVE/pull/15619))
- #### 🐞 Bug Fixes
- Wizarr: Build JS and CSS static assets [@vhsdream](https://github.com/vhsdream) ([#15634](https://github.com/community-scripts/ProxmoxVE/pull/15634))
- RustDesk Server: Update URL format in rustdeskserver.sh [@tremor021](https://github.com/tremor021) ([#15626](https://github.com/community-scripts/ProxmoxVE/pull/15626))
- attempt to port docker-vm to support arm64 [@asylumexp](https://github.com/asylumexp) ([#15611](https://github.com/community-scripts/ProxmoxVE/pull/15611))
- fix(plane): don't clobber global app var, breaking /usr/bin/update [@asylumexp](https://github.com/asylumexp) ([#15612](https://github.com/community-scripts/ProxmoxVE/pull/15612))
- #### 🔧 Refactor
- cliproxyapi: point setup message at /management.html [@austinpilz](https://github.com/austinpilz) ([#15628](https://github.com/community-scripts/ProxmoxVE/pull/15628))
### 🗑️ Deleted Scripts
- Remove: FlowiseAI [@MickLesk](https://github.com/MickLesk) ([#15624](https://github.com/community-scripts/ProxmoxVE/pull/15624))
## 2026-07-05
### 🆕 New Scripts
- excalidash ([#15604](https://github.com/community-scripts/ProxmoxVE/pull/15604))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix: homarr: cli [@CrazyWolf13](https://github.com/CrazyWolf13) ([#15603](https://github.com/community-scripts/ProxmoxVE/pull/15603))
- immich: vacuum smart_search/face_search before VectorChord bump [@irishpadres](https://github.com/irishpadres) ([#15607](https://github.com/community-scripts/ProxmoxVE/pull/15607))
## 2026-07-04
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Element Synapse: Fix heredoc syntax in elementsynapse-install.sh [@tremor021](https://github.com/tremor021) ([#15594](https://github.com/community-scripts/ProxmoxVE/pull/15594))
- Twenty: Fix backup restore [@tremor021](https://github.com/tremor021) ([#15586](https://github.com/community-scripts/ProxmoxVE/pull/15586))
- fix(endurain): update frontend dist path after upstream restructure [@joaovitoriasilva](https://github.com/joaovitoriasilva) ([#15590](https://github.com/community-scripts/ProxmoxVE/pull/15590))
## 2026-07-03
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: handle mise monorepo_root rename correctly | bump to 3.0.1 [@MickLesk](https://github.com/MickLesk) ([#15557](https://github.com/community-scripts/ProxmoxVE/pull/15557))
- #### ✨ New Features
- Opencloud: bump to v7.2.0 [@vhsdream](https://github.com/vhsdream) ([#15575](https://github.com/community-scripts/ProxmoxVE/pull/15575))
### 💾 Core
- #### 🔧 Refactor
- tools.func: fix corepack/pnpm install flow in setup_nodejs [@MickLesk](https://github.com/MickLesk) ([#15579](https://github.com/community-scripts/ProxmoxVE/pull/15579))
### 🧰 Tools
- #### 🐞 Bug Fixes
- Removed deprecated parameter in Filebrowser Quantum configuration [@alpargatagazer](https://github.com/alpargatagazer) ([#15573](https://github.com/community-scripts/ProxmoxVE/pull/15573))
-49
View File
@@ -103,55 +103,6 @@ Key rules at a glance:
- Quote all variables: `"$VAR"` not `$VAR`
- Use lowercase variable names
- Do not hardcode credentials or sensitive values
- Never prompt without an escape hatch — see below
### Answering a prompt up front
An install script that only asks cannot be deployed unattended. Read the
variable first and prompt only when it is unset:
```bash
if [[ -z "${var_admin_user:-}" ]]; then
read -r -p "${TAB3}Admin username: " var_admin_user
fi
var_admin_user="${var_admin_user:-admin}"
```
Name them `var_<something>`, the same namespace the container variables use.
`install/forgejo-runner-install.sh`, `install/pangolin-install.sh`, and `install/docker-install.sh` all
follow this.
The variable also has to be exported from `ct/<app>.sh`, or it never reaches
the container — `lxc-attach` carries the caller's environment, but only for
what was actually exported:
```bash
export var_admin_user="${var_admin_user:-}"
```
Declare them on the script's PocketBase record in `app_vars` so the website's
generator can offer them as fields:
```json
"app_vars": [
{
"name": "var_admin_user",
"label": "Admin Username",
"type": "text",
"default": "admin"
},
{
"name": "var_admin_pass",
"label": "Admin Password",
"type": "password",
"secret": true
}
]
```
`type` is one of `text`, `password`, `number`, `boolean` (`yes`/`no`) or
`select` (with `options`). Mark anything credential-like `secret` — the
generator keeps those out of shareable links and out of the on-screen summary.
Full standards and examples: **[community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution)**
+4 -14
View File
@@ -171,21 +171,11 @@ This project runs on community contributions. Whether you want to write new scri
</p>
<p align="center">
<a href="https://www.star-history.com/?repos=community-scripts%2FProxmoxVE&type=date&legend=top-left">
<a href="https://star-history.com/#community-scripts/ProxmoxVE&Date">
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="https://api.star-history.com/chart?repos=community-scripts/ProxmoxVE&type=date&theme=dark&legend=top-left&sealed_token=-lUy2KH6pJryUyLCbiWRwFz3ilXMDFVFHm0lJcqh-Hl5e0QNNKjINNcXzULhaZVpBugN_TDaxOawEAuzwBc5Sjqa2mCRw9K6TdCioKRDsS_qXUwhkLQSCMRSg1schCdl4rQD8qEj7PWBjin-fyHnDm7NnM6akZp59jndG7qV7PrvG8nLBgDIuuA-wAI3"
/>
<source
media="(prefers-color-scheme: light)"
srcset="https://api.star-history.com/chart?repos=community-scripts/ProxmoxVE&type=date&legend=top-left&sealed_token=-lUy2KH6pJryUyLCbiWRwFz3ilXMDFVFHm0lJcqh-Hl5e0QNNKjINNcXzULhaZVpBugN_TDaxOawEAuzwBc5Sjqa2mCRw9K6TdCioKRDsS_qXUwhkLQSCMRSg1schCdl4rQD8qEj7PWBjin-fyHnDm7NnM6akZp59jndG7qV7PrvG8nLBgDIuuA-wAI3"
/>
<img
alt="Star History Chart"
src="https://api.star-history.com/chart?repos=community-scripts/ProxmoxVE&type=date&legend=top-left&sealed_token=-lUy2KH6pJryUyLCbiWRwFz3ilXMDFVFHm0lJcqh-Hl5e0QNNKjINNcXzULhaZVpBugN_TDaxOawEAuzwBc5Sjqa2mCRw9K6TdCioKRDsS_qXUwhkLQSCMRSg1schCdl4rQD8qEj7PWBjin-fyHnDm7NnM6akZp59jndG7qV7PrvG8nLBgDIuuA-wAI3"
width="700"
/>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date" width="700" />
</picture>
</a>
</p>
+1 -1
View File
@@ -81,7 +81,7 @@ function update_script() {
msg_info "Configuring Byparr"
cd /opt/Byparr
$STD uv sync --link-mode copy
$STD uv run python -m invisible_playwright fetch
$STD uv run camoufox fetch
msg_ok "Configured Byparr"
msg_info "Starting Service"
+1 -1
View File
@@ -44,7 +44,6 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gl_release "Crafty-Controller" "crafty-controller/crafty-4" "tarball" "latest" "/opt/crafty-controller/crafty/crafty-4"
chown -R crafty:crafty /opt/crafty-controller
restore_backup
msg_info "Updating TemurinJDK"
@@ -54,6 +53,7 @@ function update_script() {
msg_ok "Updated TemurinJDK"
msg_info "Updating Python dependencies"
chown -R crafty:crafty /opt/crafty-controller
cd /opt/crafty-controller/crafty/crafty-4
$STD sudo -u crafty bash -c '
source /opt/crafty-controller/crafty/.venv/bin/activate
+14
View File
@@ -42,6 +42,20 @@ function update_script() {
echo -e "${TAB}${TAB}${GN}bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)\"${CL}"
fi
if docker ps -a --format '{{.Names}}' | grep -q '^portainer_agent$'; then
msg_info "Updating Portainer Agent"
$STD docker pull portainer/agent:latest
$STD docker stop portainer_agent
$STD docker rm portainer_agent
$STD docker run -d \
-p 9001:9001 \
--name=portainer_agent \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/lib/docker/volumes:/var/lib/docker/volumes \
portainer/agent
msg_ok "Updated Portainer Agent"
fi
msg_ok "Updated successfully!"
exit
}
-5
View File
@@ -18,7 +18,6 @@ var_nesting="${var_nesting:-1}"
var_keyctl="${var_keyctl:-1}"
export var_forgejo_instance="${var_forgejo_instance:-}"
export var_forgejo_runner_uuid="${var_forgejo_runner_uuid:-}"
export var_forgejo_runner_token="${var_forgejo_runner_token:-}"
export var_runner_labels="${var_runner_labels:-}"
@@ -65,10 +64,6 @@ if [[ -n "${mode:-}" ]]; then
msg_error "var_forgejo_instance is required for unattended installs."
exit 1
fi
if [[ -z "${var_forgejo_runner_uuid:-}" ]]; then
msg_error "var_forgejo_runner_uuid is required for unattended installs."
exit 1
fi
if [[ -z "${var_forgejo_runner_token:-}" ]]; then
msg_error "var_forgejo_runner_token is required for unattended installs."
exit 1
-54
View File
@@ -1,54 +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 (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://about.gitlab.com/
APP="GitLab"
var_tags="${var_tags:-git;devtools}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-8192}"
var_disk="${var_disk:-40}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
#var_arm64="${var_arm64:-no}" # unset = ask the user; set yes/no only when verified
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/gitlab ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating GitLab (Patience)"
$STD apt update
$STD apt install -y gitlab-ce
msg_ok "Updated GitLab"
msg_info "Reconfiguring GitLab (Patience)"
$STD gitlab-ctl reconfigure
msg_ok "Reconfigured GitLab"
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 "${GATEWAY}${BGN}http://${IP}${CL}"
echo -e "${INFO}${YW}The initial root password is valid for 24 hours:${CL}"
echo -e "${TAB}${DEFAULT}${BGN}cat /etc/gitlab/initial_root_password${CL}"
-6
View File
@@ -1,6 +0,0 @@
_______ __ __ __
/ ____(_) /_/ / ____ _/ /_
/ / __/ / __/ / / __ `/ __ \
/ /_/ / / /_/ /___/ /_/ / /_/ /
\____/_/\__/_____/\__,_/_.___/
-6
View File
@@ -1,6 +0,0 @@
____ _
/ _/___ _____ (_)____
/ // __ `/ __ \/ / ___/
_/ // /_/ / / / / (__ )
/___/\__, /_/ /_/_/____/
/____/
-6
View File
@@ -1,6 +0,0 @@
_ __ __ __ ____ __ _ _
/ | / /__ / /__ ______ _____/ /__/ __ \____ / /_(_)___ ___ (_)___ ___ _____
/ |/ / _ \/ __/ | /| / / __ \/ ___/ //_/ / / / __ \/ __/ / __ `__ \/ /_ / / _ \/ ___/
/ /| / __/ /_ | |/ |/ / /_/ / / / ,< / /_/ / /_/ / /_/ / / / / / / / / /_/ __/ /
/_/ |_/\___/\__/ |__/|__/\____/_/ /_/|_|\____/ .___/\__/_/_/ /_/ /_/_/ /___/\___/_/
/_/
-6
View File
@@ -1,6 +0,0 @@
____ __
/ __ \_____/ /_
/ / / / ___/ __ \
/ /_/ / / / /_/ /
\____/_/ /_.___/
+9 -36
View File
@@ -1,7 +1,7 @@
#!/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 (CanbiZ) | Co-Author: Tom Frenzel (tomfrenzel)
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/danielbrendel/hortusfox-web
@@ -34,56 +34,29 @@ function update_script() {
systemctl stop apache2
msg_ok "Stopped Service"
cd /opt/hortusfox
if [[ ! -s app/migrations/migrations.list ]]; then
msg_info "Rebuilding HortusFox migration history"
local database_tables
database_tables="$(mariadb -u root -D hortusfox -NBe "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE();")"
: >app/migrations/migrations.list
local migration migration_file table_name
for migration in app/migrations/*.php; do
migration_file="${migration##*/}"
table_name="${migration_file%.php}"
if [[ "$migration_file" == "VersionModel.php" ]] || grep -Fxq "$table_name" <<<"$database_tables"; then
php -r 'echo hash("sha512", $argv[1]), PHP_EOL;' -- "$migration_file" >>app/migrations/migrations.list
fi
done
msg_ok "Rebuilt HortusFox migration history"
fi
if [[ ! -f app/migrations/verhist.json && -f ~/.hortusfox ]]; then
printf '["%s"]\n' "$(<~/.hortusfox)" >app/migrations/verhist.json
fi
create_backup \
/opt/hortusfox/.env \
/opt/hortusfox/app/migrations/migrations.list \
/opt/hortusfox/app/migrations/verhist.json \
/opt/hortusfox/public/img \
/opt/hortusfox/public/attachments \
/opt/hortusfox/public/backup \
/opt/hortusfox/public/exports \
/opt/hortusfox/public/snd \
/opt/hortusfox/public/themes
msg_info "Backing up current HortusFox installation"
cd /opt
mv /opt/hortusfox/ /opt/hortusfox-backup
msg_ok "Backed up current HortusFox installation"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" "tarball"
restore_backup
msg_info "Updating HortusFox"
cd /opt/hortusfox
cp /opt/hortusfox-backup/.env /opt/hortusfox/.env
cp -a /opt/hortusfox-backup/public/img/. /opt/hortusfox/public/img/
export COMPOSER_ALLOW_SUPERUSER=1
$STD composer install --no-dev --optimize-autoloader
$STD php asatru migrate:list
$STD php asatru migrate:upgrade
$STD php asatru calendar:classes
$STD php asatru plants:attributes
$STD php asatru aquashell:config
$STD php asatru calendar:classes
chown -R www-data:www-data /opt/hortusfox
rm -r /opt/hortusfox-backup
msg_ok "Updated HortusFox"
msg_info "Starting Service"
systemctl start apache2
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit
-79
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 (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Nystik-gh/ignis
APP="Ignis"
var_tags="${var_tags:-notes;obsidian}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_arm64="${var_arm64:-yes}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/ignis ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "ignis" "Nystik-gh/ignis"; then
msg_info "Stopping Service"
systemctl stop ignis
msg_ok "Stopped Service"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ignis" "Nystik-gh/ignis" "tarball"
msg_info "Building Ignis"
cd /opt/ignis
export NODE_OPTIONS="--max-old-space-size=4096"
export IGNIS_BUILD="$(cat ~/.ignis)"
$STD npm ci --ignore-scripts
$STD npm run build
$STD npm install -g @electron/asar
msg_ok "Built Ignis"
msg_info "Checking Obsidian Web Assets"
OBSIDIAN_VERSION=$(grep -oP 'OBSIDIAN_VERSION=\K[0-9.]+' /opt/ignis/apps/ignis-server/Dockerfile | head -n1)
[[ -z "$OBSIDIAN_VERSION" ]] && OBSIDIAN_VERSION="$(cat /opt/ignis_data/obsidian.version 2>/dev/null)"
if [[ -n "$OBSIDIAN_VERSION" && "$OBSIDIAN_VERSION" != "$(cat /opt/ignis_data/obsidian.version 2>/dev/null)" ]]; then
rm -rf /opt/ignis_data/obsidian-app
mkdir -p /opt/ignis_data/obsidian-app
curl -fsSL -o /tmp/obsidian.asar.gz "https://github.com/obsidianmd/obsidian-releases/releases/download/v${OBSIDIAN_VERSION}/obsidian-${OBSIDIAN_VERSION}.asar.gz"
gunzip -f /tmp/obsidian.asar.gz
$STD asar extract /tmp/obsidian.asar /opt/ignis_data/obsidian-app
rm -f /tmp/obsidian.asar
echo "${OBSIDIAN_VERSION}" >/opt/ignis_data/obsidian.version
fi
msg_ok "Checked Obsidian Web Assets"
msg_info "Starting Service"
systemctl start ignis
systemctl reload nginx
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 "${GATEWAY}${BGN}https://${IP}${CL}"
+1 -10
View File
@@ -214,16 +214,7 @@ EOF
cd "$SRC_DIR"
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg
mise_ok=0
for i in 1 2 3; do
$STD mise install && {
mise_ok=1
break
}
msg_warn "mise install failed (attempt $i/3) - retrying"
sleep 5
done
[[ "$mise_ok" -eq 1 ]] || exit 1
$STD mise install
export PATH="$(mise bin-paths 2>/dev/null | tr '\n' ':')$PATH"
if ! command -v extism-js >/dev/null 2>&1; then
# extism-js ships as a bare gzip-compressed single binary (.gz) that
-70
View File
@@ -1,70 +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 (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Ozark-Connect/NetworkOptimizer
APP="NetworkOptimizer"
var_tags="${var_tags:-network;monitoring;unifi}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-12}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-yes}"
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/networkoptimizer ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "networkoptimizer" "Ozark-Connect/NetworkOptimizer"; then
msg_info "Stopping Service"
systemctl stop networkoptimizer
msg_ok "Stopped Service"
create_backup /opt/networkoptimizer/networkoptimizer.env
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "networkoptimizer" "Ozark-Connect/NetworkOptimizer" "tarball"
msg_info "Rebuilding NetworkOptimizer"
RID="linux-x64"
[[ "$(dpkg --print-architecture)" == "arm64" ]] && RID="linux-arm64"
cd /opt/networkoptimizer
$STD dotnet publish src/NetworkOptimizer.Web -c Release -r "$RID" --self-contained -o /opt/networkoptimizer/publish
chmod +x /opt/networkoptimizer/publish/NetworkOptimizer.Web
mkdir -p /opt/networkoptimizer/publish/tools
cd /opt/networkoptimizer/src/uwnspeedtest
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $STD go build -trimpath -ldflags "-s -w" -o /opt/networkoptimizer/publish/tools/uwnspeedtest-linux-arm64 .
msg_ok "Rebuilt NetworkOptimizer"
restore_backup
msg_info "Starting Service"
systemctl start networkoptimizer
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 "${GATEWAY}${BGN}http://${IP}:8042${CL}"
-47
View File
@@ -1,47 +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: angusmaul
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://orb.net/
APP="Orb"
var_tags="${var_tags:-network;monitoring}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-yes}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if ! dpkg -s orb >/dev/null 2>&1; then
msg_error "No ${APP} Installation Found!"
exit 233
fi
msg_info "Updating Orb"
$STD apt update
$STD apt install -y --only-upgrade orb
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 "${TAB}${GATEWAY}${BGN}To link the sensor to your orb account run:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}pct exec ${CTID} -- runuser -u orb -- orb link${CL}"
-3
View File
@@ -17,9 +17,6 @@ var_arm64="${var_arm64:-yes}"
var_unprivileged="${var_unprivileged:-1}"
var_tun="${var_tun:-1}"
export var_pangolin_url="${var_pangolin_url:-}"
export var_pangolin_email="${var_pangolin_email:-}"
header_info "$APP"
variables
color
-1
View File
@@ -30,7 +30,6 @@ function update_script() {
exit
fi
ensure_dependencies git
if check_for_gh_release "sparkyfitness" "CodeWithCJ/SparkyFitness"; then
msg_info "Stopping Services"
systemctl stop sparkyfitness-server nginx
+1 -1
View File
@@ -14,7 +14,7 @@ network_check
update_os
msg_info "Installing Apache CouchDB"
ERLANG_COOKIE=$(openssl rand -hex 32)
ERLANG_COOKIE=$(openssl rand -base64 32)
ADMIN_PASS="$(openssl rand -base64 18 | cut -c1-13)"
debconf-set-selections <<<"couchdb couchdb/cookie string $ERLANG_COOKIE"
debconf-set-selections <<<"couchdb couchdb/mode select standalone"
+1 -1
View File
@@ -58,7 +58,7 @@ fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" "tarball" "latest"
msg_info "Configuring Byparr"
cd /opt/Byparr
$STD uv sync --link-mode copy
$STD uv run python -m invisible_playwright fetch
$STD uv run camoufox fetch
msg_ok "Configured Byparr"
msg_info "Creating Service"
+1
View File
@@ -83,6 +83,7 @@ $STD apt-get install -y \
fonts-kacst fonts-liberation \
fonts-noto-cjk \
fonts-noto-color-emoji \
msttcorefonts \
fonts-roboto \
fonts-thai-tlwg \
fonts-wqy-zenhei
-51
View File
@@ -1,51 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://about.gitlab.com/
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 \
openssh-server \
perl \
tzdata
msg_ok "Installed Dependencies"
setup_deb822_repo \
"gitlab-ce" \
"https://packages.gitlab.com/gitlab/gitlab-ce/gpgkey" \
"https://packages.gitlab.com/gitlab/gitlab-ce/debian" \
"$(get_os_info codename)" \
"main"
msg_info "Configuring GitLab"
mkdir -p /etc/gitlab
cat <<EOF >/etc/gitlab/gitlab.rb
external_url 'http://${LOCAL_IP}'
# Omnibus runs 'sysctl -e --system' during reconfigure, which fails in an
# unprivileged LXC because most kernel keys are not writable.
package['modify_kernel_parameters'] = false
EOF
msg_ok "Configured GitLab"
msg_info "Installing GitLab CE (Patience)"
$STD apt install -y gitlab-ce
msg_ok "Installed GitLab CE"
msg_info "Reconfiguring GitLab (Patience)"
$STD gitlab-ctl reconfigure
msg_ok "Reconfigured GitLab"
motd_ssh
customize
cleanup_lxc
+2 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ) | Co-Author: Tom Frenzel (tomfrenzel)
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/danielbrendel/hortusfox-web
@@ -36,15 +36,12 @@ msg_ok "Composer dependencies installed"
msg_info "Running DB migration"
$STD php asatru migrate:fresh
HORTUSFOX_VERSION="$(<~/.hortusfox)"
printf '["%s"]\n' "$HORTUSFOX_VERSION" >/opt/hortusfox/app/migrations/verhist.json
msg_ok "Migration finished"
msg_info "Setting up HortusFox"
$STD mariadb -u root -D $MARIADB_DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());"
$STD php asatru calendar:classes
$STD php asatru plants:attributes
$STD php asatru aquashell:config
$STD php asatru calendar:classes
ADMIN_EMAIL="admin@example.com"
ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)"
ADMIN_HASH=$(php -r "echo password_hash('$ADMIN_PASS', PASSWORD_BCRYPT);")
-113
View File
@@ -1,113 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Nystik-gh/ignis
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 nginx
msg_ok "Installed Dependencies"
NODE_VERSION="22" setup_nodejs
fetch_and_deploy_gh_release "ignis" "Nystik-gh/ignis" "tarball"
msg_info "Building Ignis"
cd /opt/ignis
export NODE_OPTIONS="--max-old-space-size=4096"
export IGNIS_BUILD="$(cat ~/.ignis)"
$STD npm ci --ignore-scripts
$STD npm run build
$STD npm install -g @electron/asar
msg_ok "Built Ignis"
msg_info "Downloading Obsidian Web Assets"
mkdir -p /opt/ignis_data/{obsidian-app,vaults,data}
OBSIDIAN_VERSION=$(grep -oP 'OBSIDIAN_VERSION=\K[0-9.]+' /opt/ignis/apps/ignis-server/Dockerfile | head -n1)
[[ -z "$OBSIDIAN_VERSION" ]] && OBSIDIAN_VERSION="1.12.7"
curl -fsSL -o /tmp/obsidian.asar.gz "https://github.com/obsidianmd/obsidian-releases/releases/download/v${OBSIDIAN_VERSION}/obsidian-${OBSIDIAN_VERSION}.asar.gz"
gunzip -f /tmp/obsidian.asar.gz
$STD asar extract /tmp/obsidian.asar /opt/ignis_data/obsidian-app
rm -f /tmp/obsidian.asar
echo "${OBSIDIAN_VERSION}" >/opt/ignis_data/obsidian.version
msg_ok "Downloaded Obsidian Web Assets"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/ignis.service
[Unit]
Description=Ignis (Obsidian in the browser)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/ignis
Environment=NODE_ENV=production
Environment=PORT=8080
Environment=VAULT_ROOT=/opt/ignis_data/vaults
Environment=DATA_ROOT=/opt/ignis_data/data
Environment=OBSIDIAN_ASSETS_PATH=/opt/ignis_data/obsidian-app
Environment=AUTO_CREATE_DEFAULT=true
ExecStart=/usr/bin/node /opt/ignis/apps/ignis-server/server/index.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now ignis
msg_ok "Created Service"
msg_info "Generating Self-Signed Certificate"
create_self_signed_cert "ignis"
msg_ok "Generated Self-Signed Certificate"
msg_info "Configuring Nginx"
cat <<'EOF' >/etc/nginx/sites-available/ignis.conf
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
http2 on;
server_name _;
ssl_certificate /etc/ssl/ignis/ignis.crt;
ssl_certificate_key /etc/ssl/ignis/ignis.key;
client_max_body_size 1024m;
location / {
proxy_pass http://127.0.0.1:8080;
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 $scheme;
proxy_read_timeout 3600s;
}
}
EOF
ln -sf /etc/nginx/sites-available/ignis.conf /etc/nginx/sites-enabled/ignis.conf
rm -f /etc/nginx/sites-enabled/default
systemctl restart nginx
msg_ok "Configured Nginx"
motd_ssh
customize
cleanup_lxc
+1 -10
View File
@@ -355,16 +355,7 @@ cp LICENSE "$APP_DIR"
cd "$SRC_DIR"
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg
mise_ok=0
for i in 1 2 3; do
$STD mise install && {
mise_ok=1
break
}
msg_warn "mise install failed (attempt $i/3) - retrying"
sleep 5
done
[[ "$mise_ok" -eq 1 ]] || exit 1
$STD mise install
export PATH="$(mise bin-paths 2>/dev/null | tr '\n' ':')$PATH"
if ! command -v extism-js >/dev/null 2>&1; then
# extism-js is published as a bare gzip-compressed single binary (.gz), which
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Ozark-Connect/NetworkOptimizer
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 \
git \
sshpass \
iperf3
setup_deb822_repo \
"microsoft" \
"https://packages.microsoft.com/keys/microsoft-2025.asc" \
"https://packages.microsoft.com/debian/13/prod/" \
"trixie"
$STD apt install -y dotnet-sdk-10.0
msg_ok "Installed Dependencies"
setup_go
fetch_and_deploy_gh_release "networkoptimizer" "Ozark-Connect/NetworkOptimizer" "tarball"
msg_info "Building NetworkOptimizer"
RID="linux-x64"
[[ "$(dpkg --print-architecture)" == "arm64" ]] && RID="linux-arm64"
cd /opt/networkoptimizer
export MinVerVersionOverride="$(cat ~/.networkoptimizer)"
$STD dotnet publish src/NetworkOptimizer.Web -c Release -r "$RID" --self-contained -o /opt/networkoptimizer/publish
chmod +x /opt/networkoptimizer/publish/NetworkOptimizer.Web
msg_ok "Built NetworkOptimizer"
msg_info "Building Gateway Speed Test Binary"
mkdir -p /opt/networkoptimizer/publish/tools
cd /opt/networkoptimizer/src/uwnspeedtest
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $STD go build -trimpath -ldflags "-s -w" -o /opt/networkoptimizer/publish/tools/uwnspeedtest-linux-arm64 .
msg_ok "Built Gateway Speed Test Binary"
msg_info "Configuring NetworkOptimizer"
cat <<EOF >/opt/networkoptimizer/networkoptimizer.env
ASPNETCORE_URLS=http://*:8042
HOST_IP=${LOCAL_IP}
Iperf3Server__Enabled=true
EOF
msg_ok "Configured NetworkOptimizer"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/networkoptimizer.service
[Unit]
Description=NetworkOptimizer
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/networkoptimizer/publish
EnvironmentFile=/opt/networkoptimizer/networkoptimizer.env
ExecStart=/opt/networkoptimizer/publish/NetworkOptimizer.Web
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now networkoptimizer
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: angusmaul
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://orb.net/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
setup_deb822_repo \
"orb" \
"https://pkgs.orb.net/stable/debian/orbforge.noarmor.gpg" \
"https://pkgs.orb.net/stable/debian" \
"orb" \
"main"
msg_info "Installing Orb"
$STD apt install -y orb
msg_ok "Installed Orb"
msg_info "Enabling Service"
systemctl enable -q --now orb
msg_ok "Enabled Service"
motd_ssh
customize
cleanup_lxc
+2 -11
View File
@@ -27,18 +27,9 @@ fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VER
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
# Read the variable first and prompt only when it is unset, so the install can
# be supplied up front. Same convention as install/forgejo-runner-install.sh.
pango_url="${var_pangolin_url:-}"
if [[ -z "$pango_url" ]]; then
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
fi
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
[[ "$pango_url" != https://* && "$pango_url" != http://* ]] && pango_url="https://${pango_url}"
pango_email="${var_pangolin_email:-}"
if [[ -z "$pango_email" ]]; then
read -rp "${TAB3}Enter your email address: " pango_email
fi
read -rp "${TAB3}Enter your email address: " pango_email
msg_info "Setup Pangolin"
SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32)
-3
View File
@@ -103,9 +103,6 @@ cat <<EOF >/etc/clickhouse-server/config.d/cluster.xml
<shard>01</shard>
<replica>01</replica>
</macros>
<merge_tree>
<allow_dimensions_outside_sorting_key>1</allow_dimensions_outside_sorting_key>
</merge_tree>
</clickhouse>
EOF
systemctl enable -q --now clickhouse-server
+1 -1
View File
@@ -14,7 +14,7 @@ network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y git nginx
$STD apt install -y nginx
msg_ok "Installed Dependencies"
PG_VERSION="18" setup_postgresql
+1 -1
View File
@@ -66,7 +66,7 @@ for i in {1..15}; do
fi
sleep 2
done
mariadb -u webtrees -p"${MARIADB_DB_PASS}" -h 127.0.0.1 webtrees -e "SHOW TABLES LIKE 'wt_user';" | grep -q wt_user
$STD mariadb -u webtrees -p"${MARIADB_DB_PASS}" -h 127.0.0.1 webtrees -e "SHOW TABLES LIKE 'wt_user';" | grep -q wt_user
msg_ok "Initialized Webtrees database schema"
$STD sudo -u www-data php /opt/webtrees/index.php user Admin \
--create \
+60 -38
View File
@@ -2576,6 +2576,11 @@ _deploy_unpacked_archive() {
local archive="$1" target="$2" workdir="$3"
local filename="${archive##*/}"
mkdir -p "$target"
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
find "${target:?}" -mindepth 1 -delete
fi
if [[ "$filename" == *.zip ]]; then
ensure_dependencies unzip
unzip -q "$archive" -d "$workdir" || {
@@ -2602,44 +2607,38 @@ _deploy_unpacked_archive() {
return 65
fi
local top_entries inner_dir source_dir
local top_entries inner_dir
top_entries=$(find "$workdir" -mindepth 1 -maxdepth 1)
if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then
inner_dir="$top_entries"
source_dir="$inner_dir"
else
source_dir="$workdir"
fi
shopt -s dotglob nullglob
if ! compgen -G "$source_dir/*" >/dev/null; then
if [[ -n "$inner_dir" ]]; then
shopt -s dotglob nullglob
if compgen -G "$inner_dir/*" >/dev/null; then
cp -r "$inner_dir"/* "$target/" || {
msg_error "Failed to copy contents from $inner_dir to $target"
shopt -u dotglob nullglob
return 252
}
else
msg_error "Inner directory is empty: $inner_dir"
shopt -u dotglob nullglob
return 252
fi
shopt -u dotglob nullglob
else
shopt -s dotglob nullglob
if compgen -G "$workdir/*" >/dev/null; then
cp -r "$workdir"/* "$target/" || {
msg_error "Failed to copy contents to $target"
shopt -u dotglob nullglob
return 252
}
else
msg_error "Unpacked archive is empty"
shopt -u dotglob nullglob
return 252
fi
shopt -u dotglob nullglob
return 252
fi
# <target> is only touched once the payload is known good. Wiping earlier left
# CLEAN_INSTALL callers with an empty target directory whenever the download
# was truncated, the archive was unreadable, or it unpacked to nothing.
mkdir -p "$target"
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
find "${target:?}" -mindepth 1 -delete
fi
if ! cp -r "$source_dir"/* "$target/"; then
if [[ -n "$inner_dir" ]]; then
msg_error "Failed to copy contents from $inner_dir to $target"
else
msg_error "Failed to copy contents to $target"
fi
shopt -u dotglob nullglob
return 252
fi
shopt -u dotglob nullglob
return 0
}
@@ -4802,8 +4801,8 @@ EOF
# Container Update Check
# - Skipped entirely when running unattended / non-interactive
# - Lists containers with a newer image available and the command to update
# them; does not pull/recreate anything itself
# - Compose-managed containers: offered via Y/N → docker compose pull + up -d
# - Standalone containers: offered via Y/N → image pull only (no stop/rm)
if [ "$docker_installed" = true ] && ! _docker_is_noninteractive; then
msg_info "Checking for container updates"
@@ -4841,14 +4840,41 @@ EOF
if [[ ${#compose_updates[@]} -eq 0 && ${#standalone_updates[@]} -eq 0 ]]; then
msg_ok "All containers are up-to-date"
else
msg_warn "Updates available (not applied automatically):"
local reply
for entry in "${compose_updates[@]}"; do
IFS='|' read -r name image compose_workdir compose_service <<<"$entry"
echo -e "${TAB3}${name} (${image}) — update via: cd ${compose_workdir} && docker compose pull ${compose_service} && docker compose up -d ${compose_service}"
reply=""
if read -r -t 60 -p "${TAB3}Update ${name} (${image}) via Compose? <y/N> (auto-no in 60s): " reply </dev/tty; then
echo ""
else
echo ""
fi
if [[ "${reply,,}" =~ ^(y|yes)$ ]]; then
msg_info "Updating $name"
if (cd "$compose_workdir" && $STD docker compose pull "$compose_service" && $STD docker compose up -d "$compose_service"); then
msg_ok "Updated $name"
else
msg_warn "Could not update $name — try manually in $compose_workdir"
fi
fi
done
for entry in "${standalone_updates[@]}"; do
IFS='|' read -r name image <<<"$entry"
echo -e "${TAB3}${name} (${image}) — update via: docker pull ${image} && docker restart ${name}"
reply=""
if read -r -t 60 -p "${TAB3}Pull new image for ${name} (${image})? <y/N> (auto-no in 60s): " reply </dev/tty; then
echo ""
else
echo ""
fi
if [[ "${reply,,}" =~ ^(y|yes)$ ]]; then
msg_info "Pulling new image for $name"
if $STD docker pull "$image"; then
msg_ok "New image available for $name — recreate the container to apply the update"
else
msg_warn "Failed to pull image for $name"
fi
fi
done
fi
fi
@@ -7706,10 +7732,6 @@ setup_nodejs() {
fi
fi
if [[ "$(npm -v 2>/dev/null | cut -d. -f1)" -ge 11 ]]; then
$STD npm config set dangerously-allow-all-scripts true --location=global 2>/dev/null || true
fi
# Set a safe default heap limit for Node.js builds if not explicitly provided.
# Priority:
# 1) NODE_OPTIONS (caller/user override)
+38 -198
View File
@@ -27,10 +27,6 @@ APP_TYPE="addon"
INSTALL_PATH="/opt/portainer"
COMPOSE_FILE="${INSTALL_PATH}/compose.yaml"
DEFAULT_PORT=9443
PORTAINER_IMAGE=""
COMPOSE_WORKDIR=""
COMPOSE_SERVICE=""
COMPOSE_CONFIG_FILES=""
# Initialize all core functions (colors, formatting, icons, STD mode)
load_functions
@@ -53,185 +49,18 @@ function uninstall() {
msg_ok "${APP} has been uninstalled"
}
# ==============================================================================
# UPDATE SCRIPT HELPER
# ==============================================================================
function ensure_update_script() {
[[ -f /usr/local/bin/update_portainer ]] && return 0
msg_info "Creating update script"
cat <<'UPDATEEOF' >/usr/local/bin/update_portainer
#!/usr/bin/env bash
# Portainer Update Script
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)"
UPDATEEOF
chmod +x /usr/local/bin/update_portainer
msg_ok "Created update script (/usr/local/bin/update_portainer)"
}
# ==============================================================================
# DETECT EXISTING INSTALLATION
# ==============================================================================
function detect_portainer() {
PORTAINER_IMAGE=""
COMPOSE_WORKDIR=""
COMPOSE_SERVICE=""
COMPOSE_CONFIG_FILES=""
if ! docker container inspect portainer &>/dev/null; then
return 0
fi
PORTAINER_IMAGE=$(docker inspect portainer --format '{{.Config.Image}}')
if [[ ! "$PORTAINER_IMAGE" =~ (^|/)portainer-(ce|ee)(:|@) ]]; then
msg_error "A container named 'portainer' exists but does not use an official Portainer CE or BE image."
exit 10
fi
COMPOSE_WORKDIR=$(docker inspect portainer --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' 2>/dev/null || true)
COMPOSE_SERVICE=$(docker inspect portainer --format '{{index .Config.Labels "com.docker.compose.service"}}' 2>/dev/null || true)
COMPOSE_CONFIG_FILES=$(docker inspect portainer --format '{{index .Config.Labels "com.docker.compose.project.config_files"}}' 2>/dev/null || true)
if [[ "$COMPOSE_WORKDIR" == "<no value>" ]]; then
COMPOSE_WORKDIR=""
fi
if [[ "$COMPOSE_SERVICE" == "<no value>" ]]; then
COMPOSE_SERVICE=""
fi
if [[ "$COMPOSE_CONFIG_FILES" == "<no value>" ]]; then
COMPOSE_CONFIG_FILES=""
fi
return 0
}
# ==============================================================================
# UPDATE COMPOSE INSTALLATION
# ==============================================================================
function update_compose() {
local workdir="$1"
local service="$2"
local config_files="$3"
local config
local -a configs=()
local -a compose_args=()
if [[ -n "$config_files" ]]; then
IFS=',' read -ra configs <<<"$config_files"
for config in "${configs[@]}"; do
compose_args+=(--file "$config")
done
fi
msg_info "Pulling latest ${APP} image"
if [[ -n "$service" ]]; then
(cd "$workdir" && $STD docker compose "${compose_args[@]}" pull "$service")
else
(cd "$workdir" && $STD docker compose pull)
fi
msg_ok "Pulled latest image"
msg_info "Restarting ${APP}"
if [[ -n "$service" ]]; then
(cd "$workdir" && $STD docker compose "${compose_args[@]}" up -d "$service")
else
(cd "$workdir" && $STD docker compose up -d --remove-orphans)
fi
msg_ok "Restarted ${APP}"
}
# ==============================================================================
# UPDATE STANDALONE INSTALLATION
# ==============================================================================
function update_standalone() {
local old_image_id latest_image_id restart_name restart_max network_mode container_port host_ip host_port source destination mode writable
local was_running privileged user
local -a run_args=(-d --name portainer)
local -a command=()
old_image_id=$(docker inspect portainer --format '{{.Image}}')
was_running=$(docker inspect portainer --format '{{.State.Running}}')
restart_name=$(docker inspect portainer --format '{{.HostConfig.RestartPolicy.Name}}')
restart_max=$(docker inspect portainer --format '{{.HostConfig.RestartPolicy.MaximumRetryCount}}')
network_mode=$(docker inspect portainer --format '{{.HostConfig.NetworkMode}}')
privileged=$(docker inspect portainer --format '{{.HostConfig.Privileged}}')
user=$(docker inspect portainer --format '{{.Config.User}}')
if [[ -n "$restart_name" && "$restart_name" != "no" ]]; then
if [[ "$restart_name" == "on-failure" && "$restart_max" -gt 0 ]]; then
run_args+=(--restart "${restart_name}:${restart_max}")
else
run_args+=(--restart "$restart_name")
fi
fi
[[ "$network_mode" != "default" ]] && run_args+=(--network "$network_mode")
[[ "$privileged" == "true" ]] && run_args+=(--privileged)
[[ -n "$user" ]] && run_args+=(--user "$user")
while IFS='|' read -r container_port host_ip host_port; do
[[ -z "$container_port" || -z "$host_port" ]] && continue
[[ "$host_ip" == *:* ]] && host_ip="[${host_ip}]"
if [[ -n "$host_ip" && "$host_ip" != "0.0.0.0" ]]; then
run_args+=(-p "${host_ip}:${host_port}:${container_port}")
else
run_args+=(-p "${host_port}:${container_port}")
fi
done < <(docker inspect portainer --format '{{range $port, $bindings := .HostConfig.PortBindings}}{{range $bindings}}{{printf "%s|%s|%s\n" $port .HostIp .HostPort}}{{end}}{{end}}')
while IFS='|' read -r source destination mode writable; do
[[ -z "$source" || -z "$destination" ]] && continue
if [[ "$writable" == "false" ]]; then
mode="${mode:+${mode},}ro"
fi
run_args+=(-v "${source}:${destination}${mode:+:${mode}}")
done < <(docker inspect portainer --format '{{range .Mounts}}{{if eq .Type "volume"}}{{.Name}}{{else}}{{.Source}}{{end}}|{{.Destination}}|{{.Mode}}|{{.RW}}{{println}}{{end}}')
while IFS= read -r environment; do
[[ -n "$environment" ]] && run_args+=(-e "$environment")
done < <(docker inspect portainer --format '{{range .Config.Env}}{{println .}}{{end}}')
while IFS= read -r argument; do
[[ -n "$argument" ]] && command+=("$argument")
done < <(docker inspect portainer --format '{{range .Config.Cmd}}{{println .}}{{end}}')
msg_info "Pulling latest ${APP} image"
$STD docker pull "$PORTAINER_IMAGE"
latest_image_id=$(docker image inspect "$PORTAINER_IMAGE" --format '{{.Id}}')
msg_ok "Pulled latest image"
if [[ "$old_image_id" == "$latest_image_id" ]]; then
msg_ok "${APP} is already up to date"
return 0
fi
msg_info "Recreating ${APP}"
[[ "$was_running" == "true" ]] && $STD docker stop portainer
$STD docker rm portainer
if ! $STD docker run "${run_args[@]}" "$PORTAINER_IMAGE" "${command[@]}"; then
msg_warn "Update failed, restoring the previous ${APP} image"
docker rm -f portainer &>/dev/null || true
if $STD docker run "${run_args[@]}" "$old_image_id" "${command[@]}"; then
[[ "$was_running" != "true" ]] && $STD docker stop portainer
msg_error "Failed to update ${APP}; restored the previous container."
else
msg_error "Failed to update or restore ${APP}."
fi
exit 10
fi
msg_ok "Recreated ${APP}"
}
# ==============================================================================
# UPDATE
# ==============================================================================
function update() {
if [[ -f "$COMPOSE_FILE" ]]; then
update_compose "$INSTALL_PATH" "" ""
elif [[ -n "$COMPOSE_WORKDIR" && -n "$COMPOSE_SERVICE" && -d "$COMPOSE_WORKDIR" ]]; then
update_compose "$COMPOSE_WORKDIR" "$COMPOSE_SERVICE" "$COMPOSE_CONFIG_FILES"
else
update_standalone
fi
msg_info "Pulling latest ${APP} image"
cd "$INSTALL_PATH"
$STD docker compose pull
msg_ok "Pulled latest image"
ensure_update_script
msg_info "Restarting ${APP}"
$STD docker compose up -d --remove-orphans
msg_ok "Restarted ${APP}"
msg_ok "Updated successfully"
exit
@@ -252,16 +81,26 @@ function check_docker() {
msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') and Docker Compose are available"
}
# ==============================================================================
# CHECK EXISTING CONTAINER
# ==============================================================================
function check_existing_container() {
# Guards against clobbering a Portainer container not managed by this addon
# (e.g. Business/Enterprise Edition installed manually or via the legacy docker.sh flow)
if docker ps -a --format '{{.Names}}' | grep -q '^portainer$'; then
msg_error "A container named 'portainer' already exists but is not managed by this addon."
msg_error "This may be a Portainer Business/Enterprise Edition install or a manual setup."
msg_error "Remove/rename that container first, or manage it manually. Exiting."
exit 10
fi
}
# ==============================================================================
# INSTALL
# ==============================================================================
function install() {
check_docker
detect_portainer
if [[ -n "$PORTAINER_IMAGE" ]]; then
msg_error "${APP} is already installed."
exit 10
fi
check_existing_container
msg_info "Creating install directory"
mkdir -p "$INSTALL_PATH"
@@ -276,7 +115,15 @@ function install() {
$STD docker compose up -d
msg_ok "Started ${APP}"
ensure_update_script
# Create update script
msg_info "Creating update script"
cat <<'UPDATEEOF' >/usr/local/bin/update_portainer
#!/usr/bin/env bash
# Portainer Update Script
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)"
UPDATEEOF
chmod +x /usr/local/bin/update_portainer
msg_ok "Created update script (/usr/local/bin/update_portainer)"
echo ""
msg_ok "${APP} is reachable at: ${BL}https://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
@@ -291,9 +138,7 @@ function install() {
# Handle type=update (called from update script)
if [[ "${type:-}" == "update" ]]; then
header_info
check_docker
detect_portainer
if [[ -f "$COMPOSE_FILE" || -n "$PORTAINER_IMAGE" ]]; then
if [[ -f "$COMPOSE_FILE" ]]; then
update
else
msg_error "${APP} is not installed. Nothing to update."
@@ -305,21 +150,16 @@ fi
header_info
get_lxc_ip
check_docker
detect_portainer
# Check if already installed
if [[ -f "$COMPOSE_FILE" || -n "$PORTAINER_IMAGE" ]]; then
if [[ -f "$COMPOSE_FILE" ]]; then
msg_warn "${APP} is already installed."
echo ""
if [[ -f "$COMPOSE_FILE" ]]; then
echo -n "${TAB}Uninstall ${APP}? (y/N): "
read -r uninstall_prompt
if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then
uninstall
exit 0
fi
echo -n "${TAB}Uninstall ${APP}? (y/N): "
read -r uninstall_prompt
if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then
uninstall
exit 0
fi
echo -n "${TAB}Update ${APP}? (y/N): "
-422
View File
@@ -1,422 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
cat <<"EOF"
____ ____ __ ___ ____ __ ____ __ ____
/ __ \/ __ \/ |/ / / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
/ /_/ / / / / /|_/ / / /_/ / __ \/ ___/ __/ / // __ \/ ___/ __/ __ `/ / /
/ ____/ /_/ / / / / / ____/ /_/ (__ ) /_ _/ // / / (__ ) /_/ /_/ / / /
/_/ /_____/_/ /_/ /_/ \____/____/\__/ /___/_/ /_/____/\__/\__,_/_/_/
EOF
}
RD=$(echo "\033[01;31m")
YW=$(echo "\033[33m")
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
BFR="\\r\\033[K"
HOLD="-"
CM="${GN}${CL}"
CROSS="${RD}${CL}"
set -euo pipefail
shopt -s inherit_errexit nullglob
msg_info() {
local msg="$1"
echo -ne " ${HOLD} ${YW}${msg}..."
}
msg_ok() {
local msg="$1"
echo -e "${BFR} ${CM} ${GN}${msg}${CL}"
}
msg_error() {
local msg="$1"
echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}"
}
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pdm-install" "pve"
if ! dpkg -s proxmox-datacenter-manager >/dev/null 2>&1; then
msg_error "This script is only intended for Proxmox Datacenter Manager"
exit 232
fi
repo_state() {
# $1 = component name (e.g. pdm-enterprise, pdm-no-subscription, pdm-test)
local repo="$1"
local file=""
local state="missing"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
[[ -f "$f" ]] || continue
if grep -q "$repo" "$f"; then
file="$f"
if [[ "$f" == *.sources ]]; then
if grep -qiE '^Enabled:\s*(no|false)' "$f"; then
state="disabled"
else
state="active"
fi
else
if grep -qE "^[^#].*${repo}" "$f"; then
state="active"
elif grep -qE "^#.*${repo}" "$f"; then
state="disabled"
fi
fi
break
fi
done
echo "$state $file"
}
toggle_repo() {
# $1 = file, $2 = action (enable|disable)
local file="$1" action="$2"
if [[ "$file" == *.sources ]]; then
if [[ "$action" == "disable" ]]; then
if grep -qiE '^Enabled:' "$file"; then
sed -i 's/^Enabled:.*/Enabled: false/' "$file"
else
echo "Enabled: false" >>"$file"
fi
else
sed -i 's/^Enabled:.*/Enabled: true/' "$file"
fi
else
if [[ "$action" == "disable" ]]; then
sed -i '/^[^#]/s/^/# /' "$file"
else
sed -i 's/^# *//' "$file"
fi
fi
}
start_routines() {
header_info
VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)"
# ---- SOURCES ----
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM SOURCES" --menu \
"This will set the correct Debian sources for Proxmox Datacenter Manager.\n\nCorrect sources?" 14 58 2 \
"yes" " " \
"no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Correcting Debian Sources"
cat <<EOF >/etc/apt/sources.list.d/debian.sources
Types: deb
URIs: http://deb.debian.org/debian/
Suites: ${VERSION} ${VERSION}-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: ${VERSION}-security
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
rm -f /etc/apt/sources.list
msg_ok "Corrected Debian Sources"
;;
no) msg_error "Selected no to Correcting Debian Sources" ;;
esac
# ---- PDM-ENTERPRISE ----
read -r state file <<<"$(repo_state pdm-enterprise)"
case $state in
active)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-ENTERPRISE" \
--menu "'pdm-enterprise' repository is currently ENABLED.\n\nIt requires a subscription and will fail apt update without one.\n\nWhat do you want to do?" 16 58 3 \
"disable" "Disable this repo" \
"keep" "Keep as is" \
"delete" "Delete repo file" \
3>&2 2>&1 1>&3)
case $CHOICE in
keep) msg_ok "Kept 'pdm-enterprise' repository" ;;
disable)
msg_info "Disabling 'pdm-enterprise' repository"
toggle_repo "$file" disable
msg_ok "Disabled 'pdm-enterprise' repository"
;;
delete)
msg_info "Deleting 'pdm-enterprise' repository file"
rm -f "$file"
msg_ok "Deleted 'pdm-enterprise' repository file"
;;
esac
;;
disabled)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-ENTERPRISE" \
--menu "'pdm-enterprise' repository is currently DISABLED.\n\nWhat do you want to do?" 14 58 3 \
"keep" "Keep disabled" \
"enable" "Enable (subscription required)" \
"delete" "Delete repo file" \
3>&2 2>&1 1>&3)
case $CHOICE in
enable)
msg_info "Enabling 'pdm-enterprise' repository"
toggle_repo "$file" enable
msg_ok "Enabled 'pdm-enterprise' repository"
;;
keep) msg_ok "Kept 'pdm-enterprise' repository disabled" ;;
delete)
msg_info "Deleting 'pdm-enterprise' repository file"
rm -f "$file"
msg_ok "Deleted 'pdm-enterprise' repository file"
;;
esac
;;
missing)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-ENTERPRISE" \
--menu "Add 'pdm-enterprise' repository?\n\nOnly for subscription customers." 14 58 2 \
"no" " " \
"yes" " " \
--default-item "no" \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Adding 'pdm-enterprise' repository"
cat <<EOF >/etc/apt/sources.list.d/pdm-enterprise.sources
Types: deb
URIs: https://enterprise.proxmox.com/debian/pdm
Suites: ${VERSION}
Components: pdm-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "Added 'pdm-enterprise' repository"
;;
no) msg_error "Selected no to Adding 'pdm-enterprise' repository" ;;
esac
;;
esac
# ---- PDM-NO-SUBSCRIPTION ----
read -r state file <<<"$(repo_state pdm-no-subscription)"
case $state in
active)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-NO-SUBSCRIPTION" \
--menu "'pdm-no-subscription' repository is currently ENABLED.\n\nWhat do you want to do?" 14 58 3 \
"keep" "Keep as is" \
"disable" "Disable this repo" \
"delete" "Delete repo file" \
3>&2 2>&1 1>&3)
case $CHOICE in
keep) msg_ok "Kept 'pdm-no-subscription' repository" ;;
disable)
msg_info "Disabling 'pdm-no-subscription' repository"
toggle_repo "$file" disable
msg_ok "Disabled 'pdm-no-subscription' repository"
;;
delete)
msg_info "Deleting 'pdm-no-subscription' repository file"
rm -f "$file"
msg_ok "Deleted 'pdm-no-subscription' repository file"
;;
esac
;;
disabled)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-NO-SUBSCRIPTION" \
--menu "'pdm-no-subscription' repository is currently DISABLED.\n\nWhat do you want to do?" 14 58 3 \
"enable" "Enable (recommended)" \
"keep" "Keep disabled" \
"delete" "Delete repo file" \
3>&2 2>&1 1>&3)
case $CHOICE in
enable)
msg_info "Enabling 'pdm-no-subscription' repository"
toggle_repo "$file" enable
msg_ok "Enabled 'pdm-no-subscription' repository"
;;
keep) msg_ok "Kept 'pdm-no-subscription' repository disabled" ;;
delete)
msg_info "Deleting 'pdm-no-subscription' repository file"
rm -f "$file"
msg_ok "Deleted 'pdm-no-subscription' repository file"
;;
esac
;;
missing)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-NO-SUBSCRIPTION" \
--menu "The 'pdm-no-subscription' repository provides access to all open-source components of Proxmox Datacenter Manager.\n\nAdd 'pdm-no-subscription' repository?" 14 58 2 \
"yes" " " \
"no" " " \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Adding 'pdm-no-subscription' repository"
cat <<EOF >/etc/apt/sources.list.d/proxmox.sources
Types: deb
URIs: http://download.proxmox.com/debian/pdm
Suites: ${VERSION}
Components: pdm-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "Added 'pdm-no-subscription' repository"
;;
no) msg_error "Selected no to Adding 'pdm-no-subscription' repository" ;;
esac
;;
esac
# ---- PDM-TEST ----
read -r state file <<<"$(repo_state pdm-test)"
case $state in
active) msg_ok "'pdm-test' repository already active (skipped)" ;;
disabled) msg_ok "'pdm-test' repository already disabled (skipped)" ;;
missing)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PDM-TEST" \
--menu "The 'pdm-test' repository is used by developers to test new features.\n\nAdd (disabled) 'pdm-test' repository?" 14 58 2 \
"yes" " " \
"no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Adding 'pdm-test' repository (disabled)"
cat <<EOF >/etc/apt/sources.list.d/pdm-test.sources
Types: deb
URIs: http://download.proxmox.com/debian/pdm
Suites: ${VERSION}
Components: pdm-test
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: false
EOF
msg_ok "Added 'pdm-test' repository"
;;
no) msg_error "Selected no to Adding 'pdm-test' repository" ;;
esac
;;
esac
# ---- SUBSCRIPTION NAG ----
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUBSCRIPTION NAG" --menu \
"Disable subscription nag in the PDM UI?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" \
"Supporting the software's development team is essential.\nPlease consider buying a subscription." 10 58
msg_info "Disabling subscription nag"
mkdir -p /usr/local/bin
cat <<'EOF' >/usr/local/bin/pdm-remove-nag.sh
#!/bin/sh
# PDM ships its web UI as a Yew/WASM app in a single JS bundle - there is no
# proxmoxlib.js and no HTML template to patch, so the dialog is confirmed at
# runtime instead. The nag gates the update refresh: its on_close callback is
# what sends RefreshAll, so it must be close()d, never removed from the DOM.
# Re-applied by the apt hook after every package update.
BUNDLE=/usr/share/javascript/proxmox-datacenter-manager/js/pdm-ui_bundle.js
if [ -s "$BUNDLE" ] && ! grep -q PDM_NO_MORE_NAGGING "$BUNDLE"; then
echo "Patching PDM subscription nag..."
cat >>"$BUNDLE" <<'JS'
// PDM_NO_MORE_NAGGING
(function () {
var MARK = 'data-pdm-nag';
var dismiss = function (d) {
if (d.getAttribute(MARK)) return;
d.setAttribute(MARK, '1');
var tries = 12;
var tick = function () {
if (!d.isConnected || tries-- <= 0) return;
try { d.close(); } catch (e) {}
setTimeout(tick, 120);
};
requestAnimationFrame(tick);
};
var scan = function () {
document.querySelectorAll('dialog.pwt-outer-dialog').forEach(function (d) {
if ((d.textContent || '').indexOf('pdm.proxmox.com') !== -1) dismiss(d);
});
};
var start = function () {
scan();
new MutationObserver(scan).observe(document.body, { childList: true, subtree: true });
};
if (document.body) { start(); } else {
document.addEventListener('DOMContentLoaded', start);
}
})();
JS
fi
EOF
chmod 755 /usr/local/bin/pdm-remove-nag.sh
/usr/local/bin/pdm-remove-nag.sh >/dev/null 2>&1
cat <<'EOF' >/etc/apt/apt.conf.d/no-nag-script
DPkg::Post-Invoke { "/usr/local/bin/pdm-remove-nag.sh"; };
EOF
chmod 644 /etc/apt/apt.conf.d/no-nag-script
msg_ok "Disabled subscription nag (clear browser cache!)"
;;
no)
msg_error "Selected no to Disabling subscription nag"
rm -f /etc/apt/apt.conf.d/no-nag-script /usr/local/bin/pdm-remove-nag.sh 2>/dev/null
apt --reinstall install proxmox-datacenter-manager-ui &>/dev/null || msg_error "UI reinstall failed"
;;
esac
# ---- UPDATE ----
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE" --menu \
"Update Proxmox Datacenter Manager now?" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Updating Proxmox Datacenter Manager (Patience)"
apt update &>/dev/null || msg_error "apt update failed"
apt -y dist-upgrade &>/dev/null || msg_error "apt dist-upgrade failed"
msg_ok "Updated Proxmox Datacenter Manager"
;;
no) msg_error "Selected no to updating Proxmox Datacenter Manager" ;;
esac
# ---- REMINDER ----
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Post-Install Reminder" --msgbox \
"IMPORTANT:
Proxmox Datacenter Manager is still under active development. Do not rely on it as the only management path to your nodes.
After completing these steps, it is strongly recommended to REBOOT.
After the upgrade or post-install routines, always clear your browser cache or perform a hard reload (Ctrl+Shift+R) before using the PDM Web UI to avoid UI display issues." 20 80
# ---- REBOOT ----
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu \
"Reboot Proxmox Datacenter Manager now? (recommended)" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Rebooting Proxmox Datacenter Manager"
sleep 2
msg_ok "Completed Post Install Routines"
reboot
;;
no)
msg_error "Selected no to reboot (Reboot recommended)"
msg_ok "Completed Post Install Routines"
;;
esac
}
header_info
echo -e "\nThis script will Perform Post Install Routines.\n"
while true; do
read -rp "Start the Proxmox Datacenter Manager Post Install Script (y/n)? " yn
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
exit
;;
*) echo "Please answer yes or no." ;;
esac
done
start_routines
+12
View File
@@ -125,6 +125,12 @@ EOF
no) msg_error "Selected no to Correcting Proxmox VE Sources" ;;
esac
if [[ "$(dpkg --print-architecture 2>/dev/null)" == "arm64" ]]; then
msg_ok "ARM64 detected - skipping Proxmox repository setup"
post_routines_common
return
fi
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-ENTERPRISE" --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nDisable 'pve-enterprise' repository?" 14 58 2 \
"yes" " " \
"no" " " 3>&2 2>&1 1>&3)
@@ -282,6 +288,12 @@ EOF
esac
fi
if [[ "$(dpkg --print-architecture 2>/dev/null)" == "arm64" ]]; then
msg_ok "ARM64 detected - skipping Proxmox repository setup"
post_routines_common
return
fi
# ---- PVE-ENTERPRISE ----
if component_exists_in_sources "pve-enterprise"; then
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \