Compare commits

..

10 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
bcdabbd0a9 Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:54:01 +01:00
CanbiZ (MickLesk)
d3b75f80cd Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:53:48 +01:00
CanbiZ (MickLesk)
9311cdabc1 Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:53:37 +01:00
CanbiZ (MickLesk)
89df7f7c85 Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:53:15 +01:00
CanbiZ (MickLesk)
3e034fb59e Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:52:59 +01:00
CanbiZ (MickLesk)
a15f44682e Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:52:44 +01:00
CanbiZ (MickLesk)
d3465eb934 Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:52:36 +01:00
CanbiZ (MickLesk)
8927a66554 Update tools/pve/update-apps.sh
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 19:52:29 +01:00
CanbiZ (MickLesk)
3182bca7c7 Fix source command syntax in update-apps.sh 2026-02-04 11:36:29 +01:00
CanbiZ (MickLesk)
5d8ffd68c4 Add PVE LXC Apps Updater script and metadata
Add a new Proxmox LXC Apps Updater: frontend/public/json/update-apps.json registers the app for the UI (install script points to tools/pve/update-apps.sh) and provides documentation/notes for usage and environment variables. Add tools/pve/update-apps.sh (MIT, authors noted) — a full-featured updater for community-scripts managed LXC containers that supports interactive Whiptail selection or environment-driven automation (var_backup, var_backup_storage, var_container, var_unattended, var_skip_confirm, var_auto_reboot). Features include exportable JSON config, optional vzdump backups, detection of service/update script from the community repo, temporary CPU/RAM adjustments for build requirements, unattended update mode, restore-from-backup on failure, and reporting/optional reboot of containers that require it.
2026-02-04 11:34:48 +01:00
59 changed files with 2361 additions and 5294 deletions

310
.github/changelogs/2026/01.md generated vendored
View File

@@ -1,156 +1,14 @@
## 2026-01-31 ## 2026-01-27
### 🆕 New Scripts
- shelfmark ([#11371](https://github.com/community-scripts/ProxmoxVE/pull/11371))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix: yubal: add git [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11394](https://github.com/community-scripts/ProxmoxVE/pull/11394))
## 2026-01-30
### 🆕 New Scripts
- languagetool ([#11370](https://github.com/community-scripts/ProxmoxVE/pull/11370))
- Ampache ([#11369](https://github.com/community-scripts/ProxmoxVE/pull/11369))
### 🚀 Updated Scripts
- #### 🔧 Refactor
- Refactor: remove redundant PHP_MODULE entries in several scripts [@MickLesk](https://github.com/MickLesk) ([#11362](https://github.com/community-scripts/ProxmoxVE/pull/11362))
- Refactor: Koillection [@MickLesk](https://github.com/MickLesk) ([#11361](https://github.com/community-scripts/ProxmoxVE/pull/11361))
### 💾 Core
- #### 🐞 Bug Fixes
- core: meilisearch - add data migration for version upgrades [@MickLesk](https://github.com/MickLesk) ([#11356](https://github.com/community-scripts/ProxmoxVE/pull/11356))
- #### ✨ New Features
- [tools] Add `fetch_and_deploy_from_url()` [@tremor021](https://github.com/tremor021) ([#11376](https://github.com/community-scripts/ProxmoxVE/pull/11376))
- core: php - improve module handling and prevent installation failures [@MickLesk](https://github.com/MickLesk) ([#11358](https://github.com/community-scripts/ProxmoxVE/pull/11358))
## 2026-01-29
### 🆕 New Scripts
- Alpine-Valkey [@MickLesk](https://github.com/MickLesk) ([#11320](https://github.com/community-scripts/ProxmoxVE/pull/11320))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: Pin version to 2.5.2 [@vhsdream](https://github.com/vhsdream) ([#11335](https://github.com/community-scripts/ProxmoxVE/pull/11335))
- Kollection: Update to php 8.5 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11315](https://github.com/community-scripts/ProxmoxVE/pull/11315))
- Notifiarr: change installation check from apt to systemd service [@MickLesk](https://github.com/MickLesk) ([#11319](https://github.com/community-scripts/ProxmoxVE/pull/11319))
- #### ✨ New Features
- [FEAT] Immich: Enable Maintenance Mode before update [@vhsdream](https://github.com/vhsdream) ([#11342](https://github.com/community-scripts/ProxmoxVE/pull/11342))
- jellyfin: add logrotate instead of reducing log level [@MickLesk](https://github.com/MickLesk) ([#11326](https://github.com/community-scripts/ProxmoxVE/pull/11326))
- core: Add config file handling options | Fix Vikunja update with interactive overwrite [@MickLesk](https://github.com/MickLesk) ([#11317](https://github.com/community-scripts/ProxmoxVE/pull/11317))
- Immich: v2.5.0 [@vhsdream](https://github.com/vhsdream) ([#11240](https://github.com/community-scripts/ProxmoxVE/pull/11240))
- #### 💥 Breaking Changes
- fix: vikunja v1 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11308](https://github.com/community-scripts/ProxmoxVE/pull/11308))
- #### 🔧 Refactor
- Refactor: Byparr [@vhsdream](https://github.com/vhsdream) ([#11338](https://github.com/community-scripts/ProxmoxVE/pull/11338))
- cloudflare: Remove deprecated DNS-over-HTTPS proxy option [@MickLesk](https://github.com/MickLesk) ([#11068](https://github.com/community-scripts/ProxmoxVE/pull/11068))
### 💾 Core
- #### 🐞 Bug Fixes
- build.func: Replace storage variable with searchdomain variable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11322](https://github.com/community-scripts/ProxmoxVE/pull/11322))
### 📂 Github
- Add workflow to lock closed issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11316](https://github.com/community-scripts/ProxmoxVE/pull/11316))
## 2026-01-28
### 🆕 New Scripts
- nodecast-tv ([#11287](https://github.com/community-scripts/ProxmoxVE/pull/11287))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Ubuntu 25.04 VM - Change default start from yes to no [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11292](https://github.com/community-scripts/ProxmoxVE/pull/11292))
- #### ✨ New Features
- various scripts: use setup_meilisearch function [@MickLesk](https://github.com/MickLesk) ([#11259](https://github.com/community-scripts/ProxmoxVE/pull/11259))
- #### 🔧 Refactor
- Refactor: NPMPlus / Default Login [@MickLesk](https://github.com/MickLesk) ([#11262](https://github.com/community-scripts/ProxmoxVE/pull/11262))
### 💾 Core
- #### 🐞 Bug Fixes
- core: sed patch for ram [@lavacano](https://github.com/lavacano) ([#11285](https://github.com/community-scripts/ProxmoxVE/pull/11285))
- Fix installer loop caused by invalid whiptail menu separator [@Mesteriis](https://github.com/Mesteriis) ([#11237](https://github.com/community-scripts/ProxmoxVE/pull/11237))
- core: fix Debian 13 LXC template root ownership bug [@MickLesk](https://github.com/MickLesk) ([#11277](https://github.com/community-scripts/ProxmoxVE/pull/11277))
- tools.func: prevent systemd-tmpfiles failure in unprivileged LXC during deb install [@MickLesk](https://github.com/MickLesk) ([#11271](https://github.com/community-scripts/ProxmoxVE/pull/11271))
- tools.func: fix php "wait_for" hint [@MickLesk](https://github.com/MickLesk) ([#11254](https://github.com/community-scripts/ProxmoxVE/pull/11254))
- #### ✨ New Features
- core: update dynamic values in LXC profile on update_motd_ip [@MickLesk](https://github.com/MickLesk) ([#11268](https://github.com/community-scripts/ProxmoxVE/pull/11268))
- tools.func: add new function - setup_meilisearch [@MickLesk](https://github.com/MickLesk) ([#11258](https://github.com/community-scripts/ProxmoxVE/pull/11258))
### 📂 Github
- github: add GitHub-based versions.json updater [@MickLesk](https://github.com/MickLesk) ([#10021](https://github.com/community-scripts/ProxmoxVE/pull/10021))
### 🌐 Website
- #### ✨ New Features
- Frontend: use github-versions.json for version display [@MickLesk](https://github.com/MickLesk) ([#11281](https://github.com/community-scripts/ProxmoxVE/pull/11281))
- #### 📝 Script Information
- fix: homarr: conf location [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11253](https://github.com/community-scripts/ProxmoxVE/pull/11253))
## 2026-01-27
### 🚀 Updated Scripts ### 🚀 Updated Scripts
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- [FIX] Jotty: backup and restore custom config [@vhsdream](https://github.com/vhsdream) ([#11212](https://github.com/community-scripts/ProxmoxVE/pull/11212)) - [FIX] Jotty: backup and restore custom config [@vhsdream](https://github.com/vhsdream) ([#11212](https://github.com/community-scripts/ProxmoxVE/pull/11212))
- Immich: update libraw [@vhsdream](https://github.com/vhsdream) ([#11233](https://github.com/community-scripts/ProxmoxVE/pull/11233))
- #### ✨ New Features
- grist: enable optional enterprise features toggle [@MickLesk](https://github.com/MickLesk) ([#11239](https://github.com/community-scripts/ProxmoxVE/pull/11239))
- #### 🔧 Refactor
- Termix: use nginx.conf from upstream repo [@MickLesk](https://github.com/MickLesk) ([#11228](https://github.com/community-scripts/ProxmoxVE/pull/11228))
### 💾 Core
- #### ✨ New Features
- feat: add NVIDIA driver install prompt for GPU-enabled containers [@devdecrux](https://github.com/devdecrux) ([#11184](https://github.com/community-scripts/ProxmoxVE/pull/11184))
### 📚 Documentation ### 📚 Documentation
- doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215)) - doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215))
- changelog: archive old entries to year/month files [@MickLesk](https://github.com/MickLesk) ([#11225](https://github.com/community-scripts/ProxmoxVE/pull/11225))
## 2026-01-26 ## 2026-01-26
@@ -242,7 +100,7 @@
### 🆕 New Scripts ### 🆕 New Scripts
- Tracearr ([#11079](https://github.com/community-scripts/ProxmoxVE/pull/11079)) - Tracearr ([#11079](https://github.com/community-scripts/ProxmoxVE/pull/11079))
- Dawarich ([#11075](https://github.com/community-scripts/ProxmoxVE/pull/11075)) - Dawarich ([#11075](https://github.com/community-scripts/ProxmoxVE/pull/11075))
### 🚀 Updated Scripts ### 🚀 Updated Scripts
@@ -392,7 +250,7 @@
### 🆕 New Scripts ### 🆕 New Scripts
- Termix ([#10887](https://github.com/community-scripts/ProxmoxVE/pull/10887)) - Termix ([#10887](https://github.com/community-scripts/ProxmoxVE/pull/10887))
- ThingsBoard ([#10904](https://github.com/community-scripts/ProxmoxVE/pull/10904)) - ThingsBoard ([#10904](https://github.com/community-scripts/ProxmoxVE/pull/10904))
### 🚀 Updated Scripts ### 🚀 Updated Scripts
@@ -461,7 +319,7 @@
### 🆕 New Scripts ### 🆕 New Scripts
- Flatnotes ([#10857](https://github.com/community-scripts/ProxmoxVE/pull/10857)) - Flatnotes ([#10857](https://github.com/community-scripts/ProxmoxVE/pull/10857))
- Unifi OS Server ([#10856](https://github.com/community-scripts/ProxmoxVE/pull/10856)) - Unifi OS Server ([#10856](https://github.com/community-scripts/ProxmoxVE/pull/10856))
### 🚀 Updated Scripts ### 🚀 Updated Scripts
@@ -527,7 +385,7 @@
### 🆕 New Scripts ### 🆕 New Scripts
- Investbrain ([#10774](https://github.com/community-scripts/ProxmoxVE/pull/10774)) - Investbrain ([#10774](https://github.com/community-scripts/ProxmoxVE/pull/10774))
- Fladder ([#10768](https://github.com/community-scripts/ProxmoxVE/pull/10768)) - Fladder ([#10768](https://github.com/community-scripts/ProxmoxVE/pull/10768))
### 🚀 Updated Scripts ### 🚀 Updated Scripts
@@ -716,7 +574,7 @@
### 📚 Documentation ### 📚 Documentation
- [gh] New Script template update [@tremor021](https://github.com/tremor021) ([#10607](https://github.com/community-scripts/ProxmoxVE/pull/10607)) - [gh] New Script template update [@tremor021](https://github.com/tremor021) ([#10607](https://github.com/community-scripts/ProxmoxVE/pull/10607))
- chore: bump copyright to 2026 - happy new year [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10585](https://github.com/community-scripts/ProxmoxVE/pull/10585)) - chore: bump copyright to 2026 - happy new year [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10585](https://github.com/community-scripts/ProxmoxVE/pull/10585))
### 📂 Github ### 📂 Github
@@ -797,7 +655,7 @@
### ❔ Uncategorized ### ❔ Uncategorized
- Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496)) - Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496))
- InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497)) - InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497))
## 2026-01-02 ## 2026-01-02
@@ -826,3 +684,157 @@
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- Fix MariaDB runtime directory persistence on container reboot [@Copilot](https://github.com/Copilot) ([#10468](https://github.com/community-scripts/ProxmoxVE/pull/10468)) - Fix MariaDB runtime directory persistence on container reboot [@Copilot](https://github.com/Copilot) ([#10468](https://github.com/community-scripts/ProxmoxVE/pull/10468))
## 2026-01-31
### 🆕 New Scripts
- shelfmark ([#11371](https://github.com/community-scripts/ProxmoxVE/pull/11371))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix: yubal: add git [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11394](https://github.com/community-scripts/ProxmoxVE/pull/11394))
## 2026-01-30
### 🆕 New Scripts
- languagetool ([#11370](https://github.com/community-scripts/ProxmoxVE/pull/11370))
- Ampache ([#11369](https://github.com/community-scripts/ProxmoxVE/pull/11369))
### 🚀 Updated Scripts
- #### 🔧 Refactor
- Refactor: remove redundant PHP_MODULE entries in several scripts [@MickLesk](https://github.com/MickLesk) ([#11362](https://github.com/community-scripts/ProxmoxVE/pull/11362))
- Refactor: Koillection [@MickLesk](https://github.com/MickLesk) ([#11361](https://github.com/community-scripts/ProxmoxVE/pull/11361))
### 💾 Core
- #### 🐞 Bug Fixes
- core: meilisearch - add data migration for version upgrades [@MickLesk](https://github.com/MickLesk) ([#11356](https://github.com/community-scripts/ProxmoxVE/pull/11356))
- #### ✨ New Features
- [tools] Add `fetch_and_deploy_from_url()` [@tremor021](https://github.com/tremor021) ([#11376](https://github.com/community-scripts/ProxmoxVE/pull/11376))
- core: php - improve module handling and prevent installation failures [@MickLesk](https://github.com/MickLesk) ([#11358](https://github.com/community-scripts/ProxmoxVE/pull/11358))
## 2026-01-29
### 🆕 New Scripts
- Alpine-Valkey [@MickLesk](https://github.com/MickLesk) ([#11320](https://github.com/community-scripts/ProxmoxVE/pull/11320))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: Pin version to 2.5.2 [@vhsdream](https://github.com/vhsdream) ([#11335](https://github.com/community-scripts/ProxmoxVE/pull/11335))
- Kollection: Update to php 8.5 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11315](https://github.com/community-scripts/ProxmoxVE/pull/11315))
- Notifiarr: change installation check from apt to systemd service [@MickLesk](https://github.com/MickLesk) ([#11319](https://github.com/community-scripts/ProxmoxVE/pull/11319))
- #### ✨ New Features
- [FEAT] Immich: Enable Maintenance Mode before update [@vhsdream](https://github.com/vhsdream) ([#11342](https://github.com/community-scripts/ProxmoxVE/pull/11342))
- jellyfin: add logrotate instead of reducing log level [@MickLesk](https://github.com/MickLesk) ([#11326](https://github.com/community-scripts/ProxmoxVE/pull/11326))
- core: Add config file handling options | Fix Vikunja update with interactive overwrite [@MickLesk](https://github.com/MickLesk) ([#11317](https://github.com/community-scripts/ProxmoxVE/pull/11317))
- Immich: v2.5.0 [@vhsdream](https://github.com/vhsdream) ([#11240](https://github.com/community-scripts/ProxmoxVE/pull/11240))
- #### 💥 Breaking Changes
- fix: vikunja v1 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11308](https://github.com/community-scripts/ProxmoxVE/pull/11308))
- #### 🔧 Refactor
- Refactor: Byparr [@vhsdream](https://github.com/vhsdream) ([#11338](https://github.com/community-scripts/ProxmoxVE/pull/11338))
- cloudflare: Remove deprecated DNS-over-HTTPS proxy option [@MickLesk](https://github.com/MickLesk) ([#11068](https://github.com/community-scripts/ProxmoxVE/pull/11068))
### 💾 Core
- #### 🐞 Bug Fixes
- build.func: Replace storage variable with searchdomain variable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11322](https://github.com/community-scripts/ProxmoxVE/pull/11322))
### 📂 Github
- Add workflow to lock closed issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11316](https://github.com/community-scripts/ProxmoxVE/pull/11316))
## 2026-01-28
### 🆕 New Scripts
- nodecast-tv ([#11287](https://github.com/community-scripts/ProxmoxVE/pull/11287))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Ubuntu 25.04 VM - Change default start from yes to no [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11292](https://github.com/community-scripts/ProxmoxVE/pull/11292))
- #### ✨ New Features
- various scripts: use setup_meilisearch function [@MickLesk](https://github.com/MickLesk) ([#11259](https://github.com/community-scripts/ProxmoxVE/pull/11259))
- #### 🔧 Refactor
- Refactor: NPMPlus / Default Login [@MickLesk](https://github.com/MickLesk) ([#11262](https://github.com/community-scripts/ProxmoxVE/pull/11262))
### 💾 Core
- #### 🐞 Bug Fixes
- core: sed patch for ram [@lavacano](https://github.com/lavacano) ([#11285](https://github.com/community-scripts/ProxmoxVE/pull/11285))
- Fix installer loop caused by invalid whiptail menu separator [@Mesteriis](https://github.com/Mesteriis) ([#11237](https://github.com/community-scripts/ProxmoxVE/pull/11237))
- core: fix Debian 13 LXC template root ownership bug [@MickLesk](https://github.com/MickLesk) ([#11277](https://github.com/community-scripts/ProxmoxVE/pull/11277))
- tools.func: prevent systemd-tmpfiles failure in unprivileged LXC during deb install [@MickLesk](https://github.com/MickLesk) ([#11271](https://github.com/community-scripts/ProxmoxVE/pull/11271))
- tools.func: fix php "wait_for" hint [@MickLesk](https://github.com/MickLesk) ([#11254](https://github.com/community-scripts/ProxmoxVE/pull/11254))
- #### ✨ New Features
- core: update dynamic values in LXC profile on update_motd_ip [@MickLesk](https://github.com/MickLesk) ([#11268](https://github.com/community-scripts/ProxmoxVE/pull/11268))
- tools.func: add new function - setup_meilisearch [@MickLesk](https://github.com/MickLesk) ([#11258](https://github.com/community-scripts/ProxmoxVE/pull/11258))
### 📂 Github
- github: add GitHub-based versions.json updater [@MickLesk](https://github.com/MickLesk) ([#10021](https://github.com/community-scripts/ProxmoxVE/pull/10021))
### 🌐 Website
- #### ✨ New Features
- Frontend: use github-versions.json for version display [@MickLesk](https://github.com/MickLesk) ([#11281](https://github.com/community-scripts/ProxmoxVE/pull/11281))
- #### 📝 Script Information
- fix: homarr: conf location [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11253](https://github.com/community-scripts/ProxmoxVE/pull/11253))
## 2026-01-27
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: update libraw [@vhsdream](https://github.com/vhsdream) ([#11233](https://github.com/community-scripts/ProxmoxVE/pull/11233))
- #### ✨ New Features
- grist: enable optional enterprise features toggle [@MickLesk](https://github.com/MickLesk) ([#11239](https://github.com/community-scripts/ProxmoxVE/pull/11239))
- #### 🔧 Refactor
- Termix: use nginx.conf from upstream repo [@MickLesk](https://github.com/MickLesk) ([#11228](https://github.com/community-scripts/ProxmoxVE/pull/11228))
### 💾 Core
- #### ✨ New Features
- feat: add NVIDIA driver install prompt for GPU-enabled containers [@devdecrux](https://github.com/devdecrux) ([#11184](https://github.com/community-scripts/ProxmoxVE/pull/11184))
### 📚 Documentation
- doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215))
- changelog: archive old entries to year/month files [@MickLesk](https://github.com/MickLesk) ([#11225](https://github.com/community-scripts/ProxmoxVE/pull/11225))

121
.github/changelogs/2026/02.md generated vendored
View File

@@ -1,121 +0,0 @@
## 2026-02-04
### 🆕 New Scripts
- Wishlist ([#11527](https://github.com/community-scripts/ProxmoxVE/pull/11527))
- WriteFreely ([#11524](https://github.com/community-scripts/ProxmoxVE/pull/11524))
### 💾 Core
- #### ✨ New Features
- core: create vm-core.func from dev [@MickLesk](https://github.com/MickLesk) ([#11528](https://github.com/community-scripts/ProxmoxVE/pull/11528))
### 🌐 Website
- #### 🐞 Bug Fixes
- fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534))
## 2026-02-03
### 🆕 New Scripts
- Wealthfolio ([#11511](https://github.com/community-scripts/ProxmoxVE/pull/11511))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- [FIX] Shelfmark: unpin Chromium version [@vhsdream](https://github.com/vhsdream) ([#11505](https://github.com/community-scripts/ProxmoxVE/pull/11505))
- #### ✨ New Features
- [FEAT] Scanopy: automatically update integrated daemon [@vhsdream](https://github.com/vhsdream) ([#11506](https://github.com/community-scripts/ProxmoxVE/pull/11506))
### 💾 Core
- #### 🐞 Bug Fixes
- [FIX] tools.func: trim spaces in app_lc when checking for gh release [@vhsdream](https://github.com/vhsdream) ([#11512](https://github.com/community-scripts/ProxmoxVE/pull/11512))
### 🌐 Website
- #### 🐞 Bug Fixes
- fix(frontend): decouple table pagination from summary fetching [@ls-root](https://github.com/ls-root) ([#11495](https://github.com/community-scripts/ProxmoxVE/pull/11495))
## 2026-02-02
### 🆕 New Scripts
- rustypaste | Alpine-rustypaste ([#11457](https://github.com/community-scripts/ProxmoxVE/pull/11457))
- KitchenOwl ([#11453](https://github.com/community-scripts/ProxmoxVE/pull/11453))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Grist: Update dependencies [@tremor021](https://github.com/tremor021) ([#11489](https://github.com/community-scripts/ProxmoxVE/pull/11489))
- Allow "downgrade" of libigdgmm12 [@vhsdream](https://github.com/vhsdream) ([#11478](https://github.com/community-scripts/ProxmoxVE/pull/11478))
- Disable NPM install and update due to OpenResty SHA-1 signature issues [@MickLesk](https://github.com/MickLesk) ([#11471](https://github.com/community-scripts/ProxmoxVE/pull/11471))
- #### ✨ New Features
- Refactor: Forgejo & readeck - migrate to codeberg functions [@MickLesk](https://github.com/MickLesk) ([#11460](https://github.com/community-scripts/ProxmoxVE/pull/11460))
- #### 💥 Breaking Changes
- [FIX] Scanopy: remove daemon build [@vhsdream](https://github.com/vhsdream) ([#11444](https://github.com/community-scripts/ProxmoxVE/pull/11444))
- #### 🔧 Refactor
- Refactor: Vaultwarden [@MickLesk](https://github.com/MickLesk) ([#11445](https://github.com/community-scripts/ProxmoxVE/pull/11445))
- various scripts: use ensure_dependencies instead of apt [@MickLesk](https://github.com/MickLesk) ([#11463](https://github.com/community-scripts/ProxmoxVE/pull/11463))
### 🌐 Website
- cleanup(frontend): remove unused /category-view route [@ls-root](https://github.com/ls-root) ([#11461](https://github.com/community-scripts/ProxmoxVE/pull/11461))
- #### ✨ New Features
- feat(frontend): preview tab [@ls-root](https://github.com/ls-root) ([#11475](https://github.com/community-scripts/ProxmoxVE/pull/11475))
## 2026-02-01
### 🚀 Updated Scripts
- fix headers [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11422](https://github.com/community-scripts/ProxmoxVE/pull/11422))
- #### 🐞 Bug Fixes
- 2fauth: export PHP_VERSION for nginx config [@MickLesk](https://github.com/MickLesk) ([#11441](https://github.com/community-scripts/ProxmoxVE/pull/11441))
- Prometheus Paperless NGX Exporter: Set correct binary path in systemd unit file [@andygrunwald](https://github.com/andygrunwald) ([#11438](https://github.com/community-scripts/ProxmoxVE/pull/11438))
- tracearr: install/update new prestart script from upstream [@durzo](https://github.com/durzo) ([#11433](https://github.com/community-scripts/ProxmoxVE/pull/11433))
- n8n: Fix dependencies [@tremor021](https://github.com/tremor021) ([#11429](https://github.com/community-scripts/ProxmoxVE/pull/11429))
- [Hotfix] Bunkerweb update [@vhsdream](https://github.com/vhsdream) ([#11402](https://github.com/community-scripts/ProxmoxVE/pull/11402))
- [Hotfix] Immich: revert healthcheck feature [@vhsdream](https://github.com/vhsdream) ([#11427](https://github.com/community-scripts/ProxmoxVE/pull/11427))
- #### ✨ New Features
- tools.func: add codeberg functions & autocaliweb: migrate from GitHub to Codeberg [@MickLesk](https://github.com/MickLesk) ([#11440](https://github.com/community-scripts/ProxmoxVE/pull/11440))
- Immich Refactor #2 [@vhsdream](https://github.com/vhsdream) ([#11375](https://github.com/community-scripts/ProxmoxVE/pull/11375))
- #### 🔧 Refactor
- WordPress: Refactor [@tremor021](https://github.com/tremor021) ([#11408](https://github.com/community-scripts/ProxmoxVE/pull/11408))
- Refactor: Whisparr [@tremor021](https://github.com/tremor021) ([#11411](https://github.com/community-scripts/ProxmoxVE/pull/11411))
### 💾 Core
- #### ✨ New Features
- [tools]: Update `fetch_and_deply_from_url()` [@tremor021](https://github.com/tremor021) ([#11410](https://github.com/community-scripts/ProxmoxVE/pull/11410))
### 🌐 Website
- feat(frontend): implement UX refinements and syntax highlighting [@ls-root](https://github.com/ls-root) ([#11423](https://github.com/community-scripts/ProxmoxVE/pull/11423))
- #### ✨ New Features
- feat(frontend): add contribution CTA to empty search state [@ls-root](https://github.com/ls-root) ([#11412](https://github.com/community-scripts/ProxmoxVE/pull/11412))

View File

@@ -54,9 +54,8 @@ jobs:
console.log(`Cutoff date: ${cutoffDate.toISOString().split('T')[0]}`); console.log(`Cutoff date: ${cutoffDate.toISOString().split('T')[0]}`);
// Read changelog and normalize line endings // Read changelog
let content = await fs.readFile(CHANGELOG_PATH, 'utf-8'); const content = await fs.readFile(CHANGELOG_PATH, 'utf-8');
content = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
const lines = content.split('\n'); const lines = content.split('\n');
// Parse entries // Parse entries
@@ -67,39 +66,9 @@ jobs:
let currentDate = null; let currentDate = null;
let currentContent = []; let currentContent = [];
let inHeader = true; let inHeader = true;
let inOldHistory = false;
let historyDetailsDepth = 0;
for (let i = 0; i < lines.length; i++) { for (const line of lines) {
const line = lines[i];
const match = line.match(datePattern); const match = line.match(datePattern);
// Detect the start of History section: <details> followed by line with 📜 History
if (inHeader && !inOldHistory && line.trim() === '<details>') {
// Look ahead to see if this is the History section
const nextLine = lines[i + 1] || '';
if (nextLine.includes('📜 History')) {
inOldHistory = true;
historyDetailsDepth = 1;
continue;
}
}
// Track nested details tags to find the end of History section
if (inOldHistory) {
if (line.trim() === '<details>') {
historyDetailsDepth++;
}
if (line.trim() === '</details>') {
historyDetailsDepth--;
if (historyDetailsDepth === 0) {
// We've closed the main History details tag
inOldHistory = false;
}
}
continue;
}
if (match) { if (match) {
inHeader = false; inHeader = false;
@@ -179,55 +148,30 @@ jobs:
let existingContent = ''; let existingContent = '';
try { try {
existingContent = await fs.readFile(monthPath, 'utf-8'); existingContent = await fs.readFile(monthPath, 'utf-8');
// Normalize line endings to prevent regex issues
existingContent = existingContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
} catch (e) { } catch (e) {
// File doesn't exist // File doesn't exist
} }
// Parse existing entries into a Map (date -> content) for deduplication // Merge new entries with existing (avoid duplicates)
const allEntries = new Map(); const existingDates = new Set();
const existingDatePattern = /^## (\d{4}-\d{2}-\d{2})$/gm;
// Helper function to parse entries from content let match;
const parseEntries = (content) => { while ((match = existingDatePattern.exec(existingContent)) !== null) {
const entries = new Map(); existingDates.add(match[1]);
const parts = content.split(/(?=^## \d{4}-\d{2}-\d{2}$)/m);
for (const part of parts) {
const trimmed = part.trim();
if (!trimmed) continue;
const dateMatch = trimmed.match(/^## (\d{4}-\d{2}-\d{2})/);
if (dateMatch) {
entries.set(dateMatch[1], trimmed);
}
}
return entries;
};
// Parse existing content
if (existingContent) {
const existingEntries = parseEntries(existingContent);
for (const [date, content] of existingEntries) {
allEntries.set(date, content);
}
} }
// Add new entries (existing entries take precedence to avoid overwriting) const newEntries = archiveData[year][month].filter(entry => {
let addedCount = 0;
for (const entry of archiveData[year][month]) {
const dateMatch = entry.match(/^## (\d{4}-\d{2}-\d{2})/); const dateMatch = entry.match(/^## (\d{4}-\d{2}-\d{2})/);
if (dateMatch && !allEntries.has(dateMatch[1])) { return dateMatch && !existingDates.has(dateMatch[1]);
allEntries.set(dateMatch[1], entry.trim()); });
addedCount++;
}
}
// Sort entries by date (newest first) and write if (newEntries.length > 0) {
const sortedDates = [...allEntries.keys()].sort().reverse(); const allContent = existingContent
const sortedContent = sortedDates.map(date => allEntries.get(date)).join('\n\n'); ? existingContent + '\n\n' + newEntries.join('\n\n')
: newEntries.join('\n\n');
if (addedCount > 0 || !existingContent) { await fs.writeFile(monthPath, allContent, 'utf-8');
await fs.writeFile(monthPath, sortedContent + '\n', 'utf-8'); console.log(`Updated: ${monthPath} (+${newEntries.length} entries)`);
console.log(`Updated: ${monthPath} (${allEntries.size} total entries, +${addedCount} new)`);
} }
} }
} }
@@ -274,8 +218,7 @@ jobs:
// Count entries in month file // Count entries in month file
let entryCount = 0; let entryCount = 0;
try { try {
let monthContent = await fs.readFile(monthPath, 'utf-8'); const monthContent = await fs.readFile(monthPath, 'utf-8');
monthContent = monthContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
entryCount = (monthContent.match(/^## \d{4}-\d{2}-\d{2}$/gm) || []).length; entryCount = (monthContent.match(/^## \d{4}-\d{2}-\d{2}$/gm) || []).length;
} catch (e) { } catch (e) {
entryCount = (archiveData[year]?.[month] || []).length; entryCount = (archiveData[year]?.[month] || []).length;

View File

@@ -21,14 +21,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
<details> <details>
<summary><h4>February (4 entries)</h4></summary> <summary><h4>January - 27 entries</h4></summary>
[View February 2026 Changelog](.github/changelogs/2026/02.md)
</details>
<details>
<summary><h4>January (31 entries)</h4></summary>
[View January 2026 Changelog](.github/changelogs/2026/01.md) [View January 2026 Changelog](.github/changelogs/2026/01.md)
@@ -398,39 +391,386 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-02-05
### 🆕 New Scripts
- OpenCloud ([#11538](https://github.com/community-scripts/ProxmoxVE/pull/11538)) <details>
- Nginx-UI ([#11573](https://github.com/community-scripts/ProxmoxVE/pull/11573)) <summary><h2>📜 History</h2></summary>
- New: SQL-Server 2025 | Refactor SQL-Server 2022 [@MickLesk](https://github.com/MickLesk) ([#11546](https://github.com/community-scripts/ProxmoxVE/pull/11546))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes <details>
<summary><h3>2026</h3></summary>
- Wanderer: Fix repo [@tremor021](https://github.com/tremor021) ([#11567](https://github.com/community-scripts/ProxmoxVE/pull/11567))
- #### ✨ New Features <details>
<summary><h4>January (31 entries)</h4></summary>
- Refactor: Docker-VM (Multi-OS / Cloud-Init / Stabilization) [@MickLesk](https://github.com/MickLesk) ([#9047](https://github.com/community-scripts/ProxmoxVE/pull/9047)) [View January 2026 Changelog](.github/changelogs/2026/01.md)
### 💾 Core </details>
- #### ✨ New Features </details>
- cloud-init: add interactive SSH key discovery and selection [@MickLesk](https://github.com/MickLesk) ([#11547](https://github.com/community-scripts/ProxmoxVE/pull/11547)) <details>
<summary><h3>2025</h3></summary>
### 📚 Documentation
- github: extend docs / contribution / templates [@MickLesk](https://github.com/MickLesk) ([#10921](https://github.com/community-scripts/ProxmoxVE/pull/10921)) <details>
<summary><h4>December (31 entries)</h4></summary>
### 🌐 Website [View December 2025 Changelog](.github/changelogs/2025/12.md)
- #### 🐞 Bug Fixes </details>
- fix(frontend): theme respective syntax highlighting [@ls-root](https://github.com/ls-root) ([#11565](https://github.com/community-scripts/ProxmoxVE/pull/11565)) <details>
<summary><h4>November (29 entries)</h4></summary>
[View November 2025 Changelog](.github/changelogs/2025/11.md)
</details>
<details>
<summary><h4>October (30 entries)</h4></summary>
[View October 2025 Changelog](.github/changelogs/2025/10.md)
</details>
<details>
<summary><h4>September (29 entries)</h4></summary>
[View September 2025 Changelog](.github/changelogs/2025/09.md)
</details>
<details>
<summary><h4>August (30 entries)</h4></summary>
[View August 2025 Changelog](.github/changelogs/2025/08.md)
</details>
<details>
<summary><h4>July (29 entries)</h4></summary>
[View July 2025 Changelog](.github/changelogs/2025/07.md)
</details>
<details>
<summary><h4>June (29 entries)</h4></summary>
[View June 2025 Changelog](.github/changelogs/2025/06.md)
</details>
<details>
<summary><h4>May (30 entries)</h4></summary>
[View May 2025 Changelog](.github/changelogs/2025/05.md)
</details>
<details>
<summary><h4>April (25 entries)</h4></summary>
[View April 2025 Changelog](.github/changelogs/2025/04.md)
</details>
<details>
<summary><h4>March (30 entries)</h4></summary>
[View March 2025 Changelog](.github/changelogs/2025/03.md)
</details>
<details>
<summary><h4>February (26 entries)</h4></summary>
[View February 2025 Changelog](.github/changelogs/2025/02.md)
</details>
<details>
<summary><h4>January (27 entries)</h4></summary>
[View January 2025 Changelog](.github/changelogs/2025/01.md)
</details>
</details>
<details>
<summary><h3>2024</h3></summary>
<details>
<summary><h4>December (22 entries)</h4></summary>
[View December 2024 Changelog](.github/changelogs/2024/12.md)
</details>
<details>
<summary><h4>November (15 entries)</h4></summary>
[View November 2024 Changelog](.github/changelogs/2024/11.md)
</details>
<details>
<summary><h4>October (9 entries)</h4></summary>
[View October 2024 Changelog](.github/changelogs/2024/10.md)
</details>
<details>
<summary><h4>September (1 entries)</h4></summary>
[View September 2024 Changelog](.github/changelogs/2024/09.md)
</details>
<details>
<summary><h4>August (2 entries)</h4></summary>
[View August 2024 Changelog](.github/changelogs/2024/08.md)
</details>
<details>
<summary><h4>July (0 entries)</h4></summary>
[View July 2024 Changelog](.github/changelogs/2024/07.md)
</details>
<details>
<summary><h4>June (8 entries)</h4></summary>
[View June 2024 Changelog](.github/changelogs/2024/06.md)
</details>
<details>
<summary><h4>May (16 entries)</h4></summary>
[View May 2024 Changelog](.github/changelogs/2024/05.md)
</details>
<details>
<summary><h4>April (14 entries)</h4></summary>
[View April 2024 Changelog](.github/changelogs/2024/04.md)
</details>
<details>
<summary><h4>March (5 entries)</h4></summary>
[View March 2024 Changelog](.github/changelogs/2024/03.md)
</details>
<details>
<summary><h4>February (9 entries)</h4></summary>
[View February 2024 Changelog](.github/changelogs/2024/02.md)
</details>
<details>
<summary><h4>January (9 entries)</h4></summary>
[View January 2024 Changelog](.github/changelogs/2024/01.md)
</details>
</details>
<details>
<summary><h3>2023</h3></summary>
<details>
<summary><h4>December (3 entries)</h4></summary>
[View December 2023 Changelog](.github/changelogs/2023/12.md)
</details>
<details>
<summary><h4>November (3 entries)</h4></summary>
[View November 2023 Changelog](.github/changelogs/2023/11.md)
</details>
<details>
<summary><h4>October (7 entries)</h4></summary>
[View October 2023 Changelog](.github/changelogs/2023/10.md)
</details>
<details>
<summary><h4>September (10 entries)</h4></summary>
[View September 2023 Changelog](.github/changelogs/2023/09.md)
</details>
<details>
<summary><h4>August (7 entries)</h4></summary>
[View August 2023 Changelog](.github/changelogs/2023/08.md)
</details>
<details>
<summary><h4>July (5 entries)</h4></summary>
[View July 2023 Changelog](.github/changelogs/2023/07.md)
</details>
<details>
<summary><h4>June (5 entries)</h4></summary>
[View June 2023 Changelog](.github/changelogs/2023/06.md)
</details>
<details>
<summary><h4>May (8 entries)</h4></summary>
[View May 2023 Changelog](.github/changelogs/2023/05.md)
</details>
<details>
<summary><h4>April (8 entries)</h4></summary>
[View April 2023 Changelog](.github/changelogs/2023/04.md)
</details>
<details>
<summary><h4>March (8 entries)</h4></summary>
[View March 2023 Changelog](.github/changelogs/2023/03.md)
</details>
<details>
<summary><h4>February (6 entries)</h4></summary>
[View February 2023 Changelog](.github/changelogs/2023/02.md)
</details>
<details>
<summary><h4>January (15 entries)</h4></summary>
[View January 2023 Changelog](.github/changelogs/2023/01.md)
</details>
</details>
<details>
<summary><h3>2022</h3></summary>
<details>
<summary><h4>December (7 entries)</h4></summary>
[View December 2022 Changelog](.github/changelogs/2022/12.md)
</details>
<details>
<summary><h4>November (7 entries)</h4></summary>
[View November 2022 Changelog](.github/changelogs/2022/11.md)
</details>
<details>
<summary><h4>October (2 entries)</h4></summary>
[View October 2022 Changelog](.github/changelogs/2022/10.md)
</details>
<details>
<summary><h4>September (9 entries)</h4></summary>
[View September 2022 Changelog](.github/changelogs/2022/09.md)
</details>
<details>
<summary><h4>August (7 entries)</h4></summary>
[View August 2022 Changelog](.github/changelogs/2022/08.md)
</details>
<details>
<summary><h4>July (10 entries)</h4></summary>
[View July 2022 Changelog](.github/changelogs/2022/07.md)
</details>
<details>
<summary><h4>June (1 entries)</h4></summary>
[View June 2022 Changelog](.github/changelogs/2022/06.md)
</details>
<details>
<summary><h4>May (8 entries)</h4></summary>
[View May 2022 Changelog](.github/changelogs/2022/05.md)
</details>
<details>
<summary><h4>April (13 entries)</h4></summary>
[View April 2022 Changelog](.github/changelogs/2022/04.md)
</details>
<details>
<summary><h4>March (20 entries)</h4></summary>
[View March 2022 Changelog](.github/changelogs/2022/03.md)
</details>
<details>
<summary><h4>February (15 entries)</h4></summary>
[View February 2022 Changelog](.github/changelogs/2022/02.md)
</details>
<details>
<summary><h4>January (3 entries)</h4></summary>
[View January 2022 Changelog](.github/changelogs/2022/01.md)
</details>
</details>
</details>
## 2026-02-04 ## 2026-02-04
@@ -439,37 +779,12 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Wishlist ([#11527](https://github.com/community-scripts/ProxmoxVE/pull/11527)) - Wishlist ([#11527](https://github.com/community-scripts/ProxmoxVE/pull/11527))
- WriteFreely ([#11524](https://github.com/community-scripts/ProxmoxVE/pull/11524)) - WriteFreely ([#11524](https://github.com/community-scripts/ProxmoxVE/pull/11524))
### 🚀 Updated Scripts
- Add log directory and permissions for koillection [@shineangelic](https://github.com/shineangelic) ([#11553](https://github.com/community-scripts/ProxmoxVE/pull/11553))
- #### 🐞 Bug Fixes
- [FIX] Scanopy: ensure Scanopy Daemon update [@vhsdream](https://github.com/vhsdream) ([#11541](https://github.com/community-scripts/ProxmoxVE/pull/11541))
- Immich: pin version to 2.5.3 [@vhsdream](https://github.com/vhsdream) ([#11515](https://github.com/community-scripts/ProxmoxVE/pull/11515))
### 💾 Core ### 💾 Core
- #### ✨ New Features - #### ✨ New Features
- core: create vm-core.func from dev [@MickLesk](https://github.com/MickLesk) ([#11528](https://github.com/community-scripts/ProxmoxVE/pull/11528)) - core: create vm-core.func from dev [@MickLesk](https://github.com/MickLesk) ([#11528](https://github.com/community-scripts/ProxmoxVE/pull/11528))
### 🧰 Tools
- #### ✨ New Features
- [ADDON] Immich Public Proxy addon [@vhsdream](https://github.com/vhsdream) ([#11518](https://github.com/community-scripts/ProxmoxVE/pull/11518))
### 🌐 Website
- #### 🐞 Bug Fixes
- fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534))
### ❔ Uncategorized
- [FIX] Immich Public Proxy docs link [@vhsdream](https://github.com/vhsdream) ([#11543](https://github.com/community-scripts/ProxmoxVE/pull/11543))
## 2026-02-03 ## 2026-02-03
### 🆕 New Scripts ### 🆕 New Scripts
@@ -1323,3 +1638,64 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🔧 Refactor - #### 🔧 Refactor
- Refactor: IP-Tag (Multiple IP / Performance / Execution Time) [@MickLesk](https://github.com/MickLesk) ([#10558](https://github.com/community-scripts/ProxmoxVE/pull/10558)) - Refactor: IP-Tag (Multiple IP / Performance / Execution Time) [@MickLesk](https://github.com/MickLesk) ([#10558](https://github.com/community-scripts/ProxmoxVE/pull/10558))
## 2026-01-04
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- PocketID: Update PocketID for 2.x [@tremor021](https://github.com/tremor021) ([#10506](https://github.com/community-scripts/ProxmoxVE/pull/10506))
- fix: reitti: nginx [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10511](https://github.com/community-scripts/ProxmoxVE/pull/10511))
- MagicMirror: bump to nodejs 24 [@MickLesk](https://github.com/MickLesk) ([#10534](https://github.com/community-scripts/ProxmoxVE/pull/10534))
- #### 🔧 Refactor
- Refactor: SFTPGo [@tremor021](https://github.com/tremor021) ([#10518](https://github.com/community-scripts/ProxmoxVE/pull/10518))
- Refactor: Pelican Wings [@tremor021](https://github.com/tremor021) ([#10517](https://github.com/community-scripts/ProxmoxVE/pull/10517))
- Refactor: Pelican Panel [@tremor021](https://github.com/tremor021) ([#10516](https://github.com/community-scripts/ProxmoxVE/pull/10516))
- Refactor: Audiobookshelf [@tremor021](https://github.com/tremor021) ([#10519](https://github.com/community-scripts/ProxmoxVE/pull/10519))
### 💾 Core
- #### 🐞 Bug Fixes
- Export IPV6_METHOD to trigger verb_ip6() function [@remz1337](https://github.com/remz1337) ([#10538](https://github.com/community-scripts/ProxmoxVE/pull/10538))
### 🌐 Website
- #### 📝 Script Information
- Prowlarr: Update config_path [@tremor021](https://github.com/tremor021) ([#10504](https://github.com/community-scripts/ProxmoxVE/pull/10504))
## 2026-01-03
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix ownership and permissions for InvoiceNinja setup [@twinzdragonz](https://github.com/twinzdragonz) ([#10298](https://github.com/community-scripts/ProxmoxVE/pull/10298))
- Fix headscale Caddyfile to pass non-API URLs [@IlyaSemenov](https://github.com/IlyaSemenov) ([#10493](https://github.com/community-scripts/ProxmoxVE/pull/10493))
### 💾 Core
- #### 🔧 Refactor
- [core]: Preserve log files [@tremor021](https://github.com/tremor021) ([#10509](https://github.com/community-scripts/ProxmoxVE/pull/10509))
### ❔ Uncategorized
- Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496))
- InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497))
## 2026-01-02
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix Intel Level Zero package conflict on Debian 13 [@Copilot](https://github.com/Copilot) ([#10467](https://github.com/community-scripts/ProxmoxVE/pull/10467))
### ❔ Uncategorized
- Extend guidance for changing the immich upload location for #10447 [@jshprentz](https://github.com/jshprentz) ([#10475](https://github.com/community-scripts/ProxmoxVE/pull/10475))

View File

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

View File

@@ -1,6 +0,0 @@
____ ________ __
/ __ \____ ___ ____ / ____/ /___ __ ______/ /
/ / / / __ \/ _ \/ __ \/ / / / __ \/ / / / __ /
/ /_/ / /_/ / __/ / / / /___/ / /_/ / /_/ / /_/ /
\____/ .___/\___/_/ /_/\____/_/\____/\__,_/\__,_/
/_/

View File

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

View File

@@ -36,6 +36,10 @@ function update_script() {
exit exit
fi fi
setup_uv
PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')"
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
if [[ ! -f /etc/apt/preferences.d/preferences ]]; then if [[ ! -f /etc/apt/preferences.d/preferences ]]; then
msg_info "Adding Debian Testing repo" msg_info "Adding Debian Testing repo"
sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources
@@ -105,7 +109,7 @@ EOF
msg_ok "Image-processing libraries up to date" msg_ok "Image-processing libraries up to date"
fi fi
RELEASE="2.5.3" RELEASE="2.5.2"
if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then
if [[ $(cat ~/.immich) > "2.5.1" ]]; then if [[ $(cat ~/.immich) > "2.5.1" ]]; then
msg_info "Enabling Maintenance Mode" msg_info "Enabling Maintenance Mode"
@@ -160,10 +164,7 @@ EOF
rm -rf "${APP_DIR:?}"/* rm -rf "${APP_DIR:?}"/*
) )
setup_uv
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v${RELEASE}" "$SRC_DIR" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v${RELEASE}" "$SRC_DIR"
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1]' ${SRC_DIR}/package.json)"
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
msg_info "Updating Immich web and microservices" msg_info "Updating Immich web and microservices"
cd "$SRC_DIR"/server cd "$SRC_DIR"/server

View File

@@ -59,8 +59,6 @@ function update_script() {
$STD yarn install $STD yarn install
$STD yarn build $STD yarn build
mkdir -p /opt/koillection/public/uploads mkdir -p /opt/koillection/public/uploads
mkdir -p /opt/koillection/var/log
chown -R www-data:www-data /opt/koillection/var/log
chown -R www-data:www-data /opt/koillection/public/uploads chown -R www-data:www-data /opt/koillection/public/uploads
rm -r /opt/koillection-backup rm -r /opt/koillection-backup

View File

@@ -1,68 +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://nginxui.com
APP="Nginx-UI"
var_tags="${var_tags:-webserver;nginx;proxy}"
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_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /usr/local/bin/nginx-ui ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "nginx-ui" "0xJacky/nginx-ui"; then
msg_info "Stopping Service"
systemctl stop nginx-ui
msg_ok "Stopped Service"
msg_info "Backing up Configuration"
cp /usr/local/etc/nginx-ui/app.ini /tmp/nginx-ui-app.ini.bak
msg_ok "Backed up Configuration"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginx-ui" "0xJacky/nginx-ui" "prebuild" "latest" "/opt/nginx-ui" "nginx-ui-linux-64.tar.gz"
msg_info "Updating Binary"
cp /opt/nginx-ui/nginx-ui /usr/local/bin/nginx-ui
chmod +x /usr/local/bin/nginx-ui
rm -rf /opt/nginx-ui
msg_ok "Updated Binary"
msg_info "Restoring Configuration"
mv /tmp/nginx-ui-app.ini.bak /usr/local/etc/nginx-ui/app.ini
msg_ok "Restored Configuration"
msg_info "Starting Service"
systemctl start nginx-ui
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}:9000${CL}"

View File

@@ -1,60 +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: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://opencloud.eu
APP="OpenCloud"
var_tags="${var_tags:-files;cloud}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-20}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /etc/opencloud ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
RELEASE="v5.0.1"
if check_for_gh_release "opencloud" "opencloud-eu/opencloud" "${RELEASE}"; then
msg_info "Stopping services"
systemctl stop opencloud opencloud-wopi
msg_ok "Stopped services"
msg_info "Updating packages"
$STD apt-get update
$STD apt-get dist-upgrade -y
msg_ok "Updated packages"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "${RELEASE}" "/usr/bin" "opencloud-*-linux-amd64"
msg_info "Starting services"
systemctl start opencloud opencloud-wopi
msg_ok "Started services"
msg_ok "Updated successfully"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}https://<your-OpenCloud-FQDN>${CL}"

View File

@@ -29,7 +29,7 @@ function update_script() {
exit exit
fi fi
if check_for_gh_release "Scanopy" "scanopy/scanopy"; then if check_for_gh_release "scanopy" "scanopy/scanopy"; then
msg_info "Stopping services" msg_info "Stopping services"
systemctl stop scanopy-server systemctl stop scanopy-server
[[ -f /etc/systemd/system/scanopy-daemon.service ]] && systemctl stop scanopy-daemon [[ -f /etc/systemd/system/scanopy-daemon.service ]] && systemctl stop scanopy-daemon
@@ -40,7 +40,7 @@ function update_script() {
[[ -f /opt/scanopy/oidc.toml ]] && cp /opt/scanopy/oidc.toml /opt/scanopy.oidc.toml [[ -f /opt/scanopy/oidc.toml ]] && cp /opt/scanopy/oidc.toml /opt/scanopy.oidc.toml
msg_ok "Backed up configurations" msg_ok "Backed up configurations"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
ensure_dependencies pkg-config libssl-dev ensure_dependencies pkg-config libssl-dev
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')" TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
@@ -61,22 +61,19 @@ function update_script() {
$STD npm run build $STD npm run build
msg_ok "Created frontend UI" msg_ok "Created frontend UI"
msg_info "Building Scanopy Server (patience)" msg_info "Building scanopy-server (patience)"
cd /opt/scanopy/backend cd /opt/scanopy/backend
$STD cargo build --release --bin server $STD cargo build --release --bin server
mv ./target/release/server /usr/bin/scanopy-server mv ./target/release/server /usr/bin/scanopy-server
msg_ok "Built Scanopy Server" msg_ok "Built scanopy-server"
if [[ -f /etc/systemd/system/scanopy-daemon.service ]]; then [[ -f /etc/systemd/system/scanopy-daemon.service ]] &&
fetch_and_deploy_gh_release "Scanopy Daemon" "scanopy/scanopy" "singlefile" "latest" "/usr/local/bin" "scanopy-daemon-linux-amd64" fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "singlefile" "latest" "/usr/local/bin" "scanopy-daemon-linux-amd64" &&
mv "/usr/local/bin/Scanopy Daemon" /usr/local/bin/scanopy-daemon rm -f /usr/bin/scanopy-daemon ~/configure_daemon.sh &&
rm -f /usr/bin/scanopy-daemon ~/configure_daemon.sh
sed -i -e 's|usr/bin|usr/local/bin|' \ sed -i -e 's|usr/bin|usr/local/bin|' \
-e 's/push/daemon_poll/' \ -e 's/push/daemon_poll/' \
-e 's/pull/server_poll/' /etc/systemd/system/scanopy-daemon.service -e 's/pull/server_poll/' /etc/systemd/system/scanopy-daemon.service &&
systemctl daemon-reload systemctl daemon-reload
msg_ok "Updated Scanopy Daemon"
fi
msg_info "Starting services" msg_info "Starting services"
systemctl start scanopy-server systemctl start scanopy-server

View File

@@ -27,8 +27,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!" msg_error "No ${APP} Installation Found!"
exit exit
fi fi
msg_info "Updating SQL Server 2022" msg_info "Updating ${APP} LXC"
rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh
$STD apt update $STD apt update
$STD apt -y upgrade $STD apt -y upgrade
msg_ok "Updated successfully!" msg_ok "Updated successfully!"

View File

@@ -1,45 +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://www.microsoft.com/en-us/sql-server/sql-server-2025
APP="SQL Server 2025"
var_tags="${var_tags:-sql;database}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-10}"
var_os="${var_os:-ubuntu}"
var_version="${var_version:-24.04}"
var_unprivileged="${var_unprivileged:-0}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/mssql ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating SQL Server 2025"
rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh
$STD apt update
$STD apt -y upgrade
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 IP:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}${IP}:1433${CL}"

View File

@@ -34,7 +34,7 @@ function update_script() {
systemctl stop wanderer-web systemctl stop wanderer-web
msg_ok "Stopped service" msg_ok "Stopped service"
fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" fetch_and_deploy_gh_release "wanderer" "Flomp/wanderer" "tarball" "latest" "/opt/wanderer/source"
msg_info "Updating wanderer" msg_info "Updating wanderer"
cd /opt/wanderer/source/db cd /opt/wanderer/source/db

View File

@@ -1,868 +0,0 @@
# 🤖 AI Contribution Guidelines for ProxmoxVE
> **This documentation is intended for all AI assistants (GitHub Copilot, Claude, ChatGPT, etc.) contributing to this project.**
## 🎯 Core Principles
### 1. **Maximum Use of `tools.func` Functions**
We have an extensive library of helper functions. **NEVER** implement your own solutions when a function already exists!
### 2. **No Pointless Variables**
Only create variables when they:
- Are used multiple times
- Improve readability
- Are intended for configuration
### 3. **Consistent Script Structure**
All scripts follow an identical structure. Deviations are not acceptable.
### 4. **Bare-Metal Installation**
We do **NOT use Docker** for our installation scripts. All applications are installed directly on the system.
---
## 📁 Script Types and Their Structure
### CT Script (`ct/AppName.sh`)
```bash
#!/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: AuthorName (GitHubUsername)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://application-url.com
APP="AppName"
var_tags="${var_tags:-tag1;tag2;tag3}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
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 [[ ! -d /opt/appname ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "appname" "YourUsername/YourRepo"; then
msg_info "Stopping Service"
systemctl stop appname
msg_ok "Stopped Service"
msg_info "Backing up Data"
cp -r /opt/appname/data /opt/appname_data_backup
msg_ok "Backed up Data"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname"
# Build steps...
msg_info "Restoring Data"
cp -r /opt/appname_data_backup/. /opt/appname/data
rm -rf /opt/appname_data_backup
msg_ok "Restored Data"
msg_info "Starting Service"
systemctl start appname
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}:PORT${CL}"
```
### Install Script (`install/AppName-install.sh`)
```bash
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: AuthorName (GitHubUsername)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://application-url.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-get install -y \
dependency1 \
dependency2
msg_ok "Installed Dependencies"
# Runtime Setup (ALWAYS use our functions!)
NODE_VERSION="22" setup_nodejs
# or
PG_VERSION="16" setup_postgresql
# or
setup_uv
# etc.
fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname"
msg_info "Setting up Application"
cd /opt/appname
# Build/Setup Schritte...
msg_ok "Set up Application"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/appname.service
[Unit]
Description=AppName Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/appname
ExecStart=/path/to/executable
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now appname
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc
```
---
## 🔧 Available Helper Functions
### Release Management
| Function | Description | Example |
| ----------------------------- | ----------------------------------- | ------------------------------------------------------------- |
| `fetch_and_deploy_gh_release` | Fetches and installs GitHub Release | `fetch_and_deploy_gh_release "app" "owner/repo"` |
| `check_for_gh_release` | Checks for new version | `if check_for_gh_release "app" "YourUsername/YourRepo"; then` |
**Modes for `fetch_and_deploy_gh_release`:**
```bash
# Tarball/Source (Standard)
fetch_and_deploy_gh_release "appname" "owner/repo"
# Binary (.deb)
fetch_and_deploy_gh_release "appname" "owner/repo" "binary"
# Prebuilt Archive
fetch_and_deploy_gh_release "appname" "owner/repo" "prebuild" "latest" "/opt/appname" "filename.tar.gz"
# Single Binary
fetch_and_deploy_gh_release "appname" "owner/repo" "singlefile" "latest" "/opt/appname" "binary-linux-amd64"
```
**Clean Install Flag:**
```bash
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo"
```
### Runtime/Language Setup
| Function | Variable(s) | Example |
| -------------- | ----------------------------- | ---------------------------------------------------- |
| `setup_nodejs` | `NODE_VERSION`, `NODE_MODULE` | `NODE_VERSION="22" setup_nodejs` |
| `setup_uv` | `PYTHON_VERSION` | `PYTHON_VERSION="3.12" setup_uv` |
| `setup_go` | `GO_VERSION` | `GO_VERSION="1.22" setup_go` |
| `setup_rust` | `RUST_VERSION`, `RUST_CRATES` | `RUST_CRATES="monolith" setup_rust` |
| `setup_ruby` | `RUBY_VERSION` | `RUBY_VERSION="3.3" setup_ruby` |
| `setup_java` | `JAVA_VERSION` | `JAVA_VERSION="21" setup_java` |
| `setup_php` | `PHP_VERSION`, `PHP_MODULES` | `PHP_VERSION="8.3" PHP_MODULES="redis,gd" setup_php` |
### Database Setup
| Function | Variable(s) | Example |
| --------------------- | ------------------------------------ | ----------------------------------------------------------- |
| `setup_postgresql` | `PG_VERSION`, `PG_MODULES` | `PG_VERSION="16" setup_postgresql` |
| `setup_postgresql_db` | `PG_DB_NAME`, `PG_DB_USER` | `PG_DB_NAME="mydb" PG_DB_USER="myuser" setup_postgresql_db` |
| `setup_mariadb_db` | `MARIADB_DB_NAME`, `MARIADB_DB_USER` | `MARIADB_DB_NAME="mydb" setup_mariadb_db` |
| `setup_mysql` | `MYSQL_VERSION` | `setup_mysql` |
| `setup_mongodb` | `MONGO_VERSION` | `setup_mongodb` |
| `setup_clickhouse` | - | `setup_clickhouse` |
### Tools & Utilities
| Function | Description |
| ------------------- | ---------------------------------- |
| `setup_adminer` | Installs Adminer for DB management |
| `setup_composer` | Install PHP Composer |
| `setup_ffmpeg` | Install FFmpeg |
| `setup_imagemagick` | Install ImageMagick |
| `setup_gs` | Install Ghostscript |
| `setup_hwaccel` | Configure hardware acceleration |
### Helper Utilities
| Function | Description | Example |
| ----------------------------- | ---------------------------- | ----------------------------------------- |
| `import_local_ip` | Sets `$LOCAL_IP` variable | `import_local_ip` |
| `ensure_dependencies` | Checks/installs dependencies | `ensure_dependencies curl jq` |
| `install_packages_with_retry` | APT install with retry | `install_packages_with_retry nginx redis` |
---
## ❌ Anti-Patterns (NEVER use!)
### 1. Pointless Variables
```bash
# ❌ WRONG - unnecessary variables
APP_NAME="myapp"
APP_DIR="/opt/${APP_NAME}"
APP_USER="root"
APP_PORT="3000"
cd $APP_DIR
# ✅ CORRECT - use directly
cd /opt/myapp
```
### 2. Custom Download Logic
```bash
# ❌ WRONG - custom wget/curl logic
RELEASE=$(curl -s https://api.github.com/repos/YourUsername/YourRepo/releases/latest | jq -r '.tag_name')
wget https://github.com/YourUsername/YourRepo/archive/${RELEASE}.tar.gz
tar -xzf ${RELEASE}.tar.gz
mv repo-${RELEASE} /opt/myapp
# ✅ CORRECT - use our function
fetch_and_deploy_gh_release "myapp" "YourUsername/YourRepo" "tarball" "latest" "/opt/myapp"
```
### 3. Custom Version-Check Logic
```bash
# ❌ WRONG - custom version check
CURRENT=$(cat /opt/myapp/version.txt)
LATEST=$(curl -s https://api.github.com/repos/YourUsername/YourRepo/releases/latest | jq -r '.tag_name')
if [[ "$CURRENT" != "$LATEST" ]]; then
# update...
fi
# ✅ CORRECT - use our function
if check_for_gh_release "myapp" "YourUsername/YourRepo"; then
# update...
fi
```
### 4. Docker-based Installation
```bash
# ❌ WRONG - using Docker
docker pull myapp/myapp:latest
docker run -d --name myapp myapp/myapp:latest
# ✅ CORRECT - Bare-Metal Installation
fetch_and_deploy_gh_release "myapp" "YourUsername/YourRepo"
npm install && npm run build
```
### 5. Custom Runtime Installation
```bash
# ❌ WRONG - custom Node.js installation
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
# ✅ CORRECT - use our function
NODE_VERSION="22" setup_nodejs
```
### 6. Redundant echo Statements
```bash
# ❌ WRONG - custom logging messages
echo "Installing dependencies..."
apt install -y curl
echo "Done!"
# ✅ CORRECT - use msg_info/msg_ok
msg_info "Installing Dependencies"
$STD apt install -y curl
msg_ok "Installed Dependencies"
```
### 7. Missing $STD Usage
```bash
# ❌ WRONG - apt without $STD
apt install -y nginx
# ✅ CORRECT - with $STD for silent output
$STD apt install -y nginx
```
### 8. Wrapping `tools.func` Functions in msg Blocks
```bash
# ❌ WRONG - tools.func functions have their own msg_info/msg_ok!
msg_info "Installing Node.js"
NODE_VERSION="22" setup_nodejs
msg_ok "Installed Node.js"
msg_info "Updating Application"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname"
msg_ok "Updated Application"
# ✅ CORRECT - call directly without msg wrapper
NODE_VERSION="22" setup_nodejs
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname"
```
**Functions with built-in messages (NEVER wrap in msg blocks):**
- `fetch_and_deploy_gh_release`
- `check_for_gh_release`
- `setup_nodejs`
- `setup_postgresql` / `setup_postgresql_db`
- `setup_mariadb` / `setup_mariadb_db`
- `setup_mongodb`
- `setup_mysql`
- `setup_ruby`
- `setup_go`
- `setup_java`
- `setup_php`
- `setup_uv`
- `setup_rust`
- `setup_composer`
- `setup_ffmpeg`
- `setup_imagemagick`
- `setup_gs`
- `setup_adminer`
- `setup_hwaccel`
### 9. Creating Unnecessary System Users
```bash
# ❌ WRONG - LXC containers run as root, no separate user needed
useradd -m -s /usr/bin/bash appuser
chown -R appuser:appuser /opt/appname
sudo -u appuser npm install
# ✅ CORRECT - run directly as root
cd /opt/appname
$STD npm install
```
### 10. Using `export` in .env Files
```bash
# ❌ WRONG - export is unnecessary in .env files
cat <<EOF >/opt/appname/.env
export DATABASE_URL=postgres://...
export SECRET_KEY=abc123
export NODE_ENV=production
EOF
# ✅ CORRECT - simple KEY=VALUE format (files are sourced with set -a)
cat <<EOF >/opt/appname/.env
DATABASE_URL=postgres://...
SECRET_KEY=abc123
NODE_ENV=production
EOF
```
### 11. Using External Shell Scripts
```bash
# ❌ WRONG - external script that gets executed
cat <<'EOF' >/opt/appname/install_script.sh
#!/bin/bash
cd /opt/appname
npm install
npm run build
EOF
chmod +x /opt/appname/install_script.sh
$STD bash /opt/appname/install_script.sh
rm -f /opt/appname/install_script.sh
# ✅ CORRECT - run commands directly
cd /opt/appname
$STD npm install
$STD npm run build
```
### 12. Using `sudo` in LXC Containers
```bash
# ❌ WRONG - sudo is unnecessary in LXC (already root)
sudo -u postgres psql -c "CREATE DATABASE mydb;"
sudo -u appuser npm install
# ✅ CORRECT - use functions or run directly as root
PG_DB_NAME="mydb" PG_DB_USER="myuser" setup_postgresql_db
cd /opt/appname
$STD npm install
```
### 13. Unnecessary `systemctl daemon-reload`
```bash
# ❌ WRONG - daemon-reload is only needed when MODIFYING existing services
cat <<EOF >/etc/systemd/system/appname.service
# ... service config ...
EOF
systemctl daemon-reload # Unnecessary for new services!
systemctl enable -q --now appname
# ✅ CORRECT - new services don't need daemon-reload
cat <<EOF >/etc/systemd/system/appname.service
# ... service config ...
EOF
systemctl enable -q --now appname
```
### 14. Creating Custom Credentials Files
```bash
# ❌ WRONG - custom credentials file is not part of the standard template
msg_info "Saving Credentials"
cat <<EOF >~/appname.creds
Database User: ${DB_USER}
Database Pass: ${DB_PASS}
EOF
msg_ok "Saved Credentials"
# ✅ CORRECT - credentials are stored in .env or shown in final message only
# If you use setup_postgresql_db / setup_mariadb_db, a standard ~/[appname].creds is created automatically
```
### 15. Wrong Footer Pattern
```bash
# ❌ WRONG - old cleanup pattern with msg blocks
motd_ssh
customize
msg_info "Cleaning up"
$STD apt-get -y autoremove
$STD apt-get -y autoclean
msg_ok "Cleaned"
# ✅ CORRECT - use cleanup_lxc function
motd_ssh
customize
cleanup_lxc
```
### 16. Manual Database Creation Instead of Functions
```bash
# ❌ WRONG - manual database creation
DB_USER="myuser"
DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)
$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';"
$STD sudo -u postgres psql -c "CREATE DATABASE mydb WITH OWNER $DB_USER;"
$STD sudo -u postgres psql -d mydb -c "CREATE EXTENSION IF NOT EXISTS postgis;"
# ✅ CORRECT - use setup_postgresql_db function
# This sets PG_DB_USER, PG_DB_PASS, PG_DB_NAME automatically
PG_DB_NAME="mydb" PG_DB_USER="myuser" PG_DB_EXTENSIONS="postgis" setup_postgresql_db
```
### 17. Writing Files Without Heredocs
```bash
# ❌ WRONG - echo / printf / tee
echo "# Config" > /opt/app/config.yml
echo "port: 3000" >> /opt/app/config.yml
printf "# Config\nport: 3000\n" > /opt/app/config.yml
cat config.yml | tee /opt/app/config.yml
```
```bash
# ✅ CORRECT - always use a single heredoc
cat <<EOF >/opt/app/config.yml
# Config
port: 3000
EOF
```
---
## 📝 Important Rules
### Variable Declarations (CT Script)
```bash
# Standard declarations (ALWAYS present)
APP="AppName"
var_tags="${var_tags:-tag1;tag2}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
```
### Update-Script Pattern
```bash
function update_script() {
header_info
check_container_storage
check_container_resources
# 1. Check if installation exists
if [[ ! -d /opt/appname ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
# 2. Check for update
if check_for_gh_release "appname" "YourUsername/YourRepo"; then
# 3. Stop service
msg_info "Stopping Service"
systemctl stop appname
msg_ok "Stopped Service"
# 4. Backup data (if present)
msg_info "Backing up Data"
cp -r /opt/appname/data /opt/appname_data_backup
msg_ok "Backed up Data"
# 5. Perform clean install
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname"
# 6. Rebuild (if needed)
cd /opt/appname
$STD npm install
$STD npm run build
# 7. Restore data
msg_info "Restoring Data"
cp -r /opt/appname_data_backup/. /opt/appname/data
rm -rf /opt/appname_data_backup
msg_ok "Restored Data"
# 8. Start service
msg_info "Starting Service"
systemctl start appname
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit # IMPORTANT: Always end with exit!
}
```
### Systemd Service Pattern
```bash
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/appname.service
[Unit]
Description=AppName Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/appname
Environment=NODE_ENV=production
ExecStart=/usr/bin/node /opt/appname/server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now appname
msg_ok "Created Service"
```
### Installation Script Footer
```bash
# ALWAYS at the end of the install script:
motd_ssh
customize
cleanup_lxc
```
---
## 📖 Reference: Good Example Scripts
Look at these recent well-implemented applications as reference:
### Container Scripts (Latest 10)
- [ct/thingsboard.sh](../ct/thingsboard.sh) - IoT platform with proper update_script
- [ct/unifi-os-server.sh](../ct/unifi-os-server.sh) - Complex setup with podman
- [ct/trip.sh](../ct/trip.sh) - Simple Ruby app
- [ct/fladder.sh](../ct/fladder.sh) - Media app with database
- [ct/qui.sh](../ct/qui.sh) - Lightweight utility
- [ct/kutt.sh](../ct/kutt.sh) - Node.js with PostgreSQL
- [ct/flatnotes.sh](../ct/flatnotes.sh) - Python notes app
- [ct/investbrain.sh](../ct/investbrain.sh) - Finance app
- [ct/gwn-manager.sh](../ct/gwn-manager.sh) - Network management
- [ct/sportarr.sh](../ct/sportarr.sh) - Specialized \*Arr variant
### Install Scripts (Latest)
- [install/unifi-os-server-install.sh](../install/unifi-os-server-install.sh) - Complex setup with API integration
- [install/trip-install.sh](../install/trip-install.sh) - Rails application setup
- [install/mail-archiver-install.sh](../install/mail-archiver-install.sh) - Email-related service
**Key things to notice:**
- Proper error handling with `catch_errors`
- Use of `check_for_gh_release` and `fetch_and_deploy_gh_release`
- Correct backup/restore patterns in `update_script`
- Footer always ends with `motd_ssh`, `customize`, `cleanup_lxc`
- JSON metadata files created for each app
---
## <20> JSON Metadata Files
Every application requires a JSON metadata file in `frontend/public/json/<appname>.json`.
### JSON Structure
```json
{
"name": "AppName",
"slug": "appname",
"categories": [1],
"date_created": "2026-01-16",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3000,
"documentation": "https://docs.appname.com/",
"website": "https://appname.com/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/appname.webp",
"config_path": "/opt/appname/.env",
"description": "Short description of the application and its purpose.",
"install_methods": [
{
"type": "default",
"script": "ct/appname.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 8,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}
```
### Required Fields
| Field | Type | Description |
| --------------------- | ------- | -------------------------------------------------- |
| `name` | string | Display name of the application |
| `slug` | string | Lowercase, no spaces, used for filenames |
| `categories` | array | Category ID(s) - see category list below |
| `date_created` | string | Creation date (YYYY-MM-DD) |
| `type` | string | `ct` for container, `vm` for virtual machine |
| `updateable` | boolean | Whether update_script is implemented |
| `privileged` | boolean | Whether container needs privileged mode |
| `interface_port` | number | Primary web interface port (or `null`) |
| `documentation` | string | Link to official docs |
| `website` | string | Link to official website |
| `logo` | string | URL to application logo (preferably selfhst icons) |
| `config_path` | string | Path to main config file (or empty string) |
| `description` | string | Brief description of the application |
| `install_methods` | array | Installation configurations |
| `default_credentials` | object | Default username/password (or null) |
| `notes` | array | Additional notes/warnings |
### Categories
| ID | Category |
| --- | ------------------------- |
| 0 | Miscellaneous |
| 1 | Proxmox & Virtualization |
| 2 | Operating Systems |
| 3 | Containers & Docker |
| 4 | Network & Firewall |
| 5 | Adblock & DNS |
| 6 | Authentication & Security |
| 7 | Backup & Recovery |
| 8 | Databases |
| 9 | Monitoring & Analytics |
| 10 | Dashboards & Frontends |
| 11 | Files & Downloads |
| 12 | Documents & Notes |
| 13 | Media & Streaming |
| 14 | \*Arr Suite |
| 15 | NVR & Cameras |
| 16 | IoT & Smart Home |
| 17 | ZigBee, Z-Wave & Matter |
| 18 | MQTT & Messaging |
| 19 | Automation & Scheduling |
| 20 | AI / Coding & Dev-Tools |
| 21 | Webservers & Proxies |
| 22 | Bots & ChatOps |
| 23 | Finance & Budgeting |
| 24 | Gaming & Leisure |
| 25 | Business & ERP |
### Notes Format
```json
"notes": [
{
"text": "Change the default password after first login!",
"type": "warning"
},
{
"text": "Requires at least 4GB RAM for optimal performance.",
"type": "info"
}
]
```
**Note types:** `info`, `warning`, `error`
### Examples with Credentials
```json
"default_credentials": {
"username": "admin",
"password": "admin"
}
```
Or no credentials:
```json
"default_credentials": {
"username": null,
"password": null
}
```
---
## 🔍 Checklist Before PR Creation
- [ ] No Docker installation used
- [ ] `fetch_and_deploy_gh_release` used for GitHub releases
- [ ] `check_for_gh_release` used for update checks
- [ ] `setup_*` functions used for runtimes (nodejs, postgresql, etc.)
- [ ] **`tools.func` functions NOT wrapped in msg_info/msg_ok blocks**
- [ ] No redundant variables (only when used multiple times)
- [ ] `$STD` before all apt/npm/build commands
- [ ] `msg_info`/`msg_ok`/`msg_error` for logging (only for custom code)
- [ ] Correct script structure followed (see templates)
- [ ] Update function present and functional (CT scripts)
- [ ] Data backup implemented in update function (if applicable)
- [ ] `motd_ssh`, `customize`, `cleanup_lxc` at the end of install scripts
- [ ] No custom download/version-check logic
- [ ] All links point to `community-scripts/ProxmoxVE` (not `ProxmoxVED`!)
- [ ] JSON metadata file created in `frontend/public/json/<appname>.json`
- [ ] Category IDs are valid (0-25)
- [ ] Default OS version is Debian 13 or newer (unless special requirement)
- [ ] Default resources are reasonable for the application
---
## 💡 Tips for AI Assistants
1. **ALWAYS search `tools.func` first** before implementing custom solutions
2. **Use recent scripts as reference** (Thingsboard, UniFi OS, Trip, Flatnotes, etc.)
3. **Ask when uncertain** instead of introducing wrong patterns
4. **Test via GitHub** - push to your fork and test with curl (not local bash)
```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"
# Wait 10-30 seconds after pushing - GitHub takes time to update files
```
5. **Consistency > Creativity** - follow established patterns strictly
6. **Check the templates** - they show the correct structure
7. **Don't wrap tools.func functions** - they handle their own msg_info/msg_ok output
8. **Minimal variables** - only create variables that are truly reused multiple times
9. **Always use $STD** - ensures silent/non-interactive execution
10. **Reference good examples** - look at recent additions in each category
---
## 🍒 Important: Cherry-Picking Your Files for PR Submission
⚠️ **CRITICAL**: When you submit your PR, you must use git cherry-pick to send ONLY your 3-4 files!
Why? Because `setup-fork.sh` modifies 600+ files to update links. If you commit all changes, your PR will be impossible to merge.
**See**: [README.md - Cherry-Pick Section](README.md#-cherry-pick-submitting-only-your-changes) for complete instructions on:
- Creating a clean submission branch
- Cherry-picking only your files (ct/myapp.sh, install/myapp-install.sh, frontend/public/json/myapp.json)
- Verifying your PR has only 3 file changes (not 600+)
**Quick reference**:
```bash
# Create clean branch from upstream
git fetch upstream
git checkout -b submit/myapp upstream/main
# Cherry-pick your commit(s) or manually add your 3-4 files
# Then push to your fork and create PR
```
---
## 📚 Further Documentation
- [CONTRIBUTING.md](CONTRIBUTING.md) - General contribution guidelines
- [GUIDE.md](GUIDE.md) - Detailed developer documentation
- [HELPER_FUNCTIONS.md](HELPER_FUNCTIONS.md) - Complete tools.func reference
- [README.md](README.md) - Cherry-pick guide and workflow instructions
- [../TECHNICAL_REFERENCE.md](../TECHNICAL_REFERENCE.md) - Technical deep dive
- [../EXIT_CODES.md](../EXIT_CODES.md) - Exit code reference
- [templates_ct/](templates_ct/) - CT script templates
- [templates_install/](templates_install/) - Install script templates
- [templates_json/](templates_json/) - JSON metadata templates

View File

@@ -1,41 +1,14 @@
# 🧪 Code Audit: LXC Script Flow <div align="center">
<img src="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo.png" height="100px" />
</div>
<h2><div align="center">Exploring the Scripts and Steps Involved in an Application LXC Installation</div></h2>
This guide explains the current execution flow and what to verify during reviews. 1) [adguard.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/adguard.sh): This script collects system parameters. (Also holds the function to update the application.)
2) [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func): Adds user settings and integrates collected information.
3) [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/create_lxc.sh): Constructs the LXC container.
4) [adguard-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adguard-install.sh): Executes functions from [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), and installs the application.
5) [adguard.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/adguard.sh) (again): To display the completion message.
## Execution Flow (CT + Install) The installation process uses reusable scripts: [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func), [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/create_lxc.sh), and [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), which are not specific to any particular application.
1. `ct/appname.sh` runs on the Proxmox host and sources `misc/build.func`. To gain a better understanding, focus on reviewing [adguard-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adguard-install.sh). This script contains the commands and configurations for installing and configuring AdGuard Home within the LXC container.
2. `build.func` orchestrates prompts, container creation, and invokes the install script.
3. Inside the container, `misc/install.func` exposes helper functions via `$FUNCTIONS_FILE_PATH`.
4. `install/appname-install.sh` performs the application install.
5. The CT script prints the completion message.
## Audit Checklist
### CT Script (ct/)
- Sources `misc/build.func` from `community-scripts/ProxmoxVE/main` (setup-fork.sh updates for forks).
- Uses `check_for_gh_release` + `fetch_and_deploy_gh_release` for updates.
- No Docker-based installs.
### Install Script (install/)
- Sources `$FUNCTIONS_FILE_PATH`.
- Uses `tools.func` helpers (setup\_\*).
- Ends with `motd_ssh`, `customize`, `cleanup_lxc`.
### JSON Metadata
- File in `frontend/public/json/<appname>.json` matches template schema.
### Testing
- Test via curl from your fork (CT script only).
- Wait 10-30 seconds after push.
## References
- `docs/contribution/templates_ct/AppName.sh`
- `docs/contribution/templates_install/AppName-install.sh`
- `docs/contribution/templates_json/AppName.json`
- `docs/contribution/GUIDE.md`

View File

@@ -81,22 +81,11 @@ git clone https://github.com/yourUserName/ForkName
git switch -c your-feature-branch git switch -c your-feature-branch
``` ```
### 4. Run setup-fork.sh to auto-configure your fork ### 4. Change paths in build.func install.func and AppName.sh
```bash To be able to develop from your own branch you need to change `https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main` to `https://raw.githubusercontent.com/[USER]/[REPOSITORY]/refs/heads/[BRANCH]`. You need to make this change atleast in misc/build.func misc/install.func and in your ct/AppName.sh. This change is only for testing. Before opening a Pull Request you should change this line change all this back to point to `https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main`.
bash docs/contribution/setup-fork.sh --full
```
This script automatically: ### 4. Commit changes (without build.func and install.func!)
- Detects your GitHub username
- Updates ALL curl URLs to point to your fork (for testing)
- Creates `.git-setup-info` with your config
- Backs up all modified files (\*.backup)
**IMPORTANT**: This modifies 600+ files! Use cherry-pick when submitting your PR (see below).
### 5. Commit ONLY your new application files
```bash ```bash
git commit -m "Your commit message" git commit -m "Your commit message"
@@ -108,66 +97,9 @@ git commit -m "Your commit message"
git push origin your-feature-branch git push origin your-feature-branch
``` ```
### 6. Cherry-Pick: Submit Only Your Files for PR ### 6. Create a Pull Request
⚠️ **IMPORTANT**: setup-fork.sh modified 600+ files. You MUST only submit your 3 new files! Open a Pull Request from your feature branch to the main repository branch. You must only include your **$AppName.sh**, **$AppName-install.sh** and **$AppName.json** files in the pull request.
See [README.md - Cherry-Pick Guide](README.md#-cherry-pick-submitting-only-your-changes) for step-by-step instructions.
Quick version:
```bash
# Create clean branch from upstream
git fetch upstream
git checkout -b submit/myapp upstream/main
# Copy only your files
cp ../your-work-branch/ct/myapp.sh ct/myapp.sh
cp ../your-work-branch/install/myapp-install.sh install/myapp-install.sh
cp ../your-work-branch/frontend/public/json/myapp.json frontend/public/json/myapp.json
# Commit and verify
git add ct/myapp.sh install/myapp-install.sh frontend/public/json/myapp.json
git commit -m "feat: add MyApp"
git diff upstream/main --name-only # Should show ONLY your 3 files
# Push and create PR
git push origin submit/myapp
```
### 7. Create a Pull Request
Open a Pull Request from `submit/myapp``community-scripts/ProxmoxVE/main`.
Verify the PR shows ONLY these 3 files:
- `ct/myapp.sh`
- `install/myapp-install.sh`
- `frontend/public/json/myapp.json`
---
# 🛠️ Developer Mode & Debugging
When building or testing scripts, you can use the `dev_mode` variable to enable powerful debugging features. These flags can be combined (comma-separated).
**Usage**:
```bash
# Example: Run with trace and keep the container even if it fails
dev_mode="trace,keep" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)"
```
### Available Flags:
| Flag | Description |
| :--- | :--- |
| `trace` | Enables `set -x` for maximum verbosity during execution. |
| `keep` | Prevents the container from being deleted if the build fails. |
| `pause` | Pauses execution at key points (e.g., before customization). |
| `breakpoint` | Allows hardcoded `breakpoint` calls in scripts to drop to a shell. |
| `logs` | Saves detailed build logs to `/var/log/community-scripts/`. |
| `dryrun` | Bypasses actual container creation (limited support). |
| `motd` | Forces an update of the Message of the Day (MOTD). |
--- ---

View File

@@ -10,7 +10,7 @@ git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git
cd ProxmoxVE cd ProxmoxVE
# Run setup script (auto-detects your username from git) # Run setup script (auto-detects your username from git)
bash docs/contribution/setup-fork.sh --full bash setup-fork.sh
``` ```
That's it! ✅ That's it! ✅
@@ -22,101 +22,64 @@ That's it! ✅
The `setup-fork.sh` script automatically: The `setup-fork.sh` script automatically:
1. **Detects** your GitHub username from git config 1. **Detects** your GitHub username from git config
2. **Updates ALL hardcoded links** to point to your fork: 2. **Updates** 22 hardcoded links in documentation to point to your fork
- Documentation links pointing to `community-scripts/ProxmoxVE` 3. **Creates** `.git-setup-info` with recommended git workflows
- **Curl download URLs** in scripts (e.g., `curl ... github.com/community-scripts/ProxmoxVE/main/...`) 4. **Backs up** all modified files (*.backup)
3. **Creates** `.git-setup-info` with your configuration details
4. **Backs up** all modified files (\*.backup for safety)
### Why Updating Curl Links Matters
Your scripts contain `curl` commands that download dependencies from GitHub (build.func, tools.func, etc.):
```bash
# First line of ct/myapp.sh
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
```
**WITHOUT setup-fork.sh:**
- Script URLs still point to `community-scripts/ProxmoxVE/main`
- If you test locally with `bash ct/myapp.sh`, you're testing local files, but the script's curl commands would download from **upstream** repo
- Your modifications aren't actually being tested via the curl commands! ❌
**AFTER setup-fork.sh:**
- Script URLs are updated to `YourUsername/ProxmoxVE/main`
- When you test via curl from GitHub: `bash -c "$(curl ... YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"`, it downloads from **your fork**
- The script's curl commands also point to your fork, so you're actually testing your changes! ✅
- ⏱️ **Important:** GitHub takes 10-30 seconds to recognize pushed files - wait before testing!
```bash
# Example: What setup-fork.sh changes
# BEFORE (points to upstream):
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# AFTER (points to your fork):
source <(curl -fsSL https://raw.githubusercontent.com/john/ProxmoxVE/main/misc/build.func)
```
--- ---
## Usage ## Usage
### Auto-Detect (Recommended) ### Auto-Detect (Recommended)
```bash ```bash
bash docs/contribution/setup-fork.sh --full bash setup-fork.sh
``` ```
Automatically reads your GitHub username from `git remote origin url` Automatically reads your GitHub username from `git remote origin url`
### Specify Username ### Specify Username
```bash ```bash
bash docs/contribution/setup-fork.sh --full john bash setup-fork.sh john
``` ```
Updates links to `github.com/john/ProxmoxVE` Updates links to `github.com/john/ProxmoxVE`
### Custom Repository Name ### Custom Repository Name
```bash ```bash
bash docs/contribution/setup-fork.sh --full john my-fork bash setup-fork.sh john my-fork
``` ```
Updates links to `github.com/john/my-fork` Updates links to `github.com/john/my-fork`
--- ---
## What Gets Updated? ## What Gets Updated?
The script updates hardcoded links in these areas when using `--full`: The script updates these documentation files:
- `docs/CONTRIBUTION_GUIDE.md` (4 links)
- `ct/`, `install/`, `vm/` scripts - `docs/README.md` (1 link)
- `misc/` function libraries - `docs/INDEX.md` (3 links)
- `docs/` (including `docs/contribution/`) - `docs/EXIT_CODES.md` (2 links)
- Code examples in documentation - `docs/DEFAULTS_SYSTEM_GUIDE.md` (2 links)
- `docs/api/README.md` (1 link)
- `docs/APP-ct.md` (1 link)
- `docs/APP-install.md` (1 link)
- `docs/alpine-install.func.md` (2 links)
- `docs/install.func.md` (1 link)
- And code examples in documentation
--- ---
## After Setup ## After Setup
1. **Review changes** 1. **Review changes**
```bash ```bash
git diff docs/ git diff docs/
``` ```
2. **Read git workflow tips** 2. **Read git workflow tips**
```bash ```bash
cat .git-setup-info cat .git-setup-info
``` ```
3. **Start contributing** 3. **Start contributing**
```bash ```bash
git checkout -b feature/my-app git checkout -b feature/my-app
# Make your changes... # Make your changes...
@@ -125,7 +88,7 @@ The script updates hardcoded links in these areas when using `--full`:
4. **Follow the guide** 4. **Follow the guide**
```bash ```bash
cat docs/contribution/GUIDE.md cat docs/CONTRIBUTION_GUIDE.md
``` ```
--- ---
@@ -133,7 +96,6 @@ The script updates hardcoded links in these areas when using `--full`:
## Common Workflows ## Common Workflows
### Keep Your Fork Updated ### Keep Your Fork Updated
```bash ```bash
# Add upstream if you haven't already # Add upstream if you haven't already
git remote add upstream https://github.com/community-scripts/ProxmoxVE.git git remote add upstream https://github.com/community-scripts/ProxmoxVE.git
@@ -145,7 +107,6 @@ git push origin main
``` ```
### Create a Feature Branch ### Create a Feature Branch
```bash ```bash
git checkout -b feature/docker-improvements git checkout -b feature/docker-improvements
# Make changes... # Make changes...
@@ -154,7 +115,6 @@ git push origin feature/docker-improvements
``` ```
### Sync Before Contributing ### Sync Before Contributing
```bash ```bash
git fetch upstream git fetch upstream
git rebase upstream/main git rebase upstream/main
@@ -167,16 +127,14 @@ git checkout -b feature/my-feature
## Troubleshooting ## Troubleshooting
### "Git is not installed" or "not a git repository" ### "Git is not installed" or "not a git repository"
```bash ```bash
# Make sure you cloned the repo first # Make sure you cloned the repo first
git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git
cd ProxmoxVE cd ProxmoxVE
bash docs/contribution/setup-fork.sh --full bash setup-fork.sh
``` ```
### "Could not auto-detect GitHub username" ### "Could not auto-detect GitHub username"
```bash ```bash
# Your git origin URL isn't set up correctly # Your git origin URL isn't set up correctly
git remote -v git remote -v
@@ -184,32 +142,29 @@ git remote -v
# Fix it: # Fix it:
git remote set-url origin https://github.com/YOUR_USERNAME/ProxmoxVE.git git remote set-url origin https://github.com/YOUR_USERNAME/ProxmoxVE.git
bash docs/contribution/setup-fork.sh --full bash setup-fork.sh
``` ```
### "Permission denied" ### "Permission denied"
```bash ```bash
# Make script executable # Make script executable
chmod +x docs/contribution/setup-fork.sh chmod +x setup-fork.sh
bash docs/contribution/setup-fork.sh --full bash setup-fork.sh
``` ```
### Reverted Changes by Accident? ### Reverted Changes by Accident?
```bash ```bash
# Backups are created automatically # Backups are created automatically
git checkout docs/*.backup git checkout docs/*.backup
# Or just re-run setup-fork.sh # Or just re-run setup-fork.sh
bash docs/contribution/setup-fork.sh --full
``` ```
--- ---
## Next Steps ## Next Steps
1. ✅ Run `bash docs/contribution/setup-fork.sh --full` 1. ✅ Run `bash setup-fork.sh`
2. 📖 Read [docs/contribution/GUIDE.md](GUIDE.md) 2. 📖 Read [docs/CONTRIBUTION_GUIDE.md](docs/CONTRIBUTION_GUIDE.md)
3. 🍴 Choose your contribution path: 3. 🍴 Choose your contribution path:
- **Containers** → [docs/ct/README.md](docs/ct/README.md) - **Containers** → [docs/ct/README.md](docs/ct/README.md)
- **Installation** → [docs/install/README.md](docs/install/README.md) - **Installation** → [docs/install/README.md](docs/install/README.md)
@@ -222,10 +177,10 @@ bash docs/contribution/setup-fork.sh --full
## Questions? ## Questions?
- **Fork Setup Issues?** → See [Troubleshooting](#troubleshooting) above - **Fork Setup Issues?** → See [Troubleshooting](#troubleshooting) above
- **How to Contribute?** → [docs/contribution/GUIDE.md](GUIDE.md) - **How to Contribute?** → [docs/CONTRIBUTION_GUIDE.md](docs/CONTRIBUTION_GUIDE.md)
- **Git Workflows?** → `cat .git-setup-info` - **Git Workflows?** → `cat .git-setup-info`
- **Project Structure?** → [docs/README.md](docs/README.md) - **Project Structure?** → [docs/README.md](docs/README.md)
--- ---
## Happy Contributing! 🚀 **Happy Contributing! 🚀**

View File

@@ -38,8 +38,8 @@ git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git
cd ProxmoxVE cd ProxmoxVE
# 3. Run fork setup script (automatically configures everything) # 3. Run fork setup script (automatically configures everything)
bash docs/contribution/setup-fork.sh --full bash setup-fork.sh
# --full updates ct/, install/, vm/, docs/, misc/ links for fork testing # This auto-detects your username and updates all documentation links
# 4. Read the git workflow tips # 4. Read the git workflow tips
cat .git-setup-info cat .git-setup-info
@@ -51,29 +51,28 @@ cat .git-setup-info
# 1. Create feature branch # 1. Create feature branch
git checkout -b add/my-awesome-app git checkout -b add/my-awesome-app
# 2. Create application scripts from templates # 2. Create application scripts
cp docs/contribution/templates_ct/AppName.sh ct/myapp.sh cp ct/example.sh ct/myapp.sh
cp docs/contribution/templates_install/AppName-install.sh install/myapp-install.sh cp install/example-install.sh install/myapp-install.sh
cp docs/contribution/templates_json/AppName.json frontend/public/json/myapp.json
# 3. Edit your scripts # 3. Edit your scripts
nano ct/myapp.sh nano ct/myapp.sh
nano install/myapp-install.sh nano install/myapp-install.sh
nano frontend/public/json/myapp.json
# 4. Commit and push to your fork # 4. Test locally
git add ct/myapp.sh install/myapp-install.sh frontend/public/json/myapp.json bash ct/myapp.sh # Will prompt for container creation
git commit -m "feat: add MyApp container and install scripts"
# 5. Commit and push
git add ct/myapp.sh install/myapp-install.sh
git commit -m "feat: add MyApp container"
git push origin add/my-awesome-app git push origin add/my-awesome-app
# 5. Test via curl from your fork (GitHub may take 10-30 seconds) # 6. Open Pull Request on GitHub
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # Click: New Pull Request (GitHub will show this automatically)
# 6. Use cherry-pick to submit only your files (see Cherry-Pick section) # 7. Keep your fork updated
# DO NOT submit the 600+ files modified by setup-fork.sh! git fetch upstream
git rebase upstream/main
# 7. Open Pull Request on GitHub
# Create PR from: your-fork/add/my-awesome-app → community-scripts/ProxmoxVE/main
``` ```
**💡 Tip**: See `../FORK_SETUP.md` for detailed fork setup and troubleshooting **💡 Tip**: See `../FORK_SETUP.md` for detailed fork setup and troubleshooting
@@ -113,9 +112,9 @@ ProxmoxVE/
│ └── alpine-tools.func # Alpine tools │ └── alpine-tools.func # Alpine tools
├── docs/ # 📚 Documentation ├── docs/ # 📚 Documentation
│ ├── ct/DETAILED_GUIDE.md # Container script guide │ ├── UPDATED_APP-ct.md # Container script guide
│ ├── install/DETAILED_GUIDE.md # Install script guide │ ├── UPDATED_APP-install.md # Install script guide
│ └── contribution/README.md # Contribution overview │ └── CONTRIBUTING.md # (This file!)
├── tools/ # 🔧 Proxmox management tools ├── tools/ # 🔧 Proxmox management tools
│ └── pve/ │ └── pve/
@@ -138,7 +137,6 @@ Examples:
``` ```
**Rules**: **Rules**:
- Container script name: **Title Case** (PiHole, Docker, NextCloud) - Container script name: **Title Case** (PiHole, Docker, NextCloud)
- Install script name: **lowercase** with **hyphens** (pihole-install, docker-install) - Install script name: **lowercase** with **hyphens** (pihole-install, docker-install)
- Must match: `ct/AppName.sh``install/appname-install.sh` - Must match: `ct/AppName.sh``install/appname-install.sh`
@@ -158,7 +156,6 @@ Examples:
- Ubuntu 20.04 / Debian 11+ on host - Ubuntu 20.04 / Debian 11+ on host
2. **Git** installed 2. **Git** installed
```bash ```bash
apt-get install -y git apt-get install -y git
``` ```
@@ -201,33 +198,32 @@ git rebase upstream/main
git push origin feat/add-myapp git push origin feat/add-myapp
``` ```
#### Option B: Testing on a Proxmox Host (still via curl) #### Option B: Local Testing on Proxmox Host
```bash ```bash
# 1. SSH into Proxmox host # 1. SSH into Proxmox host
ssh root@192.168.1.100 ssh root@192.168.1.100
# 2. Test via curl from your fork (CT script only) # 2. Download your script
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" curl -O https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/feat/myapp/ct/myapp.sh
# ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update
# 3. Make it executable
chmod +x myapp.sh
# 4. Update URLs to your fork
# Edit: curl -s https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/feat/myapp/...
# 5. Run and test
bash myapp.sh
# 6. If container created successfully, script is working!
``` ```
> **Note:** Do not edit URLs manually or run install scripts directly. The CT script calls the install script inside the container. #### Option C: Docker Testing (Without Proxmox)
#### Option C: Using Curl (Recommended for Real Testing)
```bash ```bash
# Always test via curl from your fork (GitHub takes 10-30 seconds after push) # You can test script syntax/functionality locally
git push origin feature/myapp # Note: Won't fully test (no Proxmox, no actual container)
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"
# This tests the actual GitHub URLs, not local files
```
#### Option D: Static Checks (Without Proxmox)
```bash
# You can validate syntax and linting locally (limited)
# Note: This does NOT replace real Proxmox testing
# Run ShellCheck # Run ShellCheck
shellcheck ct/myapp.sh shellcheck ct/myapp.sh
@@ -245,18 +241,18 @@ bash -n install/myapp-install.sh
### Step 1: Choose Your Template ### Step 1: Choose Your Template
**For Simple Web Apps** (Node.js, Python, PHP): **For Simple Web Apps** (Node.js, Python, PHP):
```bash ```bash
cp ct/example.sh ct/myapp.sh cp ct/example.sh ct/myapp.sh
cp install/example-install.sh install/myapp-install.sh cp install/example-install.sh install/myapp-install.sh
``` ```
**For Database Apps** (PostgreSQL, MariaDB, MongoDB): **For Database Apps** (PostgreSQL, MongoDB):
```bash
Use the standard templates and the database helpers from `tools.func` (no Docker). cp ct/docker.sh ct/myapp.sh # Use Docker container
# OR manual setup for more control
```
**For Alpine Linux Apps** (lightweight): **For Alpine Linux Apps** (lightweight):
```bash ```bash
# Use ct/alpine.sh as reference # Use ct/alpine.sh as reference
# Edit install script to use Alpine packages (apk not apt) # Edit install script to use Alpine packages (apk not apt)
@@ -268,7 +264,7 @@ Use the standard templates and the database helpers from `tools.func` (no Docker
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/misc/build.func) source <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/feat/myapp/misc/build.func)
# Update these: # Update these:
APP="MyAwesomeApp" # Display name APP="MyAwesomeApp" # Display name
@@ -295,19 +291,17 @@ function update_script() {
exit exit
fi fi
if check_for_gh_release "myapp" "owner/repo"; then # Get latest version
msg_info "Stopping Service" RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \
systemctl stop myapp grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
msg_ok "Stopped Service"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "myapp" "owner/repo" "tarball" "latest" "/opt/myapp" if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
msg_info "Updating ${APP} to v${RELEASE}"
# ... update logic (migrations, rebuilds, etc.) ... # ... update logic ...
echo "${RELEASE}" > /opt/${APP}_version.txt
msg_info "Starting Service" msg_ok "Updated ${APP}"
systemctl start myapp else
msg_ok "Started Service" msg_ok "No update required. ${APP} is already at v${RELEASE}."
msg_ok "Updated successfully!"
fi fi
exit exit
} }
@@ -323,7 +317,6 @@ echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:PORT${CL}"
``` ```
**Checklist**: **Checklist**:
- [ ] APP variable matches filename - [ ] APP variable matches filename
- [ ] var_tags semicolon-separated (no spaces) - [ ] var_tags semicolon-separated (no spaces)
- [ ] Realistic CPU/RAM/disk values - [ ] Realistic CPU/RAM/disk values
@@ -352,12 +345,24 @@ update_os
msg_info "Installing Dependencies" msg_info "Installing Dependencies"
$STD apt-get install -y \ $STD apt-get install -y \
curl \
wget \
git \
build-essential build-essential
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
msg_info "Setting up Node.js"
NODE_VERSION="22" setup_nodejs NODE_VERSION="22" setup_nodejs
msg_ok "Node.js installed"
fetch_and_deploy_gh_release "myapp" "owner/repo" "tarball" "latest" "/opt/myapp" msg_info "Downloading Application"
cd /opt
wget -q "https://github.com/user/repo/releases/download/v1.0.0/myapp.tar.gz"
tar -xzf myapp.tar.gz
rm -f myapp.tar.gz
msg_ok "Application installed"
echo "1.0.0" > /opt/${APP}_version.txt
motd_ssh motd_ssh
customize customize
@@ -365,7 +370,6 @@ cleanup_lxc
``` ```
**Checklist**: **Checklist**:
- [ ] Functions loaded from `$FUNCTIONS_FILE_PATH` - [ ] Functions loaded from `$FUNCTIONS_FILE_PATH`
- [ ] All installation phases present (deps, tools, app, config, cleanup) - [ ] All installation phases present (deps, tools, app, config, cleanup)
- [ ] Using `$STD` for output suppression - [ ] Using `$STD` for output suppression
@@ -433,44 +437,26 @@ $STD apt-get install -y newdependency
# 4. Test thoroughly before committing # 4. Test thoroughly before committing
``` ```
### Step 3: The Standard Update Pattern ### Step 3: Update Update Function (if applicable)
The `update_script()` function in `ct/appname.sh` should follow a robust pattern:
1. **Check for updates**: Use `check_for_gh_release` to skip logic if no new version exists.
2. **Stop services**: Stop all relevant services (`systemctl stop appname`).
3. **Backup existing installation**: Move the old folder (e.g., `mv /opt/app /opt/app_bak`).
4. **Deploy new version**: Use `CLEAN_INSTALL=1 fetch_and_deploy_gh_release`.
5. **Restore configuration**: Copy `.env` or config files back from the backup.
6. **Rebuild/Migrate**: Run `npm install`, `composer install`, or DB migrations.
7. **Start services**: Restart services and cleanup the backup.
**Example from `ct/bookstack.sh`**:
```bash ```bash
function update_script() { # Edit: ct/existingapp.sh → update_script()
if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then
msg_info "Stopping Services"
systemctl stop apache2
msg_info "Backing up data" # 1. Update GitHub API URL if repo changed
mv /opt/bookstack /opt/bookstack-backup RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | ...)
fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack" "tarball" # 2. Update backup/restore logic (if structure changed)
# 3. Update cleanup paths
msg_info "Restoring backup" # 4. Test update on existing installation
cp /opt/bookstack-backup/.env /opt/bookstack/.env ```
# ... restore uploads ...
msg_info "Configuring" ### Step 4: Document Your Changes
cd /opt/bookstack
$STD composer install --no-dev
$STD php artisan migrate --force
systemctl start apache2 ```bash
rm -rf /opt/bookstack-backup # Add comment at top of script
msg_ok "Updated successfully!" # Co-Author: YourUsername
fi # Updated: YYYY-MM-DD - Description of changes
}
``` ```
--- ---
@@ -670,19 +656,27 @@ shellcheck install/myapp-install.sh
# 1. SSH into Proxmox host # 1. SSH into Proxmox host
ssh root@YOUR_PROXMOX_IP ssh root@YOUR_PROXMOX_IP
# 2. Test via curl from your fork (CT script only) # 2. Download your script
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" curl -O https://raw.githubusercontent.com/YOUR_USER/ProxmoxVE/feat/myapp/ct/myapp.sh
# ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update
# 3. Test interaction: # 3. Make executable
chmod +x myapp.sh
# 4. UPDATE URLS IN SCRIPT to point to your fork
sed -i 's|community-scripts|YOUR_USER|g' myapp.sh
# 5. Run script
bash myapp.sh
# 6. Test interaction:
# - Select installation mode # - Select installation mode
# - Confirm settings # - Confirm settings
# - Monitor installation # - Monitor installation
# 4. Verify container created # 7. Verify container created
pct list | grep myapp pct list | grep myapp
# 5. Log into container and verify app # 8. Log into container and verify app
pct exec 100 bash pct exec 100 bash
``` ```
@@ -703,9 +697,9 @@ pct exec 100 bash
# Verify script handles gracefully # Verify script handles gracefully
# Test 4: Update function # Test 4: Update function
# Create initial container (via curl from fork) # Create initial container
# Wait for new release # Wait for new release
# Test update: bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # Run update: bash ct/myapp.sh
# Verify it detects and applies update # Verify it detects and applies update
``` ```
@@ -780,19 +774,16 @@ Use this template:
```markdown ```markdown
## Description ## Description
Brief description of what this PR adds/fixes Brief description of what this PR adds/fixes
## Type of Change ## Type of Change
- [ ] New application (ct/AppName.sh + install/appname-install.sh) - [ ] New application (ct/AppName.sh + install/appname-install.sh)
- [ ] Update existing application - [ ] Update existing application
- [ ] Bug fix - [ ] Bug fix
- [ ] Documentation update - [ ] Documentation update
- [ ] Other: **\_\_\_** - [ ] Other: _______
## Testing ## Testing
- [ ] Tested on Proxmox VE 8.x - [ ] Tested on Proxmox VE 8.x
- [ ] Container creation successful - [ ] Container creation successful
- [ ] Application installation successful - [ ] Application installation successful
@@ -801,7 +792,6 @@ Brief description of what this PR adds/fixes
- [ ] No temporary files left after installation - [ ] No temporary files left after installation
## Application Details (for new apps only) ## Application Details (for new apps only)
- **App Name**: MyApp - **App Name**: MyApp
- **Source**: https://github.com/app/repo - **Source**: https://github.com/app/repo
- **Default OS**: Debian 12 - **Default OS**: Debian 12
@@ -810,11 +800,9 @@ Brief description of what this PR adds/fixes
- **Access URL**: http://IP:PORT/path - **Access URL**: http://IP:PORT/path
## Checklist ## Checklist
- [ ] My code follows the style guidelines - [ ] My code follows the style guidelines
- [ ] I have performed a self-review - [ ] I have performed a self-review
- [ ] I have tested the script via curl from my fork (after git push) - [ ] I have tested the script locally
- [ ] GitHub had time to update (waited 10-30 seconds)
- [ ] ShellCheck shows no critical warnings - [ ] ShellCheck shows no critical warnings
- [ ] Documentation is accurate and complete - [ ] Documentation is accurate and complete
- [ ] I have added/updated relevant documentation - [ ] I have added/updated relevant documentation
@@ -823,7 +811,6 @@ Brief description of what this PR adds/fixes
### Step 5: Respond to Review Comments ### Step 5: Respond to Review Comments
**Maintainers may request changes**: **Maintainers may request changes**:
- Fix syntax/style issues - Fix syntax/style issues
- Add better error handling - Add better error handling
- Optimize resource usage - Optimize resource usage
@@ -935,7 +922,6 @@ pct exec CTID netstat -tlnp | grep LISTEN
### Q: Can I test without a Proxmox system? ### Q: Can I test without a Proxmox system?
**A**: Partially. You can verify syntax and ShellCheck compliance locally, but real container testing requires Proxmox. Consider using: **A**: Partially. You can verify syntax and ShellCheck compliance locally, but real container testing requires Proxmox. Consider using:
- Proxmox in a VM (VirtualBox/KVM) - Proxmox in a VM (VirtualBox/KVM)
- Test instances on Hetzner/DigitalOcean - Test instances on Hetzner/DigitalOcean
- Ask maintainers to test for you - Ask maintainers to test for you
@@ -943,7 +929,6 @@ pct exec CTID netstat -tlnp | grep LISTEN
### Q: My update function is very complex - is that OK? ### Q: My update function is very complex - is that OK?
**A**: Yes! Update functions can be complex if needed. Just ensure: **A**: Yes! Update functions can be complex if needed. Just ensure:
- Backup user data before updating - Backup user data before updating
- Restore user data after update - Restore user data after update
- Test thoroughly before submitting - Test thoroughly before submitting
@@ -952,7 +937,6 @@ pct exec CTID netstat -tlnp | grep LISTEN
### Q: Can I add new dependencies to build.func? ### Q: Can I add new dependencies to build.func?
**A**: Generally no. build.func is the orchestrator and should remain stable. New functions should go in: **A**: Generally no. build.func is the orchestrator and should remain stable. New functions should go in:
- `tools.func` - Tool installation - `tools.func` - Tool installation
- `core.func` - Utility functions - `core.func` - Utility functions
- `install.func` - Container setup - `install.func` - Container setup
@@ -964,13 +948,11 @@ Ask in an issue first if you're unsure.
**A**: You have options: **A**: You have options:
**Option 1**: Use Advanced mode (19-step wizard) **Option 1**: Use Advanced mode (19-step wizard)
```bash ```bash
# Extend advanced_settings() if app needs special vars # Extend advanced_settings() if app needs special vars
``` ```
**Option 2**: Create custom setup menu **Option 2**: Create custom setup menu
```bash ```bash
function custom_config() { function custom_config() {
OPTION=$(whiptail --inputbox "Enter database name:" 8 60) OPTION=$(whiptail --inputbox "Enter database name:" 8 60)
@@ -979,7 +961,6 @@ function custom_config() {
``` ```
**Option 3**: Leave as defaults + documentation **Option 3**: Leave as defaults + documentation
```bash ```bash
# In success message: # In success message:
echo "Edit /opt/myapp/config.json to customize settings" echo "Edit /opt/myapp/config.json to customize settings"
@@ -988,10 +969,9 @@ echo "Edit /opt/myapp/config.json to customize settings"
### Q: Can I contribute Windows/macOS/ARM support? ### Q: Can I contribute Windows/macOS/ARM support?
**A**: **A**:
- **Windows**: Not planned (ProxmoxVE is Linux/Proxmox focused) - **Windows**: Not planned (ProxmoxVE is Linux/Proxmox focused)
- **macOS**: Can contribute Docker-based alternatives - **macOS**: Can contribute Docker-based alternatives
- **ARM**: Yes! Many apps work on ARM. Add to vm/pimox-\*.sh scripts - **ARM**: Yes! Many apps work on ARM. Add to vm/pimox-*.sh scripts
--- ---
@@ -1015,7 +995,6 @@ echo "Edit /opt/myapp/config.json to customize settings"
### Report Bugs ### Report Bugs
When reporting bugs, include: When reporting bugs, include:
- Which application - Which application
- What happened (error message) - What happened (error message)
- What you expected - What you expected
@@ -1023,7 +1002,6 @@ When reporting bugs, include:
- Container OS and version - Container OS and version
Example: Example:
``` ```
Title: pihole-install.sh fails on Alpine 3.20 Title: pihole-install.sh fails on Alpine 3.20
@@ -1047,7 +1025,6 @@ Error Output:
## Contribution Statistics ## Contribution Statistics
**ProxmoxVE by the Numbers**: **ProxmoxVE by the Numbers**:
- 🎯 40+ applications supported - 🎯 40+ applications supported
- 👥 100+ contributors - 👥 100+ contributors
- 📊 10,000+ GitHub stars - 📊 10,000+ GitHub stars
@@ -1061,7 +1038,6 @@ Error Output:
## Code of Conduct ## Code of Conduct
By contributing, you agree to: By contributing, you agree to:
- ✅ Be respectful and inclusive - ✅ Be respectful and inclusive
- ✅ Follow the style guidelines - ✅ Follow the style guidelines
- ✅ Test your changes thoroughly - ✅ Test your changes thoroughly

View File

@@ -30,8 +30,6 @@
> ⚠️ **Both files are ALWAYS required!** The CT script calls the install script automatically during container creation. > ⚠️ **Both files are ALWAYS required!** The CT script calls the install script automatically during container creation.
Install scripts are **not** run directly by users; they are invoked by the CT script inside the container.
### Node.js + PostgreSQL ### Node.js + PostgreSQL
**Koel** - Music streaming with PHP + Node.js + PostgreSQL **Koel** - Music streaming with PHP + Node.js + PostgreSQL
@@ -60,22 +58,6 @@ Install scripts are **not** run directly by users; they are invoked by the CT sc
| CT (update logic) | [ct/endurain.sh](../../ct/endurain.sh) | | CT (update logic) | [ct/endurain.sh](../../ct/endurain.sh) |
| Install | [install/endurain-install.sh](../../install/endurain-install.sh) | | Install | [install/endurain-install.sh](../../install/endurain-install.sh) |
### Java + Gradle
**BookLore** - Book management with Java 21 + Gradle + MariaDB + Nginx
| File | Link |
| ----------------- | -------------------------------------------------------------- |
| CT (update logic) | [ct/booklore.sh](../../ct/booklore.sh) |
| Install | [install/booklore-install.sh](../../install/booklore-install.sh) |
### Pnpm + Meilisearch
**KaraKeep** - Bookmark manager with Pnpm + Meilisearch + Puppeteer
| File | Link |
| ----------------- | -------------------------------------------------------------- |
| CT (update logic) | [ct/karakeep.sh](../../ct/karakeep.sh) |
| Install | [install/karakeep-install.sh](../../install/karakeep-install.sh) |
### PHP + MariaDB/MySQL ### PHP + MariaDB/MySQL
**Wallabag** - Read-it-later with PHP + MariaDB + Redis + Nginx **Wallabag** - Read-it-later with PHP + MariaDB + Redis + Nginx
@@ -113,7 +95,7 @@ Install scripts are **not** run directly by users; they are invoked by the CT sc
Install Node.js from NodeSource repository. Install Node.js from NodeSource repository.
```bash ```bash
# Default (Node.js 24) # Default (Node.js 22)
setup_nodejs setup_nodejs
# Specific version # Specific version
@@ -153,12 +135,8 @@ $STD cargo build --release
Install Python uv package manager (fast pip/venv replacement). Install Python uv package manager (fast pip/venv replacement).
```bash ```bash
# Default
setup_uv setup_uv
# Install a specific Python version
PYTHON_VERSION="3.12" setup_uv
# Use in script # Use in script
setup_uv setup_uv
cd /opt/myapp cd /opt/myapp
@@ -182,7 +160,7 @@ Install PHP with configurable modules and FPM/Apache support.
setup_php setup_php
# Full configuration # Full configuration
PHP_VERSION="8.4" \ PHP_VERSION="8.3" \
PHP_MODULE="mysqli,gd,curl,mbstring,xml,zip,ldap" \ PHP_MODULE="mysqli,gd,curl,mbstring,xml,zip,ldap" \
PHP_FPM="YES" \ PHP_FPM="YES" \
PHP_APACHE="YES" \ PHP_APACHE="YES" \
@@ -192,7 +170,7 @@ setup_php
**Environment Variables:** **Environment Variables:**
| Variable | Default | Description | | Variable | Default | Description |
| ------------- | ------- | ------------------------------- | | ------------- | ------- | ------------------------------- |
| `PHP_VERSION` | `8.4` | PHP version to install | | `PHP_VERSION` | `8.3` | PHP version to install |
| `PHP_MODULE` | `""` | Comma-separated list of modules | | `PHP_MODULE` | `""` | Comma-separated list of modules |
| `PHP_FPM` | `NO` | Install PHP-FPM | | `PHP_FPM` | `NO` | Install PHP-FPM |
| `PHP_APACHE` | `NO` | Install Apache module | | `PHP_APACHE` | `NO` | Install Apache module |
@@ -261,12 +239,12 @@ setup_mysql
Install PostgreSQL server. Install PostgreSQL server.
```bash ```bash
# Default (PostgreSQL 16) # Default (PostgreSQL 17)
setup_postgresql setup_postgresql
# Specific version # Specific version
PG_VERSION="16" setup_postgresql PG_VERSION="16" setup_postgresql
PG_VERSION="16" setup_postgresql PG_VERSION="17" setup_postgresql
``` ```
### `setup_postgresql_db` ### `setup_postgresql_db`
@@ -301,43 +279,6 @@ setup_clickhouse
--- ---
## Advanced Repository Management
### `setup_deb822_repo`
The modern standard (Debian 12+) for adding external repositories. Automatically handles GPG keys and sources.
```bash
setup_deb822_repo \
"nodejs" \
"https://deb.nodesource.com/gpgkey/nodesource.gpg.key" \
"https://deb.nodesource.com/node_22.x" \
"bookworm" \
"main"
```
### `prepare_repository_setup`
A high-level helper that performs three critical tasks before adding a new repo:
1. Cleans up old repo files matching the names provided.
2. Removes old GPG keyrings from all standard locations.
3. Ensures APT is in a working state (fixes locks, runs update).
```bash
# Clean up old mysql/mariadb artifacts before setup
prepare_repository_setup "mariadb" "mysql"
```
### `cleanup_tool_keyrings`
Force-removes GPG keys for specific tools from `/usr/share/keyrings/`, `/etc/apt/keyrings/`, and `/etc/apt/trusted.gpg.d/`.
```bash
cleanup_tool_keyrings "docker" "kubernetes"
```
---
## GitHub Release Helpers ## GitHub Release Helpers
> **Note**: `fetch_and_deploy_gh_release` is the **preferred method** for downloading GitHub releases. It handles version tracking automatically. Only use `get_latest_github_release` if you need the version number separately. > **Note**: `fetch_and_deploy_gh_release` is the **preferred method** for downloading GitHub releases. It handles version tracking automatically. Only use `get_latest_github_release` if you need the version number separately.
@@ -398,21 +339,26 @@ RELEASE=$(get_latest_github_release "owner/repo")
echo "Latest version: $RELEASE" echo "Latest version: $RELEASE"
``` ```
# Examples
fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack"
fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/myapp"
````
**Parameters:**
| Parameter | Default | Description |
| --------- | ------------- | -------------------------------------------- |
| `name` | required | App name (for version tracking) |
| `repo` | required | GitHub repo (`owner/repo`) |
| `type` | `tarball` | Release type: `tarball`, `zipball`, `binary` |
| `version` | `latest` | Version tag or `latest` |
| `dest` | `/opt/[name]` | Destination directory |
--- ---
## Tools & Utilities ## Tools & Utilities
### `setup_meilisearch`
Install Meilisearch, a lightning-fast search engine.
```bash
setup_meilisearch
# Use in script
$STD php artisan scout:sync-index-settings
```
### `setup_yq` ### `setup_yq`
Install yq YAML processor. Install yq YAML processor.
@@ -488,15 +434,6 @@ create_self_signed_cert
## Utility Functions ## Utility Functions
### `verify_tool_version`
Validate that the installed major version matches the expected version. Useful during upgrades or troubleshooting.
```bash
# Verify Node.js is version 22
verify_tool_version "nodejs" "22" "$(node -v | grep -oP '^v\K[0-9]+')"
```
### `get_lxc_ip` ### `get_lxc_ip`
Set the `$LOCAL_IP` variable with the container's IP address. Set the `$LOCAL_IP` variable with the container's IP address.
@@ -589,7 +526,7 @@ msg_ok "Installed Dependencies"
# Setup runtimes and databases FIRST # Setup runtimes and databases FIRST
NODE_VERSION="22" setup_nodejs NODE_VERSION="22" setup_nodejs
PG_VERSION="16" setup_postgresql PG_VERSION="17" setup_postgresql
PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db
get_lxc_ip get_lxc_ip

View File

@@ -17,9 +17,7 @@ Complete guide to contributing to the ProxmoxVE project - from your first fork t
## 🚀 Quick Start ## 🚀 Quick Start
### 60 Seconds to Contributing (Development) ### 60 Seconds to Contributing
When developing and testing **in your fork**:
```bash ```bash
# 1. Fork on GitHub # 1. Fork on GitHub
@@ -29,8 +27,8 @@ When developing and testing **in your fork**:
git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git
cd ProxmoxVE cd ProxmoxVE
# 3. Auto-configure your fork (IMPORTANT - updates all links!) # 3. Auto-configure your fork
bash docs/contribution/setup-fork.sh --full bash docs/contribution/setup-fork.sh
# 4. Create a feature branch # 4. Create a feature branch
git checkout -b feature/my-awesome-app git checkout -b feature/my-awesome-app
@@ -41,73 +39,19 @@ cat docs/ct/DETAILED_GUIDE.md # For container scripts
cat docs/install/DETAILED_GUIDE.md # For install scripts cat docs/install/DETAILED_GUIDE.md # For install scripts
# 6. Create your contribution # 6. Create your contribution
cp docs/contribution/templates_ct/AppName.sh ct/myapp.sh cp ct/example.sh ct/myapp.sh
cp docs/contribution/templates_install/AppName-install.sh install/myapp-install.sh cp install/example-install.sh install/myapp-install.sh
# ... edit files ... # ... edit files ...
# 7. Push to your fork and test via GitHub # 7. Test and commit
git push origin feature/my-awesome-app bash ct/myapp.sh
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" git add ct/myapp.sh install/myapp-install.sh
# ⏱️ GitHub may take 10-30 seconds to update files - be patient! git commit -m "feat: add MyApp"
# 8. Create your JSON metadata file
cp docs/contribution/templates_json/AppName.json frontend/public/json/myapp.json
# Edit metadata: name, slug, categories, description, resources, etc.
# 9. No direct install-script test
# Install scripts are executed by the CT script inside the container
# 10. Commit ONLY your new files (see Cherry-Pick section below!)
git add ct/myapp.sh install/myapp-install.sh frontend/public/json/myapp.json
git commit -m "feat: add MyApp container and install scripts"
git push origin feature/my-awesome-app git push origin feature/my-awesome-app
# 11. Create Pull Request on GitHub # 8. Create Pull Request on GitHub
``` ```
⚠️ **IMPORTANT: After setup-fork.sh, many files are modified!**
See the **Cherry-Pick: Submitting Only Your Changes** section below to learn how to push ONLY your 3-4 files instead of 600+ modified files!
### How Users Run Scripts (After Merged)
Once your script is merged to the main repository, users download and run it from GitHub like this:
```bash
# ✅ Users run from GitHub (normal usage after PR merged)
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)"
# Install scripts are called by the CT script and are not run directly by users
```
### Development vs. Production Execution
**During Development (you, in your fork):**
```bash
# You MUST test via curl from your GitHub fork (not local files!)
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"
# The script's curl commands are updated by setup-fork.sh to point to YOUR fork
# This ensures you're testing your actual changes
# ⏱️ Wait 10-30 seconds after pushing - GitHub updates slowly
```
**After Merge (users, from GitHub):**
```bash
# Users download the script from upstream via curl
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)"
# The script's curl commands now point back to upstream (community-scripts)
# This is the stable, tested version
```
**Summary:**
- **Development**: Push to fork, test via curl → setup-fork.sh changes curl URLs to your fork
- **Production**: curl | bash from upstream → curl URLs point to community-scripts repo
--- ---
## 🍴 Setting Up Your Fork ## 🍴 Setting Up Your Fork
@@ -117,35 +61,14 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/Proxmo
When you clone your fork, run the setup script to automatically configure everything: When you clone your fork, run the setup script to automatically configure everything:
```bash ```bash
bash docs/contribution/setup-fork.sh --full bash docs/contribution/setup-fork.sh
``` ```
**What it does:** This will:
- Auto-detects your GitHub username from git config - Auto-detect your GitHub username
- Auto-detects your fork repository name - Update all documentation links to point to your fork
- Updates **ALL** hardcoded links to point to your fork instead of the main repo (`--full`) - Create `.git-setup-info` with recommended git workflows
- Creates `.git-setup-info` with your configuration
- Allows you to develop and test independently in your fork
**Why this matters:**
Without running this script, all links in your fork will still point to the upstream repository (community-scripts). This is a problem when testing because:
- Installation links will pull from upstream, not your fork
- Updates will target the wrong repository
- Your contributions won't be properly tested
**After running setup-fork.sh:**
Your fork is fully configured and ready to develop. You can:
- Push changes to your fork
- Test via curl: `bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"`
- All links will reference your fork for development
- ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update
- Commit and push with confidence
- Create a PR to merge into upstream
**See**: [FORK_SETUP.md](FORK_SETUP.md) for detailed instructions **See**: [FORK_SETUP.md](FORK_SETUP.md) for detailed instructions
@@ -158,12 +81,11 @@ If the script doesn't work, manually configure:
git config user.name "Your Name" git config user.name "Your Name"
git config user.email "your.email@example.com" git config user.email "your.email@example.com"
# Add upstream remote for syncing with main repo # Add upstream remote for syncing
git remote add upstream https://github.com/community-scripts/ProxmoxVE.git git remote add upstream https://github.com/community-scripts/ProxmoxVE.git
# Verify remotes # Verify remotes
git remote -v git remote -v
# Should show: origin (your fork) and upstream (main repo)
``` ```
--- ---
@@ -180,7 +102,7 @@ All scripts and configurations must follow our coding standards to ensure consis
- **[HELPER_FUNCTIONS.md](HELPER_FUNCTIONS.md)** - Reference for all tools.func helper functions - **[HELPER_FUNCTIONS.md](HELPER_FUNCTIONS.md)** - Reference for all tools.func helper functions
- **Container Scripts** - `/ct/` templates and guidelines - **Container Scripts** - `/ct/` templates and guidelines
- **Install Scripts** - `/install/` templates and guidelines - **Install Scripts** - `/install/` templates and guidelines
- **JSON Configurations** - `frontend/public/json/` structure and format - **JSON Configurations** - `/json/` structure and format
### Quick Checklist ### Quick Checklist
@@ -190,7 +112,7 @@ All scripts and configurations must follow our coding standards to ensure consis
- ✅ Include proper shebang: `#!/usr/bin/env bash` - ✅ Include proper shebang: `#!/usr/bin/env bash`
- ✅ Add copyright header with author - ✅ Add copyright header with author
- ✅ Handle errors properly with `msg_error`, `msg_ok`, etc. - ✅ Handle errors properly with `msg_error`, `msg_ok`, etc.
- ✅ Test before submitting PR (via curl from your fork, not local bash) - ✅ Test before submitting PR
- ✅ Update documentation if needed - ✅ Update documentation if needed
--- ---
@@ -211,172 +133,7 @@ Key points:
--- ---
## 🍒 Cherry-Pick: Submitting Only Your Changes ## 📚 Guides & Resources
**Problem**: `setup-fork.sh` modifies 600+ files to update links. You don't want to submit all of those changes - only your new 3-4 files!
**Solution**: Use git cherry-pick to select only YOUR files.
### Step-by-Step Cherry-Pick Guide
#### 1. Check what changed
```bash
# See all modified files
git status
# Verify your files are there
git status | grep -E "ct/myapp|install/myapp|json/myapp"
```
#### 2. Create a clean feature branch for submission
```bash
# Go back to upstream main (clean slate)
git fetch upstream
git checkout -b submit/myapp upstream/main
# Don't use your modified main branch!
```
#### 3. Cherry-pick ONLY your files
Cherry-picking extracts specific changes from commits:
```bash
# Option A: Cherry-pick commits that added your files
# (if you committed your files separately)
git cherry-pick <commit-hash-of-your-files>
# Option B: Manually copy and commit only your files
# From your work branch, get the file contents
git show feature/my-awesome-app:ct/myapp.sh > /tmp/myapp.sh
git show feature/my-awesome-app:install/myapp-install.sh > /tmp/myapp-install.sh
git show feature/my-awesome-app:frontend/public/json/myapp.json > /tmp/myapp.json
# Add them to the clean branch
cp /tmp/myapp.sh ct/myapp.sh
cp /tmp/myapp-install.sh install/myapp-install.sh
cp /tmp/myapp.json frontend/public/json/myapp.json
# Commit
git add ct/myapp.sh install/myapp-install.sh frontend/public/json/myapp.json
git commit -m "feat: add MyApp"
```
#### 4. Verify only your files are in the PR
```bash
# Check git diff against upstream
git diff upstream/main --name-only
# Should show ONLY:
# ct/myapp.sh
# install/myapp-install.sh
# frontend/public/json/myapp.json
```
#### 5. Push and create PR
```bash
# Push your clean submission branch
git push origin submit/myapp
# Create PR on GitHub from: submit/myapp → main
```
### Why This Matters
- ✅ Clean PR with only your changes
- ✅ Easier for maintainers to review
- ✅ Faster merge without conflicts
- ❌ Without cherry-pick: PR has 600+ file changes (won't merge!)
### If You Made a Mistake
```bash
# Delete the messy branch
git branch -D submit/myapp
# Go back to clean branch
git checkout -b submit/myapp upstream/main
# Try cherry-picking again
```
---
If you're using **Visual Studio Code** with an AI assistant, you can leverage our detailed guidelines to generate high-quality contributions automatically.
### How to Use AI Assistance
1. **Open the AI Guidelines**
```
docs/contribution/AI.md
```
This file contains all requirements, patterns, and examples for writing proper scripts.
2. **Prepare Your Information**
Before asking the AI to generate code, gather:
- **Repository URL**: e.g., `https://github.com/owner/myapp`
- **Dockerfile/Script**: Paste the app's installation instructions (if available)
- **Dependencies**: What packages does it need? (Node, Python, Java, PostgreSQL, etc.)
- **Ports**: What port does it listen on? (e.g., 3000, 8080, 5000)
- **Configuration**: Any environment variables or config files?
3. **Tell the AI Assistant**
Share with the AI:
- The repository URL
- The Dockerfile or install instructions
- Link to [docs/contribution/AI.md](AI.md) with instructions to follow
**Example prompt:**
```
I want to contribute a container script for MyApp to ProxmoxVE.
Repository: https://github.com/owner/myapp
Here's the Dockerfile:
[paste Dockerfile content]
Please follow the guidelines in docs/contribution/AI.md to create:
1. ct/myapp.sh (container script)
2. install/myapp-install.sh (installation script)
3. frontend/public/json/myapp.json (metadata)
```
4. **AI Will Generate**
The AI will produce scripts that:
- Follow all ProxmoxVE patterns and conventions
- Use helper functions from `tools.func` correctly
- Include proper error handling and messages
- Have correct update mechanisms
- Are ready to submit as a PR
### Key Points for AI Assistants
- **Templates Location**: `docs/contribution/templates_ct/AppName.sh`, `templates_install/`, `templates_json/`
- **Guidelines**: Must follow `docs/contribution/AI.md` exactly
- **Helper Functions**: Use only functions from `misc/tools.func` - never write custom ones
- **Testing**: Always test before submission via curl from your fork
```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"
# Wait 10-30 seconds after pushing changes
```
- **No Docker**: Container scripts must be bare-metal, not Docker-based
### Benefits
- **Speed**: AI generates boilerplate in seconds
- **Consistency**: Follows same patterns as 200+ existing scripts
- **Quality**: Less bugs and more maintainable code
- **Learning**: See how your app should be structured
---
### Documentation ### Documentation
@@ -464,15 +221,15 @@ git push origin feature/my-feature
git rebase upstream/main git rebase upstream/main
``` ```
2. **Test your changes** (via curl from your fork) 2. **Test your changes**
```bash ```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)" bash ct/my-app.sh
# Follow prompts and test the container # Follow prompts and test the container
# ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update
``` ```
3. **Check code standards** 3. **Check code standards**
- [ ] Follows template structure - [ ] Follows template structure
- [ ] Proper error handling - [ ] Proper error handling
- [ ] Documentation updated (if needed) - [ ] Documentation updated (if needed)
@@ -503,66 +260,23 @@ Before opening a PR:
## ❓ FAQ ## ❓ FAQ
### ❌ Why can't I test with `bash ct/myapp.sh` locally? ### How do I test my changes?
You might try:
```bash ```bash
# ❌ WRONG - This won't test your actual changes! # For container scripts
bash ct/myapp.sh bash ct/my-app.sh
./ct/myapp.sh
sh ct/myapp.sh # For install scripts (runs inside container)
# The ct script will call it automatically
# For advanced debugging
VERBOSE=yes bash ct/my-app.sh
``` ```
**Why this fails:**
- `bash ct/myapp.sh` uses the LOCAL clone file
- The LOCAL file doesn't execute the curl commands - it's already on disk
- The curl URLs INSIDE the script are modified by setup-fork.sh, but they're not executed
- So you can't verify if your curl URLs actually work
- Users will get the curl URL version (which may be broken)
**Solution:** Always test via curl from GitHub:
```bash
# ✅ CORRECT - Tests the actual GitHub URLs
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"
```
### ❓ How do I test my changes?
You **cannot** test locally with `bash ct/myapp.sh` from your cloned directory!
You **must** push to GitHub and test via curl from your fork:
```bash
# 1. Push your changes to your fork
git push origin feature/my-awesome-app
# 2. Test via curl (this loads the script from GitHub, not local files)
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)"
# 3. For verbose/debug output, pass environment variables
VERBOSE=yes bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)"
DEV_MODE_LOGS=true bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)"
```
**Why?**
- Local `bash ct/myapp.sh` uses local files from your clone
- But the script's INTERNAL curl commands have been modified by setup-fork.sh to point to your fork
- This discrepancy means you're not actually testing the curl URLs
- Testing via curl ensures the script downloads from YOUR fork GitHub URLs
- ⏱️ **Important:** GitHub takes 10-30 seconds to recognize newly pushed files. Wait before testing!
**What if local bash worked?**
You'd be testing local files only, not the actual GitHub URLs that users will download. This means broken curl links wouldn't be caught during testing.
### What if my PR has conflicts? ### What if my PR has conflicts?
```bash ```bash
# Sync with upstream main repository # Sync with upstream
git fetch upstream git fetch upstream
git rebase upstream/main git rebase upstream/main
@@ -574,27 +288,17 @@ git push -f origin your-branch
### How do I keep my fork updated? ### How do I keep my fork updated?
Two ways: See "Keep Your Fork Updated" section above, or run:
**Option 1: Run setup script again**
```bash ```bash
bash docs/contribution/setup-fork.sh --full bash docs/contribution/setup-fork.sh
```
**Option 2: Manual sync**
```bash
git fetch upstream
git rebase upstream/main
git push -f origin main
``` ```
### Where do I ask questions? ### Where do I ask questions?
- **GitHub Issues**: For bugs and feature requests - **GitHub Issues**: For bugs and feature requests
- **GitHub Discussions**: For general questions and ideas - **GitHub Discussions**: For general questions
- **Discord**: Community-scripts server for real-time chat - **Discord**: Community-scripts server
--- ---
@@ -603,7 +307,7 @@ git push -f origin main
### For First-Time Contributors ### For First-Time Contributors
1. Read: [docs/README.md](../README.md) - Documentation overview 1. Read: [docs/README.md](../README.md) - Documentation overview
2. Read: [CONTRIBUTING.md](CONTRIBUTING.md) - Essential coding standards 2. Read: [docs/contribution/FORK_SETUP.md](FORK_SETUP.md) - Fork setup guide
3. Choose your path: 3. Choose your path:
- Containers → [docs/ct/DETAILED_GUIDE.md](../ct/DETAILED_GUIDE.md) - Containers → [docs/ct/DETAILED_GUIDE.md](../ct/DETAILED_GUIDE.md)
- Installation → [docs/install/DETAILED_GUIDE.md](../install/DETAILED_GUIDE.md) - Installation → [docs/install/DETAILED_GUIDE.md](../install/DETAILED_GUIDE.md)
@@ -614,24 +318,21 @@ git push -f origin main
1. Review [CONTRIBUTING.md](CONTRIBUTING.md) - Coding standards 1. Review [CONTRIBUTING.md](CONTRIBUTING.md) - Coding standards
2. Review [CODE_AUDIT.md](CODE_AUDIT.md) - Audit checklist 2. Review [CODE_AUDIT.md](CODE_AUDIT.md) - Audit checklist
3. Check templates in `/docs/contribution/templates_*/` 3. Check templates in `/ct/` and `/install/`
4. Use AI assistants with [AI.md](AI.md) for code generation 4. Submit PR with confidence
5. Submit PR with confidence
### For Using AI Assistants ### For Reviewers/Maintainers
See "Using AI Assistants" section above for: 1. Use [CODE_AUDIT.md](CODE_AUDIT.md) as review guide
2. Reference [docs/TECHNICAL_REFERENCE.md](../TECHNICAL_REFERENCE.md) for architecture
- How to structure prompts 3. Check [docs/EXIT_CODES.md](../EXIT_CODES.md) for error handling
- What information to provide
- How to validate AI output
--- ---
## 🚀 Ready to Contribute? ## 🚀 Ready to Contribute?
1. **Fork** the repository 1. **Fork** the repository
2. **Clone** your fork and **setup** with `bash docs/contribution/setup-fork.sh --full` 2. **Clone** your fork and **setup** with `bash docs/contribution/setup-fork.sh`
3. **Choose** your contribution type (container, installation, tools, etc.) 3. **Choose** your contribution type (container, installation, tools, etc.)
4. **Read** the appropriate detailed guide 4. **Read** the appropriate detailed guide
5. **Create** your feature branch 5. **Create** your feature branch
@@ -644,9 +345,9 @@ See "Using AI Assistants" section above for:
## 📞 Contact & Support ## 📞 Contact & Support
- **GitHub**: [community-scripts/ProxmoxVE](https://github.com/community-scripts/ProxmoxVE) - **GitHub**: https://github.com/community-scripts/ProxmoxVE
- **Issues**: [GitHub Issues](https://github.com/community-scripts/ProxmoxVE/issues) - **Issues**: https://github.com/community-scripts/ProxmoxVE/issues
- **Discussions**: [GitHub Discussions](https://github.com/community-scripts/ProxmoxVE/discussions) - **Discussions**: https://github.com/community-scripts/ProxmoxVE/discussions
- **Discord**: [Join Server](https://discord.gg/UHrpNWGwkH) - **Discord**: [Join Server](https://discord.gg/UHrpNWGwkH)
--- ---

View File

@@ -8,15 +8,13 @@
# Updates all hardcoded links to point to your fork # Updates all hardcoded links to point to your fork
# #
# Usage: # Usage:
# ./setup-fork.sh # Auto-detect from git config (updates misc/ only) # ./setup-fork.sh # Auto-detect from git config
# ./setup-fork.sh YOUR_USERNAME # Specify username (updates misc/ only) # ./setup-fork.sh YOUR_USERNAME # Specify username
# ./setup-fork.sh YOUR_USERNAME REPO_NAME # Specify both (updates misc/ only) # ./setup-fork.sh YOUR_USERNAME REPO_NAME # Specify both
# ./setup-fork.sh --full # Update all files including ct/, install/, vm/, etc.
# #
# Examples: # Examples:
# ./setup-fork.sh john # Uses john/ProxmoxVE, updates misc/ only # ./setup-fork.sh john # Uses john/ProxmoxVE
# ./setup-fork.sh john my-fork # Uses john/my-fork, updates misc/ only # ./setup-fork.sh john my-fork # Uses john/my-fork
# ./setup-fork.sh --full # Auto-detect + update all files
################################################################################ ################################################################################
set -e set -e
@@ -32,7 +30,6 @@ NC='\033[0m' # No Color
REPO_NAME="ProxmoxVE" REPO_NAME="ProxmoxVE"
USERNAME="" USERNAME=""
AUTO_DETECT=true AUTO_DETECT=true
UPDATE_ALL=false
################################################################################ ################################################################################
# FUNCTIONS # FUNCTIONS
@@ -102,8 +99,7 @@ confirm() {
local prompt="$1" local prompt="$1"
local response local response
echo -ne "${YELLOW}${prompt} (y/n)${NC} " read -p "$(echo -e ${YELLOW})$prompt (y/n)${NC} " -r response
read -r response
[[ $response =~ ^[Yy]$ ]] [[ $response =~ ^[Yy]$ ]]
} }
@@ -117,22 +113,24 @@ update_links() {
print_info "Scanning for hardcoded links..." print_info "Scanning for hardcoded links..."
# Change to repo root # Find all markdown and shell files
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null || pwd) local -a files_to_update=(
"docs/DEFAULTS_SYSTEM_GUIDE.md"
# Determine search path "docs/alpine-install.func.md"
local search_path="$repo_root/misc" "docs/install.func.md"
if [[ "$UPDATE_ALL" == "true" ]]; then "docs/APP-install.md"
search_path="$repo_root" "docs/APP-ct.md"
print_info "Searching all files (--full mode)" "docs/CONTRIBUTION_GUIDE.md"
else "docs/INDEX.md"
print_info "Searching misc/ directory only (core functions)" "docs/README.md"
fi "docs/EXIT_CODES.md"
"docs/api/README.md"
)
echo "" echo ""
# Find all files containing the old repo reference for file in "${files_to_update[@]}"; do
while IFS= read -r file; do if [[ -f "$file" ]]; then
# Count occurrences # Count occurrences
local count=$(grep -c "github.com/$old_repo/$old_name" "$file" 2>/dev/null || echo 0) local count=$(grep -c "github.com/$old_repo/$old_name" "$file" 2>/dev/null || echo 0)
@@ -140,19 +138,14 @@ update_links() {
# Backup original # Backup original
cp "$file" "$file.backup" cp "$file" "$file.backup"
# Replace links - use different sed syntax for BSD/macOS vs GNU sed # Replace links
if sed --version &>/dev/null 2>&1; then
# GNU sed
sed -i "s|github.com/$old_repo/$old_name|github.com/$new_owner/$new_repo|g" "$file" sed -i "s|github.com/$old_repo/$old_name|github.com/$new_owner/$new_repo|g" "$file"
else
# BSD sed (macOS)
sed -i '' "s|github.com/$old_repo/$old_name|github.com/$new_owner/$new_repo|g" "$file"
fi
((files_updated++)) ((files_updated++))
print_success "Updated $file ($count links)" print_success "Updated $file ($count links)"
fi fi
done < <(find "$search_path" -type f \( -name "*.md" -o -name "*.sh" -o -name "*.func" -o -name "*.json" \) -not -path "*/.git/*" 2>/dev/null | xargs grep -l "github.com/$old_repo/$old_name" 2>/dev/null) fi
done
return $files_updated return $files_updated
} }
@@ -220,7 +213,7 @@ git merge upstream/main
--- ---
For more help, see: docs/contribution/README.md For more help, see: docs/CONTRIBUTION_GUIDE.md
EOF EOF
print_success "Created .git-setup-info file" print_success "Created .git-setup-info file"
@@ -234,22 +227,12 @@ print_header
# Parse command line arguments # Parse command line arguments
if [[ $# -gt 0 ]]; then if [[ $# -gt 0 ]]; then
# Check for --full flag
if [[ "$1" == "--full" ]]; then
UPDATE_ALL=true
AUTO_DETECT=true
shift # Remove --full from arguments
fi
# Process remaining arguments
if [[ $# -gt 0 ]]; then
USERNAME="$1" USERNAME="$1"
AUTO_DETECT=false AUTO_DETECT=false
if [[ $# -gt 1 ]]; then if [[ $# -gt 1 ]]; then
REPO_NAME="$2" REPO_NAME="$2"
fi fi
fi
else else
# Try auto-detection # Try auto-detection
if username=$(detect_username); then if username=$(detect_username); then
@@ -286,11 +269,7 @@ fi
# Show what we'll do # Show what we'll do
echo -e "${BLUE}Configuration Summary:${NC}" echo -e "${BLUE}Configuration Summary:${NC}"
echo " Repository URL: https://github.com/$USERNAME/$REPO_NAME" echo " Repository URL: https://github.com/$USERNAME/$REPO_NAME"
if [[ "$UPDATE_ALL" == "true" ]]; then echo " Files to update: 10 files with documentation"
echo " Files to update: ALL files (ct/, install/, vm/, misc/, docs/, etc.)"
else
echo " Files to update: misc/ directory only (core functions)"
fi
echo "" echo ""
# Ask for confirmation # Ask for confirmation
@@ -328,7 +307,7 @@ echo -e "${BLUE}Next Steps:${NC}"
echo " 1. Review the changes: git diff" echo " 1. Review the changes: git diff"
echo " 2. Check .git-setup-info for recommended git workflow" echo " 2. Check .git-setup-info for recommended git workflow"
echo " 3. Start developing: git checkout -b feature/my-app" echo " 3. Start developing: git checkout -b feature/my-app"
echo " 4. Read: docs/contribution/README.md" echo " 4. Read: docs/CONTRIBUTION_GUIDE.md"
echo "" echo ""
print_success "Happy contributing! 🚀" print_success "Happy contributing! 🚀"

View File

@@ -1,189 +1,197 @@
# CT Container Scripts - Quick Reference # **AppName<span></span>.sh Scripts**
> [!WARNING] `AppName.sh` scripts found in the `/ct` directory. These scripts are responsible for the installation of the desired application. For this guide we take `/ct/snipeit.sh` as example.
> **This is legacy documentation.** Refer to the **modern template** at [templates_ct/AppName.sh](AppName.sh) for best practices.
>
> Current templates use:
>
> - `tools.func` helpers instead of manual patterns
> - `check_for_gh_release` and `fetch_and_deploy_gh_release` from build.func
> - Automatic setup-fork.sh configuration
--- ## Table of Contents
## Before Creating a Script - [**AppName.sh Scripts**](#appnamesh-scripts)
- [Table of Contents](#table-of-contents)
- [1. **File Header**](#1-file-header)
- [1.1 **Shebang**](#11-shebang)
- [1.2 **Import Functions**](#12-import-functions)
- [1.3 **Metadata**](#13-metadata)
- [2 **Variables and function import**](#2-variables-and-function-import)
- [2.1 **Default Values**](#21-default-values)
- [2.2 **📋 App output \& base settings**](#22--app-output--base-settings)
- [2.3 **🛠 Core functions**](#23--core-functions)
- [3 **Update function**](#3-update-function)
- [3.1 **Function Header**](#31-function-header)
- [3.2 **Check APP**](#32-check-app)
- [3.3 **Check version**](#33-check-version)
- [3.4 **Verbosity**](#34-verbosity)
- [3.5 **Backups**](#35-backups)
- [3.6 **Cleanup**](#36-cleanup)
- [3.7 **No update function**](#37-no-update-function)
- [4 **End of the script**](#4-end-of-the-script)
- [5. **Contribution checklist**](#5-contribution-checklist)
1. **Fork & Clone:** ## 1. **File Header**
```bash ### 1.1 **Shebang**
git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git
cd ProxmoxVE
```
2. **Run setup-fork.sh** (updates all curl URLs to your fork): - Use `#!/usr/bin/env bash` as the shebang.
```bash
bash docs/contribution/setup-fork.sh
```
3. **Copy the Modern Template:**
```bash
cp templates_ct/AppName.sh ct/MyApp.sh
# Edit ct/MyApp.sh with your app details
```
4. **Test Your Script (via GitHub):**
⚠️ **Important:** You must push to GitHub and test via curl, not `bash ct/MyApp.sh`!
```bash
# Push your changes to your fork first
git push origin feature/my-awesome-app
# Then test via curl (this loads from YOUR fork, not local files)
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/MyApp.sh)"
```
> 💡 **Why?** The script's curl commands are modified by setup-fork.sh, but local execution uses local files, not the updated GitHub URLs. Testing via curl ensures your script actually works.
>
> ⏱️ **Note:** GitHub sometimes takes 10-30 seconds to update files. If you don't see your changes, wait and try again.
5. **Cherry-Pick for PR** (submit ONLY your 3-4 files):
- See [Cherry-Pick Guide](../README.md) for step-by-step git commands
---
## Template Structure
The modern template includes:
### Header
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# (Note: setup-fork.sh changes this URL to point to YOUR fork during development)
``` ```
### Metadata ### 1.2 **Import Functions**
- Import the build.func file.
- When developing your own script, change the URL to your own repository.
> [!IMPORTANT]
> You also need to change all apperances of this URL in `misc/build.func` and `misc/install.func`
Example for development:
```bash
source <(curl -fsSL https://raw.githubusercontent.com/[USER]/[REPO]/refs/heads/[BRANCH]/misc/build.func)
```
Final script:
```bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
```
> [!CAUTION]
> Before opening a Pull Request, change the URLs to point to the community-scripts repo.
### 1.3 **Metadata**
- Add clear comments for script metadata, including author, copyright, and license information.
Example:
```bash ```bash
# Copyright (c) 2021-2026 community-scripts ORG # Copyright (c) 2021-2026 community-scripts ORG
# Author: YourUsername # Author: [YourUserName]
# License: MIT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
APP="MyApp" # Source: [SOURCE_URL]
var_tags="app-category;foss" ```
> [!NOTE]:
>
> - Add your username and source URL
> - For existing scripts, add "| Co-Author [YourUserName]" after the current author
---
## 2 **Variables and function import**
>
> [!NOTE]
> You need to have all this set in your script, otherwise it will not work!
### 2.1 **Default Values**
- This section sets the default values for the container.
- `APP` needs to be set to the application name and must be equal to the filenames of your scripts.
- `var_tags`: You can set Tags for the CT wich show up in the Proxmox UI. Don´t overdo it!
>[!NOTE]
>Description for all Default Values
>
>| Variable | Description | Notes |
>|----------|-------------|-------|
>| `APP` | Application name | Must match ct\AppName.sh |
>| `var_tags` | Proxmox display tags without Spaces, only ; | Limit the number |
>| `var_cpu` | CPU cores | Number of cores |
>| `var_ram` | RAM | In MB |
>| `var_disk` | Disk capacity | In GB |
>| `var_os` | Operating system | alpine, debian, ubuntu |
>| `var_version` | OS version | e.g., 3.20, 11, 12, 20.04 |
>| `var_unprivileged` | Container type | 1 = Unprivileged, 0 = Privileged |
Example:
```bash
APP="SnipeIT"
var_tags="asset-management;foss"
var_cpu="2" var_cpu="2"
var_ram="2048" var_ram="2048"
var_disk="4" var_disk="4"
var_os="alpine" var_os="debian"
var_version="3.20" var_version="12"
var_unprivileged="1" var_unprivileged="1"
``` ```
### Core Setup ## 2.2 **📋 App output & base settings**
```bash ```bash
header_info "$APP" header_info "$APP"
```
- `header_info`: Generates ASCII header for APP
## 2.3 **🛠 Core functions**
```bash
variables variables
color color
catch_errors catch_errors
``` ```
### Update Function - `variables`: Processes input and prepares variables
- `color`: Sets icons, colors, and formatting
- `catch_errors`: Enables error handling
The modern template provides a standard update pattern: ---
## 3 **Update function**
### 3.1 **Function Header**
- If applicable write a function that updates the application and the OS in the container.
- Each update function starts with the same code:
```bash ```bash
function update_script() { function update_script() {
header_info header_info
check_container_storage check_container_storage
check_container_resources check_container_resources
```
# Use tools.func helpers: ### 3.2 **Check APP**
check_for_gh_release "myapp" "owner/repo"
fetch_and_deploy_gh_release "myapp" "owner/repo" "tarball" "latest" "/opt/myapp" - Before doing anything update-wise, check if the app is installed in the container.
Example:
```bash
if [[ ! -d /opt/snipe-it ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
```
### 3.3 **Check version**
- Before updating, check if a new version exists.
- We use the `${APPLICATION}_version.txt` file created in `/opt` during the install to compare new versions against the currently installed version.
Example with a Github Release:
```bash
RELEASE=$(curl -fsSL https://api.github.com/repos/snipe/snipe-it/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
msg_info "Updating ${APP} to v${RELEASE}"
#DO UPDATE
else
msg_ok "No update required. ${APP} is already at v${RELEASE}."
fi
exit
} }
``` ```
---
## Key Patterns
### Check for Updates (App Repository)
Use `check_for_gh_release` with the **app repo**:
```bash
check_for_gh_release "myapp" "owner/repo"
```
### Deploy External App
Use `fetch_and_deploy_gh_release` with the **app repo**:
```bash
fetch_and_deploy_gh_release "myapp" "owner/repo"
```
### Avoid Manual Version Checking
❌ OLD (manual):
```bash
RELEASE=$(curl -fsSL https://api.github.com/repos/myapp/myapp/releases/latest | grep tag_name)
```
✅ NEW (use tools.func):
```bash
fetch_and_deploy_gh_release "myapp" "owner/repo"
```
---
## Best Practices
1. **Use tools.func helpers** - Don't manually curl for versions
2. **Only add app-specific dependencies** - Don't add ca-certificates, curl, gnupg (handled by build.func)
3. **Test via curl from your fork** - Push first, then: `bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/MyApp.sh)"`
4. **Wait for GitHub to update** - Takes 10-30 seconds after git push
5. **Cherry-pick only YOUR files** - Submit only ct/MyApp.sh, install/MyApp-install.sh, frontend/public/json/myapp.json (3 files)
6. **Verify before PR** - Run `git diff upstream/main --name-only` to confirm only your files changed
---
## Common Update Patterns
See the [modern template](AppName.sh) and [AI.md](../AI.md) for complete working examples.
Recent reference scripts with good update functions:
- [Trip](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/trip.sh)
- [Thingsboard](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/thingsboard.sh)
- [UniFi](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/unifi.sh)
---
## Need Help?
- **[README.md](../README.md)** - Full contribution workflow
- **[AI.md](../AI.md)** - AI-generated script guidelines
- **[FORK_SETUP.md](../FORK_SETUP.md)** - Why setup-fork.sh is important
- **[Slack Community](https://discord.gg/your-link)** - Ask questions
````
### 3.4 **Verbosity** ### 3.4 **Verbosity**
- Use the appropriate flag (**-q** in the examples) for a command to suppress its output. - Use the appropriate flag (**-q** in the examples) for a command to suppress its output.
Example: Example:
```bash ```bash
curl -fsSL curl -fsSL
unzip -q unzip -q
```` ```
- If a command does not come with this functionality use `$STD` to suppress it's output. - If a command does not come with this functionality use `$STD` to suppress it's output.
@@ -199,8 +207,8 @@ $STD php artisan config:clear
- Backup user data if necessary. - Backup user data if necessary.
- Move all user data back in the directory when the update is finished. - Move all user data back in the directory when the update is finished.
> [!NOTE] >[!NOTE]
> This is not meant to be a permanent backup >This is not meant to be a permanent backup
Example backup: Example backup:
@@ -219,7 +227,7 @@ Example config restore:
### 3.6 **Cleanup** ### 3.6 **Cleanup**
- Do not forget to remove any temporary files/folders such as zip-files or temporary backups. - Do not forget to remove any temporary files/folders such as zip-files or temporary backups.
Example: Example:
```bash ```bash
rm -rf /opt/v${RELEASE}.zip rm -rf /opt/v${RELEASE}.zip
@@ -269,7 +277,7 @@ echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"
## 5. **Contribution checklist** ## 5. **Contribution checklist**
- [ ] Shebang is correctly set (`#!/usr/bin/env bash`). - [ ] Shebang is correctly set (`#!/usr/bin/env bash`).
- [ ] Correct link to _build.func_ - [ ] Correct link to *build.func*
- [ ] Metadata (author, license) is included at the top. - [ ] Metadata (author, license) is included at the top.
- [ ] Variables follow naming conventions. - [ ] Variables follow naming conventions.
- [ ] Update function exists. - [ ] Update function exists.

View File

@@ -5,133 +5,89 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: [SOURCE_URL e.g. https://github.com/example/app] # Source: [SOURCE_URL e.g. https://github.com/example/app]
# ============================================================================ # App Default Values
# APP CONFIGURATION
# ============================================================================
# These values are sent to build.func and define default container resources.
# Users can customize these during installation via the interactive prompts.
# ============================================================================
APP="[AppName]" APP="[AppName]"
var_tags="${var_tags:-[category1];[category2]}" # Max 2 tags, semicolon-separated var_tags="${var_tags:-[category]}"
var_cpu="${var_cpu:-2}" # CPU cores: 1-4 typical var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}" # RAM in MB: 512, 1024, 2048, etc. var_ram="${var_ram:-2048}"
var_disk="${var_disk:-8}" # Disk in GB: 6, 8, 10, 20 typical var_disk="${var_disk:-4}"
var_os="${var_os:-debian}" # OS: debian, ubuntu, alpine var_os="${var_os:-debian}"
var_version="${var_version:-13}" # OS Version: 13 (Debian), 24.04 (Ubuntu), 3.21 (Alpine) var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-1}" # 1=unprivileged (secure), 0=privileged (for Docker/Podman) var_unprivileged="${var_unprivileged:-1}"
# ============================================================================ # =============================================================================
# INITIALIZATION - These are required in all CT scripts # CONFIGURATION GUIDE
# ============================================================================ # =============================================================================
header_info "$APP" # Display app name and setup header # APP - Display name, title case (e.g. "Koel", "Wallabag", "Actual Budget")
variables # Initialize build.func variables # var_tags - Max 2 tags, semicolon separated (e.g. "music;streaming", "finance")
color # Load color variables for output # var_cpu - CPU cores: 1-4 typical
catch_errors # Enable error handling with automatic exit on failure # var_ram - RAM in MB: 512, 1024, 2048, 4096 typical
# var_disk - Disk in GB: 4, 6, 8, 10, 20 typical
# var_os - OS: debian, ubuntu, alpine
# var_version - OS version: 12/13 (debian), 22.04/24.04 (ubuntu), 3.20/3.21 (alpine)
# var_unprivileged - 1 = unprivileged (secure, default), 0 = privileged (for docker etc.)
# ============================================================================ header_info "$APP"
# UPDATE SCRIPT - Called when user selects "Update" from web interface variables
# ============================================================================ color
# This function is triggered by the web interface to update the application. catch_errors
# It should:
# 1. Check if installation exists
# 2. Check for new GitHub releases
# 3. Stop running services
# 4. Backup critical data
# 5. Deploy new version
# 6. Run post-update commands (migrations, config updates, etc.)
# 7. Restore data if needed
# 8. Start services
#
# Exit with `exit` at the end to prevent container restart.
# ============================================================================
function update_script() { function update_script() {
header_info header_info
check_container_storage check_container_storage
check_container_resources check_container_resources
# Step 1: Verify installation exists # Check if installation exists
if [[ ! -d /opt/[appname] ]]; then if [[ ! -d /opt/[appname] ]]; then
msg_error "No ${APP} Installation Found!" msg_error "No ${APP} Installation Found!"
exit exit
fi fi
# Step 2: Check if update is available # check_for_gh_release returns 0 (true) if update available, 1 (false) if not
if check_for_gh_release "[appname]" "YourUsername/YourRepo"; then if check_for_gh_release "[appname]" "[owner/repo]"; then
msg_info "Stopping Services"
# Step 3: Stop services before update
msg_info "Stopping Service"
systemctl stop [appname] systemctl stop [appname]
msg_ok "Stopped Service" msg_ok "Stopped Services"
# Step 4: Backup critical data before overwriting # Optional: Backup important data before update
msg_info "Backing up Data" msg_info "Creating Backup"
cp -r /opt/[appname]/data /opt/[appname]_data_backup 2>/dev/null || true mkdir -p /tmp/[appname]_backup
msg_ok "Backed up Data" cp /opt/[appname]/.env /tmp/[appname]_backup/ 2>/dev/null || true
cp -r /opt/[appname]/data /tmp/[appname]_backup/ 2>/dev/null || true
msg_ok "Created Backup"
# Step 5: Download and deploy new version # CLEAN_INSTALL=1 removes old directory before extracting new version
# CLEAN_INSTALL=1 removes old directory before extracting CLEAN_INSTALL=1 fetch_and_deploy_gh_release "[appname]" "[owner/repo]" "tarball" "latest" "/opt/[appname]"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "[appname]" "owner/repo" "tarball" "latest" "/opt/[appname]"
# Step 6: Run post-update commands (uncomment as needed) # Restore configuration and data
# These examples show common patterns - use what applies to your app:
#
# For Node.js apps:
# msg_info "Installing Dependencies"
# cd /opt/[appname]
# $STD npm ci --production
# msg_ok "Installed Dependencies"
#
# For Python apps:
# msg_info "Installing Dependencies"
# cd /opt/[appname]
# $STD uv sync --frozen
# msg_ok "Installed Dependencies"
#
# For database migrations:
# msg_info "Running Database Migrations"
# cd /opt/[appname]
# $STD npm run migrate
# msg_ok "Ran Database Migrations"
#
# For PHP apps:
# msg_info "Installing Dependencies"
# cd /opt/[appname]
# $STD composer install --no-dev
# msg_ok "Installed Dependencies"
# Step 7: Restore data from backup
msg_info "Restoring Data" msg_info "Restoring Data"
cp -r /opt/[appname]_data_backup/. /opt/[appname]/data/ 2>/dev/null || true cp /tmp/[appname]_backup/.env /opt/[appname]/ 2>/dev/null || true
rm -rf /opt/[appname]_data_backup cp -r /tmp/[appname]_backup/data/* /opt/[appname]/data/ 2>/dev/null || true
rm -rf /tmp/[appname]_backup
msg_ok "Restored Data" msg_ok "Restored Data"
# Step 8: Restart service with new version # Optional: Run any post-update commands
msg_info "Starting Service" msg_info "Running Post-Update Tasks"
cd /opt/[appname]
# Examples:
# $STD npm ci --production
# $STD php artisan migrate --force
# $STD composer install --no-dev
msg_ok "Ran Post-Update Tasks"
msg_info "Starting Services"
systemctl start [appname] systemctl start [appname]
msg_ok "Started Service" msg_ok "Started Services"
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
fi fi
exit exit
} }
# ============================================================================
# MAIN EXECUTION - Container creation flow
# ============================================================================
# These are called by build.func and handle the full installation process:
# 1. start - Initialize container creation
# 2. build_container - Execute the install script inside container
# 3. description - Display completion info and access details
# ============================================================================
start start
build_container build_container
description description
# ============================================================================
# COMPLETION MESSAGE
# ============================================================================
msg_ok "Completed successfully!\n" msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}"

View File

@@ -1,195 +1,54 @@
# Install Scripts - Quick Reference
> [!WARNING] # **AppName<span></span>-install.sh Scripts**
> **This is legacy documentation.** Refer to the **modern template** at [templates_install/AppName-install.sh](AppName-install.sh) for best practices.
>
> Current templates use:
>
> - `tools.func` helpers (setup_nodejs, setup_uv, setup_postgresql_db, etc.)
> - Automatic dependency installation via build.func
> - Standardized environment variable patterns
--- `AppName-install.sh` scripts found in the `/install` directory. These scripts are responsible for the installation of the application. For this guide we take `/install/snipeit-install.sh` as example.
## Before Creating a Script ## Table of Contents
1. **Copy the Modern Template:** - [**AppName-install.sh Scripts**](#appname-installsh-scripts)
- [Table of Contents](#table-of-contents)
- [1. **File header**](#1-file-header)
- [1.1 **Shebang**](#11-shebang)
- [1.2 **Comments**](#12-comments)
- [1.3 **Variables and function import**](#13-variables-and-function-import)
- [2. **Variable naming and management**](#2-variable-naming-and-management)
- [2.1 **Naming conventions**](#21-naming-conventions)
- [3. **Dependencies**](#3-dependencies)
- [3.1 **Install all at once**](#31-install-all-at-once)
- [3.2 **Collapse dependencies**](#32-collapse-dependencies)
- [4. **Paths to application files**](#4-paths-to-application-files)
- [5. **Version management**](#5-version-management)
- [5.1 **Install the latest release**](#51-install-the-latest-release)
- [5.2 **Save the version for update checks**](#52-save-the-version-for-update-checks)
- [6. **Input and output management**](#6-input-and-output-management)
- [6.1 **User feedback**](#61-user-feedback)
- [6.2 **Verbosity**](#62-verbosity)
- [7. **String/File Manipulation**](#7-stringfile-manipulation)
- [7.1 **File Manipulation**](#71-file-manipulation)
- [8. **Security practices**](#8-security-practices)
- [8.1 **Password generation**](#81-password-generation)
- [8.2 **File permissions**](#82-file-permissions)
- [9. **Service Configuration**](#9-service-configuration)
- [9.1 **Configuration files**](#91-configuration-files)
- [9.2 **Credential management**](#92-credential-management)
- [9.3 **Enviroment files**](#93-enviroment-files)
- [9.4 **Services**](#94-services)
- [10. **Cleanup**](#10-cleanup)
- [10.1 **Remove temporary files**](#101-remove-temporary-files)
- [10.2 **Autoremove and autoclean**](#102-autoremove-and-autoclean)
- [11. **Best Practices Checklist**](#11-best-practices-checklist)
- [Example: High-Level Script Flow](#example-high-level-script-flow)
```bash ## 1. **File header**
cp templates_install/AppName-install.sh install/MyApp-install.sh
# Edit install/MyApp-install.sh
```
2. **Key Pattern:** ### 1.1 **Shebang**
- CT scripts source build.func and call the install script
- Install scripts use sourced FUNCTIONS_FILE_PATH (via build.func)
- Both scripts work together in the container
3. **Test via GitHub:** - Use `#!/usr/bin/env bash` as the shebang.
```bash
# Push your changes to your fork first
git push origin feature/my-awesome-app
# Test the CT script via curl (it will call the install script)
bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/MyApp.sh)"
# ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update
```
---
## Template Structure
### Header
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func)
# (setup-fork.sh modifies this URL to point to YOUR fork during development)
``` ```
### Dependencies (App-Specific Only)
```bash
# Don't add: ca-certificates, curl, gnupg, wget, git, jq
# These are handled by build.func
msg_info "Installing dependencies"
$STD apt-get install -y app-specific-deps
msg_ok "Installed dependencies"
```
### Runtime Setup
Use tools.func helpers instead of manual installation:
```bash
# ✅ NEW (use tools.func):
NODE_VERSION="20"
setup_nodejs
# OR
PYTHON_VERSION="3.12"
setup_uv
# OR
PG_DB_NAME="myapp_db"
PG_DB_USER="myapp"
setup_postgresql_db
```
### Service Configuration
```bash
# Create .env file
msg_info "Configuring MyApp"
cat << EOF > /opt/myapp/.env
DEBUG=false
PORT=8080
DATABASE_URL=postgresql://...
EOF
msg_ok "Configuration complete"
# Create systemd service
msg_info "Creating systemd service"
cat << EOF > /etc/systemd/system/myapp.service
[Unit]
Description=MyApp
[Service]
ExecStart=/usr/bin/node /opt/myapp/app.js
[Install]
WantedBy=multi-user.target
EOF
msg_ok "Service created"
```
### Finalization
```bash
msg_info "Finalizing MyApp installation"
systemctl enable --now myapp
motd_ssh
customize
msg_ok "MyApp installation complete"
cleanup_lxc
```
---
## Key Patterns
### Avoid Manual Version Checking
❌ OLD (manual):
```bash
RELEASE=$(curl -fsSL https://api.github.com/repos/app/repo/releases/latest | grep tag_name)
wget https://github.com/app/repo/releases/download/$RELEASE/app.tar.gz
```
✅ NEW (use tools.func via CT script's fetch_and_deploy_gh_release):
```bash
# In CT script, not install script:
fetch_and_deploy_gh_release "myapp" "app/repo" "app.tar.gz" "latest" "/opt/myapp"
```
### Database Setup
```bash
# Use setup_postgresql_db, setup_mysql_db, etc.
PG_DB_NAME="myapp"
PG_DB_USER="myapp"
setup_postgresql_db
```
### Node.js Setup
```bash
NODE_VERSION="20"
setup_nodejs
npm install --no-save
```
---
## Best Practices
1. **Only add app-specific dependencies**
- Don't add: ca-certificates, curl, gnupg, wget, git, jq
- These are handled by build.func
2. **Use tools.func helpers**
- setup_nodejs, setup_python, setup_uv, setup_postgresql_db, setup_mysql_db, etc.
3. **Don't do version checks in install script**
- Version checking happens in CT script's update_script()
- Install script just installs the latest
4. **Structure:**
- Dependencies
- Runtime setup (tools.func)
- Deployment (fetch from CT script)
- Configuration files
- Systemd service
- Finalization
---
## Reference Scripts
See working examples:
- [Trip](https://github.com/community-scripts/ProxmoxVE/blob/main/install/trip-install.sh)
- [Thingsboard](https://github.com/community-scripts/ProxmoxVE/blob/main/install/thingsboard-install.sh)
- [UniFi](https://github.com/community-scripts/ProxmoxVE/blob/main/install/unifi-install.sh)
---
## Need Help?
- **[Modern Template](AppName-install.sh)** - Start here
- **[CT Template](../templates_ct/AppName.sh)** - How CT scripts work
- **[README.md](../README.md)** - Full contribution workflow
- **[AI.md](../AI.md)** - AI-generated script guidelines
### 1.2 **Comments** ### 1.2 **Comments**
- Add clear comments for script metadata, including author, copyright, and license information. - Add clear comments for script metadata, including author, copyright, and license information.
@@ -330,7 +189,7 @@ msg_ok "Installed Dependencies"
### 6.2 **Verbosity** ### 6.2 **Verbosity**
- Use the appropiate flag (**-q** in the examples) for a command to suppres its output - Use the appropiate flag (**-q** in the examples) for a command to suppres its output
Example: Example:
```bash ```bash
curl -fsSL curl -fsSL

View File

@@ -5,6 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: [SOURCE_URL e.g. https://github.com/example/app] # Source: [SOURCE_URL e.g. https://github.com/example/app]
# Import Functions and Setup
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color color
verb_ip6 verb_ip6
@@ -14,51 +15,48 @@ network_check
update_os update_os
# ============================================================================= # =============================================================================
# DEPENDENCIES - Only add app-specific dependencies here! # DEPENDENCIES
# Don't add: ca-certificates, curl, gnupg, git, build-essential (handled by build.func)
# ============================================================================= # =============================================================================
# Only install what's actually needed - curl/sudo/mc are already in the base image
msg_info "Installing Dependencies" msg_info "Installing Dependencies"
$STD apt install -y \ $STD apt install -y \
libharfbuzz0b \ nginx \
fontconfig build-essential
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
# ============================================================================= # =============================================================================
# SETUP RUNTIMES & DATABASES (if needed) # HELPER FUNCTIONS FROM tools.func
# ============================================================================= # =============================================================================
# Examples (uncomment as needed): # These functions are available via $FUNCTIONS_FILE_PATH (tools.func)
# Call them with optional environment variables for configuration
# #
# NODE_VERSION="22" setup_nodejs # --- Runtime & Language Setup ---
# NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs # Installs pnpm # NODE_VERSION="22" setup_nodejs # Install Node.js (22, 24)
# PYTHON_VERSION="3.13" setup_uv # NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs # With pnpm
# JAVA_VERSION="21" setup_java # PYTHON_VERSION="3.13" setup_uv # Python with uv package manager
# GO_VERSION="1.22" setup_go # setup_go # Install Go (latest)
# PHP_VERSION="8.4" PHP_FPM="YES" setup_php # setup_rust # Install Rust via rustup
# setup_postgresql # Server only # setup_ruby # Install Ruby
# setup_mariadb # Server only # PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="mysqli,gd" setup_php
# setup_meilisearch # Search engine # PHP_VERSION="8.3" PHP_FPM="YES" PHP_APACHE="YES" PHP_MODULE="bcmath,curl,gd,intl,mbstring,mysql,xml,zip" setup_php
# setup_composer # Install PHP Composer
# JAVA_VERSION="21" setup_java # Install Java (17, 21)
# #
# Then set up DB and user (sets $[DB]_DB_PASS): # --- Database Setup ---
# PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db # setup_mariadb # Install MariaDB server
# MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp" setup_mariadb_db # MARIADB_DB_NAME="mydb" MARIADB_DB_USER="myuser" setup_mariadb_db
# setup_mysql # Install MySQL server
# ============================================================================= # PG_VERSION="17" setup_postgresql # Install PostgreSQL (16, 17)
# DOWNLOAD & DEPLOY APPLICATION # PG_VERSION="17" PG_MODULES="postgis" setup_postgresql # With extensions
# ============================================================================= # PG_DB_NAME="mydb" PG_DB_USER="myuser" setup_postgresql_db
# fetch_and_deploy_gh_release modes: # setup_mongodb # Install MongoDB
# "tarball" - Source tarball (default if omitted) #
# "binary" - .deb package (auto-detects amd64/arm64) # --- GitHub Release (PREFERRED METHOD) ---
# "prebuild" - Pre-built archive (.tar.gz) # fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" # Downloads, extracts, tracks version
# "singlefile" - Single binary file # fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname"
# fetch_and_deploy_gh_release "appname" "owner/repo" "prebuild" "latest" "/opt/appname" "app-*.tar.gz"
# #
# Examples:
# fetch_and_deploy_gh_release "myapp" "YourUsername/myapp" "tarball" "latest" "/opt/myapp"
# fetch_and_deploy_gh_release "myapp" "YourUsername/myapp" "binary" "latest" "/tmp"
# fetch_and_deploy_gh_release "myapp" "YourUsername/myapp" "prebuild" "latest" "/opt/myapp" "myapp-*.tar.gz"
fetch_and_deploy_gh_release "[appname]" "owner/repo" "tarball" "latest" "/opt/[appname]"
# --- Tools & Utilities --- # --- Tools & Utilities ---
# get_lxc_ip # Sets $LOCAL_IP variable (call early!) # get_lxc_ip # Sets $LOCAL_IP variable (call early!)
# setup_ffmpeg # Install FFmpeg with codecs # setup_ffmpeg # Install FFmpeg with codecs
@@ -69,11 +67,8 @@ fetch_and_deploy_gh_release "[appname]" "owner/repo" "tarball" "latest" "/opt/[a
# create_self_signed_cert # Creates cert in /etc/ssl/[appname]/ # create_self_signed_cert # Creates cert in /etc/ssl/[appname]/
# ============================================================================= # =============================================================================
# EXAMPLES
# =============================================================================
#
# EXAMPLE 1: Node.js Application with PostgreSQL # EXAMPLE 1: Node.js Application with PostgreSQL
# --------------------------------------------- # =============================================================================
# NODE_VERSION="22" setup_nodejs # NODE_VERSION="22" setup_nodejs
# PG_VERSION="17" setup_postgresql # PG_VERSION="17" setup_postgresql
# PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db # PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db
@@ -89,9 +84,10 @@ fetch_and_deploy_gh_release "[appname]" "owner/repo" "tarball" "latest" "/opt/[a
# PORT=3000 # PORT=3000
# EOF # EOF
# msg_ok "Configured MyApp" # msg_ok "Configured MyApp"
#
# =============================================================================
# EXAMPLE 2: Python Application with uv # EXAMPLE 2: Python Application with uv
# ------------------------------------ # =============================================================================
# PYTHON_VERSION="3.13" setup_uv # PYTHON_VERSION="3.13" setup_uv
# get_lxc_ip # get_lxc_ip
# fetch_and_deploy_gh_release "myapp" "owner/myapp" "tarball" "latest" "/opt/myapp" # fetch_and_deploy_gh_release "myapp" "owner/myapp" "tarball" "latest" "/opt/myapp"
@@ -145,7 +141,7 @@ fetch_and_deploy_gh_release "[appname]" "[owner/repo]" "tarball" "latest" "/opt/
msg_info "Setting up [AppName]" msg_info "Setting up [AppName]"
cd /opt/[appname] cd /opt/[appname]
# $STD npm ci $STD npm ci
msg_ok "Setup [AppName]" msg_ok "Setup [AppName]"
# ============================================================================= # =============================================================================
@@ -153,25 +149,14 @@ msg_ok "Setup [AppName]"
# ============================================================================= # =============================================================================
msg_info "Configuring [AppName]" msg_info "Configuring [AppName]"
cd /opt/[appname]
# Install application dependencies (uncomment as needed):
# $STD npm ci --production # Node.js apps
# $STD uv sync --frozen # Python apps
# $STD composer install --no-dev # PHP apps
# $STD cargo build --release # Rust apps
# Create .env file if needed:
cat <<EOF >/opt/[appname]/.env cat <<EOF >/opt/[appname]/.env
# Use import_local_ip to get container IP, or hardcode if building on Proxmox HOST=${LOCAL_IP}
APP_URL=http://localhost
PORT=8080 PORT=8080
EOF EOF
msg_ok "Configured [AppName]" msg_ok "Configured [AppName]"
# ============================================================================= # =============================================================================
# CREATE SYSTEMD SERVICE # SERVICE CREATION
# ============================================================================= # =============================================================================
msg_info "Creating Service" msg_info "Creating Service"
@@ -197,11 +182,9 @@ msg_ok "Created Service"
# ============================================================================= # =============================================================================
# CLEANUP & FINALIZATION # CLEANUP & FINALIZATION
# ============================================================================= # =============================================================================
# These are called automatically, but shown here for clarity:
# motd_ssh - Displays service info on SSH login
# customize - Enables optional customizations
# cleanup_lxc - Removes temp files, bash history, logs
motd_ssh motd_ssh
customize customize
# cleanup_lxc handles: apt autoremove, autoclean, temp files, bash history
cleanup_lxc cleanup_lxc

View File

@@ -4,26 +4,25 @@
"categories": [ "categories": [
0 0
], ],
"date_created": "2026-01-18", "date_created": "DATE CREATED",
"type": "ct", "type": "ct",
"updateable": true, "updateable": true,
"privileged": false, "privileged": false,
"interface_port": 3000, "interface_port": "DEFAULT-PORT",
"documentation": "https://docs.example.com/", "documentation": null,
"website": "https://example.com/", "website": "LINK TO WEBSITE",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/appname.webp", "logo": "LINK TO LOGO",
"config_path": "/opt/appname/.env", "description": "Description of the app",
"description": "Short description of what AppName does and its main features.",
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "ct/appname.sh", "script": "ct/AppName.sh",
"resources": { "resources": {
"cpu": 2, "cpu": 2,
"ram": 2048, "ram": 2048,
"hdd": 8, "hdd": 4,
"os": "Debian", "os": "debian",
"version": "13" "version": "12"
} }
} }
], ],
@@ -31,10 +30,5 @@
"username": null, "username": null,
"password": null "password": null
}, },
"notes": [ "notes": []
{
"text": "Change the default password after first login!",
"type": "warning"
}
]
} }

View File

@@ -1,165 +1,13 @@
# JSON Metadata Files - Quick Reference # **AppName<span></span>.json Files**
The metadata file (`frontend/public/json/myapp.json`) tells the web interface how to display your application. `AppName.json` files found in the `/json` directory. These files are used to provide informations for the website. For this guide we take `/json/snipeit.json` as example.
--- ## Table of Contents
## Quick Start - [**AppName.json Files**](#appnamejson-files)
- [Table of Contents](#table-of-contents)
- [1. JSON Generator](#1-json-generator)
**Use the JSON Generator Tool:** ## 1. JSON Generator
[https://community-scripts.github.io/ProxmoxVE/json-editor](https://community-scripts.github.io/ProxmoxVE/json-editor)
1. Enter application details Use the [JSON Generator](https://community-scripts.github.io/ProxmoxVE/json-editor) to create this file for your application.
2. Generator creates `frontend/public/json/myapp.json`
3. Copy the output to your contribution
---
## File Structure
```json
{
"name": "MyApp",
"slug": "myapp",
"categories": [1],
"date_created": "2026-01-18",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3000,
"documentation": "https://docs.example.com/",
"website": "https://example.com/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/myapp.webp",
"config_path": "/opt/myapp/.env",
"description": "Brief description of what MyApp does",
"install_methods": [
{
"type": "default",
"script": "ct/myapp.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 8,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "Change the default password after first login!",
"type": "warning"
}
]
}
```
---
## Field Reference
| Field | Required | Example | Notes |
| --------------------- | -------- | ----------------- | ---------------------------------------------- |
| `name` | Yes | "MyApp" | Display name |
| `slug` | Yes | "myapp" | URL-friendly identifier (lowercase, no spaces) |
| `categories` | Yes | [1] | One or more category IDs |
| `date_created` | Yes | "2026-01-18" | Format: YYYY-MM-DD |
| `type` | Yes | "ct" | Container type: "ct" or "vm" |
| `interface_port` | Yes | 3000 | Default web interface port |
| `logo` | No | "https://..." | Logo URL (64px x 64px PNG) |
| `config_path` | Yes | "/opt/myapp/.env" | Main config file location |
| `description` | Yes | "App description" | Brief description (100 chars) |
| `install_methods` | Yes | See below | Installation resources (array) |
| `default_credentials` | No | See below | Optional default login |
| `notes` | No | See below | Additional notes (array) |
---
## Install Methods
Each installation method specifies resource requirements:
```json
"install_methods": [
{
"type": "default",
"script": "ct/myapp.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 8,
"os": "Debian",
"version": "13"
}
}
]
```
**Resource Defaults:**
- CPU: Cores (1-8)
- RAM: Megabytes (256-4096)
- Disk: Gigabytes (4-50)
---
## Common Categories
- `0` Miscellaneous
- `1` Proxmox & Virtualization
- `2` Operating Systems
- `3` Containers & Docker
- `4` Network & Firewall
- `5` Adblock & DNS
- `6` Authentication & Security
- `7` Backup & Recovery
- `8` Databases
- `9` Monitoring & Analytics
- `10` Dashboards & Frontends
- `11` Files & Downloads
- `12` Documents & Notes
- `13` Media & Streaming
- `14` \*Arr Suite
- `15` NVR & Cameras
- `16` IoT & Smart Home
- `17` ZigBee, Z-Wave & Matter
- `18` MQTT & Messaging
- `19` Automation & Scheduling
- `20` AI / Coding & Dev-Tools
- `21` Webservers & Proxies
- `22` Bots & ChatOps
- `23` Finance & Budgeting
- `24` Gaming & Leisure
- `25` Business & ERP
---
## Best Practices
1. **Use the JSON Generator** - It validates structure
2. **Keep descriptions short** - 100 characters max
3. **Use real resource requirements** - Based on your testing
4. **Include sensible defaults** - Pre-filled in install_methods
5. **Slug must be lowercase** - No spaces, use hyphens
---
## Reference Examples
See actual examples in the repo:
- [frontend/public/json/trip.json](https://github.com/community-scripts/ProxmoxVE/blob/main/frontend/public/json/trip.json)
- [frontend/public/json/thingsboard.json](https://github.com/community-scripts/ProxmoxVE/blob/main/frontend/public/json/thingsboard.json)
- [frontend/public/json/unifi.json](https://github.com/community-scripts/ProxmoxVE/blob/main/frontend/public/json/unifi.json)
---
## Need Help?
- **[JSON Generator](https://community-scripts.github.io/ProxmoxVE/json-editor)** - Interactive tool
- **[README.md](../README.md)** - Full contribution workflow
- **[Quick Start](../README.md)** - Step-by-step guide

View File

@@ -539,69 +539,6 @@ var_nesting=0 # Nested containers disabled
--- ---
### var_diagnostics
**Type:** Boolean (yes or no)
**Default:** `yes`
**Description:** Determines if anonymous telemetry and diagnostic data is sent to Community-Scripts API.
```bash
var_diagnostics=yes # Allow telemetry (helps us improve scripts)
var_diagnostics=no # Disable all telemetry
```
**Privacy & Usage:**
- Data is strictly anonymous (random session ID)
- Reports success/failure of installations
- Maps error codes (e.g., APT lock, out of RAM)
- No user-specific data, hostnames, or secret keys are ever sent
---
### var_gpu
**Type:** Boolean/Toggle
**Options:** `yes` or `no`
**Default:** `no`
**Description:** Enable GPU passthrough for the container.
```bash
var_gpu=yes # Enable GPU passthrough (auto-detect)
var_gpu=no # Disable GPU passthrough (default)
```
**Features enabled:**
- Auto-detects Intel (QuickSync), NVIDIA, and AMD GPUs
- Passes through `/dev/dri` and render nodes
- Configures appropriate container permissions
- Crucial for media servers (Plex, Jellyfin, Immich)
**Prerequisites:**
- Host drivers installed correctly
- Hardware present and visible to Proxmox
- IOMMU enabled (for some configurations)
---
### var_tun
**Type:** Boolean/Toggle
**Options:** `yes` or `no`
**Default:** `no`
**Description:** Enable TUN/TAP device support.
```bash
var_tun=yes # Enable TUN/TAP support
var_tun=no # Disable TUN/TAP support (default)
```
**Required for:**
- VPN software (WireGuard, OpenVPN)
- Network tunneling (Tailscale, ZeroTier)
- Custom network bridges
---
### var_keyctl ### var_keyctl
**Type:** Boolean (0 or 1) **Type:** Boolean (0 or 1)
@@ -623,14 +560,13 @@ var_keyctl=0 # Keyctl disabled
### var_fuse ### var_fuse
**Type:** Boolean/Toggle **Type:** Boolean (0 or 1)
**Options:** `yes` or `no` **Default:** `0`
**Default:** `no`
**Description:** Enable FUSE filesystem support. **Description:** Enable FUSE filesystem support.
```bash ```bash
var_fuse=yes # FUSE enabled var_fuse=1 # FUSE enabled
var_fuse=no # FUSE disabled var_fuse=0 # FUSE disabled
``` ```
**Required for:** **Required for:**

View File

@@ -57,7 +57,7 @@ pveversion
### 2. Network Connectivity ### 2. Network Connectivity
```bash ```bash
# Test GitHub access # Test GitHub access
curl -I https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh curl -I https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh
# Test internet connectivity # Test internet connectivity
ping -c 1 1.1.1.1 ping -c 1 1.1.1.1
@@ -108,8 +108,6 @@ var_cpu=4 \
var_ram=4096 \ var_ram=4096 \
var_disk=30 \ var_disk=30 \
var_hostname=production-app \ var_hostname=production-app \
var_os=debian \
var_version=13 \
var_brg=vmbr0 \ var_brg=vmbr0 \
var_net=dhcp \ var_net=dhcp \
var_ipv6_method=none \ var_ipv6_method=none \
@@ -119,7 +117,7 @@ var_nesting=1 \
var_tags=production,automated \ var_tags=production,automated \
var_protection=yes \ var_protection=yes \
var_verbose=no \ var_verbose=no \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)"
echo "✓ Container deployed successfully" echo "✓ Container deployed successfully"
``` ```
@@ -153,7 +151,7 @@ var_gateway=192.168.1.1 \
**Step 1: Create defaults once (interactive)** **Step 1: Create defaults once (interactive)**
```bash ```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/pihole.sh)"
# Select "Advanced Settings" → Configure → Save as "App Defaults" # Select "Advanced Settings" → Configure → Save as "App Defaults"
``` ```
@@ -163,30 +161,10 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/Proxmo
# deploy-with-defaults.sh # deploy-with-defaults.sh
# App defaults are loaded automatically # App defaults are loaded automatically
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/pihole.sh)"
# Script will use /usr/local/community-scripts/defaults/pihole.vars # Script will use /usr/local/community-scripts/defaults/pihole.vars
``` ```
### Advanced Configuration Variables
Beyond the basic resource settings, you can control advanced container features:
| Variable | Description | Options |
|----------|-------------|---------|
| `var_os` | Operating system template | `debian`, `ubuntu`, `alpine` |
| `var_version` | OS version | `12`, `13` (Debian), `22.04`, `24.04` (Ubuntu) |
| `var_gpu` | Enable GPU passthrough | `yes`, `no` (Default: `no`) |
| `var_tun` | Enable TUN/TAP device | `yes`, `no` (Default: `no`) |
| `var_nesting` | Enable nesting | `1`, `0` (Default: `1`) |
**Example with GPU and TUN:**
```bash
var_gpu=yes \
var_tun=yes \
var_hostname=transcoder \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/plex.sh)"
```
--- ---
## Batch Deployments ## Batch Deployments
@@ -199,14 +177,14 @@ var_hostname=transcoder \
#!/bin/bash #!/bin/bash
# batch-deploy-simple.sh # batch-deploy-simple.sh
apps=("thingsboard" "qui" "flatnotes") apps=("debian" "ubuntu" "alpine")
for app in "${apps[@]}"; do for app in "${apps[@]}"; do
echo "Deploying $app..." echo "Deploying $app..."
var_hostname="$app-server" \ var_hostname="$app-container" \
var_cpu=2 \ var_cpu=2 \
var_ram=2048 \ var_ram=2048 \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)"
echo "$app deployed" echo "$app deployed"
sleep 5 # Wait between deployments sleep 5 # Wait between deployments
@@ -220,10 +198,10 @@ done
# batch-deploy-advanced.sh - Deploy multiple containers with individual configs # batch-deploy-advanced.sh - Deploy multiple containers with individual configs
declare -A CONTAINERS=( declare -A CONTAINERS=(
["beszel"]="1:512:8:vmbr0:monitoring" ["pihole"]="2:1024:8:vmbr0:dns,network"
["qui"]="2:1024:10:vmbr0:torrent,ui" ["homeassistant"]="4:4096:20:vmbr0:automation,ha"
["thingsboard"]="6:8192:50:vmbr1:iot,industrial" ["docker"]="6:8192:50:vmbr1:containers,docker"
["dockge"]="2:2048:10:vmbr0:docker,management" ["nginx"]="2:2048:10:vmbr0:webserver,proxy"
) )
for app in "${!CONTAINERS[@]}"; do for app in "${!CONTAINERS[@]}"; do
@@ -250,7 +228,7 @@ for app in "${!CONTAINERS[@]}"; do
var_ipv6_method=none \ var_ipv6_method=none \
var_ssh=yes \ var_ssh=yes \
var_tags="$tags,automated" \ var_tags="$tags,automated" \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" 2>&1 | tee "deploy-${app}.log" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)" 2>&1 | tee "deploy-${app}.log"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "$app deployed successfully" echo "$app deployed successfully"
@@ -285,7 +263,7 @@ deploy_container() {
var_disk="$disk" \ var_disk="$disk" \
var_hostname="$app" \ var_hostname="$app" \
var_net=dhcp \ var_net=dhcp \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)" \
&> "deploy-${app}.log" &> "deploy-${app}.log"
echo "[$app] ✓ Completed" echo "[$app] ✓ Completed"
@@ -316,7 +294,7 @@ echo "All deployments complete!"
```yaml ```yaml
--- ---
# playbook-proxmox.yml # playbook-proxmox.yml
- name: Deploy ProxmoxVE Containers - name: Deploy ProxmoxVED Containers
hosts: proxmox_hosts hosts: proxmox_hosts
become: yes become: yes
tasks: tasks:
@@ -330,7 +308,7 @@ echo "All deployments complete!"
var_net=dhcp \ var_net=dhcp \
var_ssh=yes \ var_ssh=yes \
var_tags=ansible,automated \ var_tags=ansible,automated \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)"
args: args:
executable: /bin/bash executable: /bin/bash
register: deploy_result register: deploy_result
@@ -387,7 +365,7 @@ echo "All deployments complete!"
var_ssh=yes \ var_ssh=yes \
var_ssh_authorized_key="{{ ssh_key }}" \ var_ssh_authorized_key="{{ ssh_key }}" \
var_tags="{{ item.tags }},ansible" \ var_tags="{{ item.tags }},ansible" \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/{{ item.name }}.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/{{ item.name }}.sh)"
args: args:
executable: /bin/bash executable: /bin/bash
loop: "{{ containers }}" loop: "{{ containers }}"
@@ -439,7 +417,7 @@ resource "null_resource" "deploy_container" {
"var_disk=${each.value.disk}", "var_disk=${each.value.disk}",
"var_hostname=${each.key}", "var_hostname=${each.key}",
"var_net=dhcp", "var_net=dhcp",
"bash -c \"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${each.value.template}.sh)\"" "bash -c \"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${each.value.template}.sh)\""
] ]
connection { connection {
@@ -520,7 +498,7 @@ jobs:
var_net=dhcp \ var_net=dhcp \
var_ssh=yes \ var_ssh=yes \
var_tags=ci-cd,automated \ var_tags=ci-cd,automated \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${{ github.event.inputs.container_type }}.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${{ github.event.inputs.container_type }}.sh)"
- name: Notify deployment status - name: Notify deployment status
if: success() if: success()
@@ -554,7 +532,7 @@ deploy_container:
var_hostname=gitlab-ci-container \ var_hostname=gitlab-ci-container \
var_net=dhcp \ var_net=dhcp \
var_tags=gitlab-ci,automated \ var_tags=gitlab-ci,automated \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)"
EOF EOF
only: only:
- main - main
@@ -586,7 +564,7 @@ deploy_container() {
var_hostname="$HOSTNAME" \ var_hostname="$HOSTNAME" \
var_net=dhcp \ var_net=dhcp \
var_ssh=yes \ var_ssh=yes \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${APP}.sh)" 2>&1 | tee deploy.log bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${APP}.sh)" 2>&1 | tee deploy.log
return ${PIPESTATUS[0]} return ${PIPESTATUS[0]}
} }
@@ -678,7 +656,7 @@ deploy() {
var_hostname="$HOSTNAME" \ var_hostname="$HOSTNAME" \
var_cpu=4 \ var_cpu=4 \
var_ram=4096 \ var_ram=4096 \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${APP}.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${APP}.sh)"
return $? return $?
} }
@@ -781,7 +759,7 @@ deploy_secure() {
var_protection=yes \ var_protection=yes \
var_tags=production,secure,automated \ var_tags=production,secure,automated \
var_verbose=no \ var_verbose=no \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${APP}.sh)" 2>&1 | tee -a "$LOG_FILE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${APP}.sh)" 2>&1 | tee -a "$LOG_FILE"
if [ ${PIPESTATUS[0]} -eq 0 ]; then if [ ${PIPESTATUS[0]} -eq 0 ]; then
log "✓ Deployment successful" log "✓ Deployment successful"
@@ -846,7 +824,7 @@ SSH_KEYS=$(load_ssh_keys)
var_ssh=yes \ var_ssh=yes \
var_ssh_authorized_key="$SSH_KEYS" \ var_ssh_authorized_key="$SSH_KEYS" \
var_hostname=multi-key-server \ var_hostname=multi-key-server \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/debian.sh)"
``` ```
--- ---
@@ -925,7 +903,7 @@ deploy_from_config() {
var_ssh=yes \ var_ssh=yes \
var_tags="$tags,automated" \ var_tags="$tags,automated" \
var_protection=yes \ var_protection=yes \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
log_success "Deployed: $name" log_success "Deployed: $name"

View File

@@ -56,7 +56,7 @@ get_error_description 255 # "Unknown critical error, often due to missing perm
- `DIAGNOSTICS` must be set to "yes" - `DIAGNOSTICS` must be set to "yes"
- `RANDOM_UUID` must be set and not empty - `RANDOM_UUID` must be set and not empty
**API Endpoint**: `https://api.community-scripts.org/dev/upload` **API Endpoint**: `http://api.community-scripts.org/dev/upload`
**JSON Payload Structure**: **JSON Payload Structure**:
```json ```json
@@ -110,7 +110,7 @@ post_to_api
- `curl` command must be available - `curl` command must be available
- `RANDOM_UUID` must be set and not empty - `RANDOM_UUID` must be set and not empty
**API Endpoint**: `https://api.community-scripts.org/dev/upload` **API Endpoint**: `http://api.community-scripts.org/dev/upload`
**JSON Payload Structure**: **JSON Payload Structure**:
```json ```json
@@ -167,7 +167,7 @@ post_to_api_vm
- `RANDOM_UUID` must be set and not empty - `RANDOM_UUID` must be set and not empty
- POST_UPDATE_DONE must be false (prevents duplicates) - POST_UPDATE_DONE must be false (prevents duplicates)
**API Endpoint**: `https://api.community-scripts.org/dev/upload/updatestatus` **API Endpoint**: `http://api.community-scripts.org/dev/upload/updatestatus`
**JSON Payload Structure**: **JSON Payload Structure**:
```json ```json

View File

@@ -461,7 +461,7 @@ check_api_health() {
echo "Error description test: $test_error" echo "Error description test: $test_error"
# Test API connectivity (without sending data) # Test API connectivity (without sending data)
local api_url="https://api.community-scripts.org/dev/upload" local api_url="http://api.community-scripts.org/dev/upload"
if curl -s --head "$api_url" >/dev/null 2>&1; then if curl -s --head "$api_url" >/dev/null 2>&1; then
echo "API endpoint is reachable" echo "API endpoint is reachable"
else else

View File

@@ -438,34 +438,6 @@ default_var_settings() # Save global defaults
maybe_offer_save_app_defaults() # Save app defaults maybe_offer_save_app_defaults() # Save app defaults
``` ```
### Container Resource & ID Management
#### `validate_container_id()`
**Purpose**: Validates if a container ID is available for use.
**Parameters**: `ctid` (Integer)
**Returns**: `0` if available, `1` if already in use or invalid.
**Description**: Checks for existing config files in `/etc/pve/lxc/` or `/etc/pve/qemu-server/`, and verifies LVM logical volumes.
#### `get_valid_container_id()`
**Purpose**: Returns the next available, unused container ID.
**Parameters**: `suggested_id` (Optional)
**Returns**: A valid container ID string.
**Description**: If the suggested ID is taken, it increments until it finds an available one.
#### `maxkeys_check()`
**Purpose**: Ensures host kernel parameters support high numbers of keys (required for some apps).
**Parameters**: None
**Description**: Checks and optionally updates `kernel.keys.maxkeys` and `kernel.keys.maxbytes`.
#### `get_current_ip()`
**Purpose**: Retrieves the current IP address of the container.
**Parameters**: `ctid` (Integer)
**Returns**: IP address string.
#### `update_motd_ip()`
**Purpose**: Updates the Message of the Day (MOTD) file with the container's IP.
**Parameters**: None
## Function Error Handling ## Function Error Handling
### Validation Functions ### Validation Functions

View File

@@ -60,10 +60,10 @@ export var_gateway="192.168.1.1"
export var_ip="192.168.1.101" export var_ip="192.168.1.101"
export var_vlan="100" export var_vlan="100"
export var_mtu="9000" export var_mtu="9000"
export var_template_storage="ssd-storage" export var_template_storage="nfs-storage"
export var_container_storage="ssd-storage" export var_container_storage="ssd-storage"
export var_fuse="yes" export ENABLE_FUSE="true"
export var_tun="yes" export ENABLE_TUN="true"
export SSH="true" export SSH="true"
# Execute build.func # Execute build.func

View File

@@ -20,19 +20,14 @@ Complete alphabetical reference of all functions in tools.func with parameters,
- `setup_nodejs(VERSION)` - Install Node.js and npm - `setup_nodejs(VERSION)` - Install Node.js and npm
- `setup_php(VERSION)` - Install PHP-FPM and CLI - `setup_php(VERSION)` - Install PHP-FPM and CLI
- `setup_python(VERSION)` - Install Python 3 with pip - `setup_python(VERSION)` - Install Python 3 with pip
- `setup_uv()` - Install Python uv (modern & fast)
- `setup_ruby(VERSION)` - Install Ruby with gem - `setup_ruby(VERSION)` - Install Ruby with gem
- `setup_golang(VERSION)` - Install Go programming language - `setup_golang(VERSION)` - Install Go programming language
- `setup_java(VERSION)` - Install OpenJDK (Adoptium)
**Databases**: **Databases**:
- `setup_mariadb()` - Install MariaDB server - `setup_mariadb()` - Install MariaDB server (distro packages by default)
- `setup_mariadb_db()` - Create user/db in MariaDB
- `setup_postgresql(VERSION)` - Install PostgreSQL - `setup_postgresql(VERSION)` - Install PostgreSQL
- `setup_postgresql_db()` - Create user/db in PostgreSQL
- `setup_mongodb(VERSION)` - Install MongoDB - `setup_mongodb(VERSION)` - Install MongoDB
- `setup_redis(VERSION)` - Install Redis cache - `setup_redis(VERSION)` - Install Redis cache
- `setup_meilisearch()` - Install Meilisearch engine
**Web Servers**: **Web Servers**:
- `setup_nginx()` - Install Nginx - `setup_nginx()` - Install Nginx
@@ -49,7 +44,6 @@ Complete alphabetical reference of all functions in tools.func with parameters,
- `setup_docker_compose()` - Install Docker Compose - `setup_docker_compose()` - Install Docker Compose
- `setup_composer()` - Install PHP Composer - `setup_composer()` - Install PHP Composer
- `setup_build_tools()` - Install build-essential - `setup_build_tools()` - Install build-essential
- `setup_yq()` - Install mikefarah/yq processor
**Monitoring**: **Monitoring**:
- `setup_grafana()` - Install Grafana - `setup_grafana()` - Install Grafana
@@ -66,13 +60,13 @@ Complete alphabetical reference of all functions in tools.func with parameters,
## Core Functions ## Core Functions
### install_packages_with_retry() ### pkg_install()
Install one or more packages safely with automatic retry logic (3 attempts), APT refresh, and lock handling. Install one or more packages safely with automatic retry logic and error handling.
**Signature**: **Signature**:
```bash ```bash
install_packages_with_retry PACKAGE1 [PACKAGE2 ...] pkg_install PACKAGE1 [PACKAGE2 ...]
``` ```
**Parameters**: **Parameters**:
@@ -80,122 +74,66 @@ install_packages_with_retry PACKAGE1 [PACKAGE2 ...]
**Returns**: **Returns**:
- `0` - All packages installed successfully - `0` - All packages installed successfully
- `1` - Installation failed after all retries - `1` - Installation failed after retries
**Features**: **Environment Variables**:
- Automatically sets `DEBIAN_FRONTEND=noninteractive` - `$STD` - Output suppression (`silent` or empty)
- Handles DPKG lock errors with `dpkg --configure -a`
- Retries on transient network or APT failures
**Example**: **Example**:
```bash ```bash
install_packages_with_retry curl wget git pkg_install curl wget git
``` ```
--- ---
### upgrade_packages_with_retry() ### pkg_update()
Upgrades installed packages with the same robust retry logic as the installation helper. Update package lists with automatic retry logic for network failures.
**Signature**: **Signature**:
```bash ```bash
upgrade_packages_with_retry pkg_update
``` ```
**Parameters**: None
**Returns**: **Returns**:
- `0` - Upgrade successful - `0` - Package lists updated
- `1` - Upgrade failed - `1` - Failed after 3 retries
---
### fetch_and_deploy_gh_release()
The primary tool for downloading and installing software from GitHub Releases. Supports binaries, tarballs, and Debian packages.
**Signature**:
```bash
fetch_and_deploy_gh_release APPREPO TYPE [VERSION] [DEST] [ASSET_PATTERN]
```
**Environment Variables**:
- `APPREPO`: GitHub repository (e.g., `owner/repo`)
- `TYPE`: Asset type (`binary`, `tarball`, `prebuild`, `singlefile`)
- `VERSION`: Specific tag or `latest` (Default: `latest`)
- `DEST`: Target directory (Default: `/opt/$APP`)
- `ASSET_PATTERN`: Regex or string pattern to match the release asset (Required for `prebuild` and `singlefile`)
**Supported Operation Modes**:
- `tarball`: Downloads and extracts the source tarball.
- `binary`: Detects host architecture and installs a `.deb` package using `apt` or `dpkg`.
- `prebuild`: Downloads and extracts a pre-built binary archive (supports `.tar.gz`, `.zip`, `.tgz`, `.txz`).
- `singlefile`: Downloads a single binary file to the destination.
**Environment Variables**:
- `CLEAN_INSTALL=1`: Removes all contents of the destination directory before extraction.
- `DPKG_FORCE_CONFOLD=1`: Forces `dpkg` to keep old config files during package updates.
- `SYSTEMD_OFFLINE=1`: Used automatically for `.deb` installs to prevent systemd-tmpfiles failures in unprivileged containers.
**Example**: **Example**:
```bash ```bash
fetch_and_deploy_gh_release "muesli/duf" "binary" "latest" "/opt/duf" "duf_.*_linux_amd64.tar.gz" pkg_update
``` ```
--- ---
### check_for_gh_release() ### pkg_remove()
Checks if a newer version is available on GitHub compared to the installed version. Remove packages completely including dependencies.
**Signature**: **Signature**:
```bash ```bash
check_for_gh_release APP REPO pkg_remove PACKAGE1 [PACKAGE2 ...]
``` ```
**Parameters**:
- `PACKAGE1, PACKAGE2, ...` - Package names to remove
**Returns**:
- `0` - Packages removed
- `1` - Removal failed
**Example**: **Example**:
```bash ```bash
if check_for_gh_release "nodejs" "nodesource/distributions"; then pkg_remove old-package outdated-tool
# update logic
fi
```
---
### prepare_repository_setup()
Performs safe repository preparation by cleaning up old files, keyrings, and ensuring the APT system is in a working state.
**Signature**:
```bash
prepare_repository_setup REPO_NAME [REPO_NAME2 ...]
```
**Example**:
```bash
prepare_repository_setup "mariadb" "mysql"
```
---
### verify_tool_version()
Validates if the installed major version matches the expected version.
**Signature**:
```bash
verify_tool_version NAME EXPECTED INSTALLED
```
**Example**:
```bash
verify_tool_version "nodejs" "22" "$(node -v | grep -oP '^v\K[0-9]+')"
``` ```
--- ---
### setup_deb822_repo() ### setup_deb822_repo()
Add repository in modern deb822 format. Add repository in modern deb822 format (recommended over legacy format).
**Signature**: **Signature**:
```bash ```bash
@@ -250,234 +188,12 @@ cleanup_repo_metadata
## Tool Installation Functions ## Tool Installation Functions
### setup_nodejs() ### setup_nodejs(VERSION)
Install Node.js and npm from official repositories. Handles legacy version cleanup (nvm) automatically. Install Node.js and npm from official repositories.
**Signature**: **Signature**:
```bash ```bash
setup_nodejs
```
**Environment Variables**:
- `NODE_VERSION`: Major version to install (e.g. "20", "22", "24"). Default: "24".
- `NODE_MODULE`: Optional npm package to install globally during setup (e.g. "pnpm", "yarn").
**Example**:
```bash
NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs
```
---
### setup_php()
Install PHP with configurable extensions and FPM/Apache integration.
**Signature**:
```bash
setup_php
```
**Environment Variables**:
- `PHP_VERSION`: Version to install (e.g. "8.3", "8.4"). Default: "8.4".
- `PHP_MODULE`: Comma-separated list of additional extensions.
- `PHP_FPM`: Set to "YES" to install php-fpm.
- `PHP_APACHE`: Set to "YES" to install libapache2-mod-php.
**Example**:
```bash
PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="mysql,xml,zip" setup_php
```
---
### setup_mariadb_db()
Creates a new MariaDB database and a dedicated user with all privileges. Automatically generates a password if not provided and saves it to a credentials file.
**Environment Variables**:
- `MARIADB_DB_NAME`: Name of the database (required)
- `MARIADB_DB_USER`: Name of the database user (required)
- `MARIADB_DB_PASS`: User password (optional, auto-generated if omitted)
**Example**:
```bash
MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp_user" setup_mariadb_db
```
---
### setup_postgresql_db()
Creates a new PostgreSQL database and a dedicated user/role with all privileges. Automatically generates a password if not provided and saves it to a credentials file.
**Environment Variables**:
- `PG_DB_NAME`: Name of the database (required)
- `PG_DB_USER`: Name of the database user (required)
- `PG_DB_PASS`: User password (optional, auto-generated if omitted)
---
### setup_java()
Installs Temurin JDK.
**Signature**:
```bash
JAVA_VERSION="21" setup_java
```
**Parameters**:
- `JAVA_VERSION` - JDK version (e.g., "17", "21") (default: "21")
**Example**:
```bash
JAVA_VERSION="17" setup_java
```
---
### setup_uv()
Installs `uv` (modern Python package manager).
**Signature**:
```bash
PYTHON_VERSION="3.13" setup_uv
```
**Parameters**:
- `PYTHON_VERSION` - Optional Python version to pre-install via uv (e.g., "3.12", "3.13")
**Example**:
```bash
PYTHON_VERSION="3.13" setup_uv
```
---
### setup_go()
Installs Go programming language.
**Signature**:
```bash
GO_VERSION="1.23" setup_go
```
**Parameters**:
- `GO_VERSION` - Go version to install (default: "1.23")
**Example**:
```bash
GO_VERSION="1.24" setup_go
```
---
### setup_yq()
Installs `yq` (YAML processor).
**Signature**:
```bash
setup_yq
```
**Example**:
```bash
setup_yq
```
---
### setup_composer()
Installs PHP Composer.
**Signature**:
```bash
setup_composer
```
**Example**:
```bash
setup_composer
```
---
### setup_meilisearch()
Install and configure Meilisearch search engine.
**Environment Variables**:
- `MEILISEARCH_BIND`: Address and port to bind to (Default: "127.0.0.1:7700")
- `MEILISEARCH_ENV`: Environment mode (Default: "production")
---
### setup_yq()
Install the `mikefarah/yq` YAML processor. Removes existing non-compliant versions.
**Example**:
```bash
setup_yq
yq eval '.app.version = "1.0.0"' -i config.yaml
```
---
### setup_composer()
Install or update the PHP Composer package manager. Handles `COMPOSER_ALLOW_SUPERUSER` automatically and performs self-updates if already installed.
**Example**:
```bash
setup_php
setup_composer
$STD composer install --no-dev
```
---
### setup_build_tools()
Install the `build-essential` package suite for compiling software.
---
### setup_uv()
Install the modern Python package manager `uv`. Extremely fast replacement for pip/venv.
**Environment Variables**:
- `PYTHON_VERSION`: Major.Minor version to ensure is installed.
**Example**:
```bash
PYTHON_VERSION="3.12" setup_uv
uv sync --locked
```
---
### setup_java()
Install OpenJDK via the Adoptium repository.
**Environment Variables**:
- `JAVA_VERSION`: Major version to install (e.g. "17", "21"). Default: "21".
**Example**:
```bash
JAVA_VERSION="21" setup_java
```
---
```bash
setup_nodejs VERSION setup_nodejs VERSION
``` ```

View File

@@ -40,15 +40,14 @@ vm/OsName-vm.sh (host-side)
See `/vm` directory for all VM creation scripts. Examples: See `/vm` directory for all VM creation scripts. Examples:
- `ubuntu2504-vm.sh` - Ubuntu 25.04 VM (Latest) - `ubuntu2404-vm.sh` - Ubuntu 24.04 VM
- `ubuntu2404-vm.sh` - Ubuntu 24.04 VM (LTS) - `ubuntu2204-vm.sh` - Ubuntu 22.04 VM
- `debian-13-vm.sh` - Debian 13 VM (Trixie) - `debian-vm.sh` - Debian VM
- `archlinux-vm.sh` - Arch Linux VM - `debian-13-vm.sh` - Debian 13 VM
- `haos-vm.sh` - Home Assistant OS
- `mikrotik-routeros.sh` - MikroTik RouterOS
- `openwrt-vm.sh` - OpenWrt VM
- `opnsense-vm.sh` - OPNsense firewall - `opnsense-vm.sh` - OPNsense firewall
- `umbrel-os-vm.sh` - Umbrel OS VM - `haos-vm.sh` - Home Assistant OS
- `unifi-os-vm.sh` - Unifi Dream Machine
- `k3s-vm.sh` - Kubernetes lightweight
- And 10+ more... - And 10+ more...
## VM vs Container ## VM vs Container

View File

@@ -1,5 +1,5 @@
{ {
"generated": "2026-02-05T18:19:13Z", "generated": "2026-02-04T06:18:03Z",
"versions": [ "versions": [
{ {
"slug": "2fauth", "slug": "2fauth",
@@ -179,16 +179,16 @@
{ {
"slug": "cloudreve", "slug": "cloudreve",
"repo": "cloudreve/cloudreve", "repo": "cloudreve/cloudreve",
"version": "4.13.0", "version": "4.12.1",
"pinned": false, "pinned": false,
"date": "2026-02-05T12:53:24Z" "date": "2026-01-28T07:29:16Z"
}, },
{ {
"slug": "comfyui", "slug": "comfyui",
"repo": "comfyanonymous/ComfyUI", "repo": "comfyanonymous/ComfyUI",
"version": "v0.12.3", "version": "v0.12.2",
"pinned": false, "pinned": false,
"date": "2026-02-05T07:04:07Z" "date": "2026-02-04T06:09:31Z"
}, },
{ {
"slug": "commafeed", "slug": "commafeed",
@@ -221,9 +221,9 @@
{ {
"slug": "cronicle", "slug": "cronicle",
"repo": "jhuckaby/Cronicle", "repo": "jhuckaby/Cronicle",
"version": "v0.9.105", "version": "v0.9.102",
"pinned": false, "pinned": false,
"date": "2026-02-05T18:16:11Z" "date": "2025-12-19T03:45:13Z"
}, },
{ {
"slug": "cryptpad", "slug": "cryptpad",
@@ -256,9 +256,9 @@
{ {
"slug": "docmost", "slug": "docmost",
"repo": "docmost/docmost", "repo": "docmost/docmost",
"version": "v0.25.1", "version": "v0.25.0",
"pinned": false, "pinned": false,
"date": "2026-02-04T15:19:51Z" "date": "2026-02-04T00:33:45Z"
}, },
{ {
"slug": "domain-locker", "slug": "domain-locker",
@@ -445,9 +445,9 @@
{ {
"slug": "headscale", "slug": "headscale",
"repo": "juanfont/headscale", "repo": "juanfont/headscale",
"version": "v0.28.0", "version": "v0.27.1",
"pinned": false, "pinned": false,
"date": "2026-02-04T20:40:23Z" "date": "2025-11-11T19:32:29Z"
}, },
{ {
"slug": "healthchecks", "slug": "healthchecks",
@@ -494,9 +494,9 @@
{ {
"slug": "homepage", "slug": "homepage",
"repo": "gethomepage/homepage", "repo": "gethomepage/homepage",
"version": "v1.10.1", "version": "v1.9.0",
"pinned": false, "pinned": false,
"date": "2026-02-05T15:03:45Z" "date": "2026-01-19T05:46:09Z"
}, },
{ {
"slug": "homer", "slug": "homer",
@@ -515,9 +515,9 @@
{ {
"slug": "huntarr", "slug": "huntarr",
"repo": "plexguide/Huntarr.io", "repo": "plexguide/Huntarr.io",
"version": "9.2.1", "version": "9.1.9.1",
"pinned": false, "pinned": false,
"date": "2026-02-05T16:39:35Z" "date": "2026-02-04T01:08:22Z"
}, },
{ {
"slug": "inspircd", "slug": "inspircd",
@@ -536,16 +536,16 @@
{ {
"slug": "invoiceninja", "slug": "invoiceninja",
"repo": "invoiceninja/invoiceninja", "repo": "invoiceninja/invoiceninja",
"version": "v5.12.55", "version": "v5.12.53",
"pinned": false, "pinned": false,
"date": "2026-02-05T01:06:15Z" "date": "2026-02-04T00:52:01Z"
}, },
{ {
"slug": "jackett", "slug": "jackett",
"repo": "Jackett/Jackett", "repo": "Jackett/Jackett",
"version": "v0.24.1032", "version": "v0.24.1027",
"pinned": false, "pinned": false,
"date": "2026-02-05T05:55:27Z" "date": "2026-02-04T05:56:22Z"
}, },
{ {
"slug": "joplin-server", "slug": "joplin-server",
@@ -746,9 +746,9 @@
{ {
"slug": "mealie", "slug": "mealie",
"repo": "mealie-recipes/mealie", "repo": "mealie-recipes/mealie",
"version": "v3.10.2", "version": "v3.10.1",
"pinned": false, "pinned": false,
"date": "2026-02-04T23:32:32Z" "date": "2026-02-03T01:04:38Z"
}, },
{ {
"slug": "mediamanager", "slug": "mediamanager",
@@ -781,9 +781,9 @@
{ {
"slug": "metube", "slug": "metube",
"repo": "alexta69/metube", "repo": "alexta69/metube",
"version": "2026.02.04", "version": "2026.02.03",
"pinned": false, "pinned": false,
"date": "2026-02-04T20:01:18Z" "date": "2026-02-03T21:49:49Z"
}, },
{ {
"slug": "miniflux", "slug": "miniflux",
@@ -834,13 +834,6 @@
"pinned": false, "pinned": false,
"date": "2026-02-03T13:54:26Z" "date": "2026-02-03T13:54:26Z"
}, },
{
"slug": "nginx-ui",
"repo": "0xJacky/nginx-ui",
"version": "v2.3.2",
"pinned": false,
"date": "2025-12-09T09:47:15Z"
},
{ {
"slug": "nocodb", "slug": "nocodb",
"repo": "nocodb/nocodb", "repo": "nocodb/nocodb",
@@ -883,13 +876,6 @@
"pinned": false, "pinned": false,
"date": "2026-01-17T12:24:31Z" "date": "2026-01-17T12:24:31Z"
}, },
{
"slug": "opencloud",
"repo": "opencloud-eu/opencloud",
"version": "v5.0.1",
"pinned": true,
"date": "2026-01-28T15:08:23Z"
},
{ {
"slug": "opengist", "slug": "opengist",
"repo": "thomiceli/opengist", "repo": "thomiceli/opengist",
@@ -1110,9 +1096,9 @@
{ {
"slug": "pulse", "slug": "pulse",
"repo": "rcourtman/Pulse", "repo": "rcourtman/Pulse",
"version": "v5.1.2", "version": "v5.0.17",
"pinned": false, "pinned": false,
"date": "2026-02-05T00:18:57Z" "date": "2026-01-20T19:07:30Z"
}, },
{ {
"slug": "pve-scripts-local", "slug": "pve-scripts-local",
@@ -1285,9 +1271,9 @@
{ {
"slug": "speedtest-tracker", "slug": "speedtest-tracker",
"repo": "alexjustesen/speedtest-tracker", "repo": "alexjustesen/speedtest-tracker",
"version": "v1.13.8", "version": "v1.13.6",
"pinned": false, "pinned": false,
"date": "2026-02-04T19:24:23Z" "date": "2026-02-03T21:20:51Z"
}, },
{ {
"slug": "spoolman", "slug": "spoolman",
@@ -1306,9 +1292,9 @@
{ {
"slug": "stirling-pdf", "slug": "stirling-pdf",
"repo": "Stirling-Tools/Stirling-PDF", "repo": "Stirling-Tools/Stirling-PDF",
"version": "v2.4.4", "version": "v2.4.3",
"pinned": false, "pinned": false,
"date": "2026-02-05T00:15:53Z" "date": "2026-01-31T22:19:14Z"
}, },
{ {
"slug": "streamlink-webui", "slug": "streamlink-webui",
@@ -1383,9 +1369,9 @@
{ {
"slug": "tianji", "slug": "tianji",
"repo": "msgbyte/tianji", "repo": "msgbyte/tianji",
"version": "v1.31.10", "version": "v1.31.9",
"pinned": false, "pinned": false,
"date": "2026-02-04T17:21:04Z" "date": "2026-01-31T18:22:40Z"
}, },
{ {
"slug": "traccar", "slug": "traccar",
@@ -1425,9 +1411,9 @@
{ {
"slug": "trip", "slug": "trip",
"repo": "itskovacs/TRIP", "repo": "itskovacs/TRIP",
"version": "1.38.1", "version": "1.38.0",
"pinned": false, "pinned": false,
"date": "2026-02-04T18:10:15Z" "date": "2026-01-31T15:56:30Z"
}, },
{ {
"slug": "tududi", "slug": "tududi",
@@ -1576,13 +1562,6 @@
"pinned": false, "pinned": false,
"date": "2026-01-08T09:50:00Z" "date": "2026-01-08T09:50:00Z"
}, },
{
"slug": "wishlist",
"repo": "cmintey/wishlist",
"version": "v0.59.0",
"pinned": false,
"date": "2026-01-19T16:42:14Z"
},
{ {
"slug": "wizarr", "slug": "wizarr",
"repo": "wizarrrr/wizarr", "repo": "wizarrrr/wizarr",
@@ -1590,13 +1569,6 @@
"pinned": false, "pinned": false,
"date": "2025-12-09T14:30:23Z" "date": "2025-12-09T14:30:23Z"
}, },
{
"slug": "writefreely",
"repo": "writefreely/writefreely",
"version": "v0.16.0",
"pinned": false,
"date": "2025-08-29T19:30:02Z"
},
{ {
"slug": "yt-dlp-webui", "slug": "yt-dlp-webui",
"repo": "marcopiovanello/yt-dlp-web-ui", "repo": "marcopiovanello/yt-dlp-web-ui",

View File

@@ -1,44 +0,0 @@
{
"name": "Immich Public Proxy",
"slug": "immich-public-proxy",
"categories": [
21
],
"date_created": "2026-02-04",
"type": "addon",
"updateable": true,
"privileged": false,
"interface_port": 3000,
"documentation": "https://github.com/alangrainger/immich-public-proxy/tree/main/docs",
"website": "https://github.com/alangrainger/immich-public-proxy",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/immich-public-proxy.webp",
"config_path": "/opt/immich-proxy/app/.env",
"description": "Share your Immich photos and albums in a safe way without exposing your Immich instance to the public.",
"install_methods": [
{
"type": "default",
"script": "tools/addon/immich-public-proxy.sh",
"resources": {
"cpu": null,
"ram": null,
"hdd": null,
"os": null,
"version": null
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "Requires Node.js 24+",
"type": "info"
},
{
"text": "Update with: update_immich-public-proxy",
"type": "info"
}
]
}

View File

@@ -1,48 +0,0 @@
{
"name": "Nginx UI",
"slug": "nginx-ui",
"categories": [
21
],
"date_created": "2026-02-05",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 9000,
"documentation": "https://nginxui.com/guide/",
"website": "https://nginxui.com",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nginx-ui.webp",
"config_path": "/usr/local/etc/nginx-ui/app.ini",
"description": "Nginx UI is a comprehensive web-based interface designed to simplify the management and configuration of Nginx servers. It provides features like online statistics, ChatGPT-powered config assistant, automatic Let's Encrypt certificates, and config file editing with syntax highlighting.",
"install_methods": [
{
"type": "default",
"script": "ct/nginx-ui.sh",
"resources": {
"cpu": 1,
"ram": 512,
"hdd": 4,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": "admin",
"password": null
},
"notes": [
{
"text": "Nginx runs on ports 80/443, Nginx UI management interface on port 9000.",
"type": "info"
},
{
"text": "SSL certificates can be managed automatically with Let's Encrypt integration.",
"type": "info"
},
{
"text": "Initial Login data: `cat ~/nginx-ui.creds`",
"type": "info"
}
]
}

View File

@@ -1,64 +0,0 @@
{
"name": "OpenCloud",
"slug": "opencloud",
"categories": [
11
],
"date_created": "2026-02-05",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 443,
"documentation": "https://docs.opencloud.eu",
"config_path": "/etc/opencloud/opencloud.env, /etc/opencloud/opencloud.yaml, /etc/opencloud/csp.yaml",
"website": "https://opencloud.eu",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/opencloud.webp",
"description": "OpenCloud is the file sharing and collaboration solution of the Heinlein Group. Through intelligent file management and a strong open source community, files become valuable resources, effectively structured and usable in the long term. With flexible data rooms and intelligent access rights, teams can access and work together on data anytime, anywhere without barriers, but with a lot of productivity.",
"install_methods": [
{
"type": "default",
"script": "ct/opencloud.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 20,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": "admin",
"password": "randomly generated during the installation process"
},
"notes": [
{
"text": "Valid TLS certificates and fully-qualified domain names behind a reverse proxy (Caddy) for 3 services - OpenCloud, Collabora, and WOPI are **REQUIRED**",
"type": "warning"
},
{
"text": "Forgot your admin password? Check `admin_password` in the 'idm' section in `/etc/opencloud/opencloud.yaml`",
"type": "info"
},
{
"text": "**Optional External Apps**: extract zip archives from App Store to `/etc/opencloud/assets/apps`",
"type": "info"
},
{
"text": "**Optional CalDAV and CardDAV**: requires separate Radicale install. Edit and rename `/opt/opencloud/proxy.yaml.bak` and change your Radicale config to use `http_x_remote_user` as the auth method",
"type": "info"
},
{
"text": "**Optional OpenID**: Authelia and PocketID supported. Uncomment relevant lines in `/opt/opencloud/opencloud.env` and consult OpenCloud GitHub discussions for configuration tips",
"type": "info"
},
{
"text": "**Optional Full-text Search with Apache Tika**: requires your own Tika LXC. See `https://community-scripts.github.io/ProxmoxVE/scripts?id=apache-tika`",
"type": "info"
},
{
"text": "**Relevant services**: `opencloud.service`, `opencloud-wopi.service`, `coolwsd.service`",
"type": "info"
}
]
}

View File

@@ -1,52 +0,0 @@
{
"name": "SQL Server 2025",
"slug": "sqlserver2025",
"categories": [
8
],
"date_created": "2026-02-05",
"type": "ct",
"updateable": true,
"privileged": true,
"interface_port": 1433,
"documentation": "https://learn.microsoft.com/en-us/sql/sql-server/?view=sql-server-ver17",
"website": "https://www.microsoft.com/en-us/sql-server/sql-server-2025",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/microsoft-sql-server.webp",
"config_path": "",
"description": "Script to automatically set up a SQL Server 2025 installation with Ubuntu 24.04 support.",
"install_methods": [
{
"type": "default",
"script": "ct/sqlserver2025.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 10,
"os": "Ubuntu",
"version": "24.04"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "If you choose not to run the installation setup, execute: `/opt/mssql/bin/mssql-conf setup` in LXC shell.",
"type": "info"
},
{
"text": "You can setup the admin account 'SA' during installation",
"type": "info"
},
{
"text": "Make sure you disable the SA account if you intend to use this in production!",
"type": "warning"
},
{
"text": "Ubuntu 24.04 support requires SQL Server 2025 CU1 or later",
"type": "info"
}
]
}

View File

@@ -0,0 +1,84 @@
{
"name": "PVE LXC Apps Updater",
"slug": "update-apps",
"categories": [
1
],
"date_created": "2025-02-04",
"type": "pve",
"updateable": true,
"privileged": false,
"interface_port": null,
"documentation": "https://github.com/community-scripts/ProxmoxVE/discussions/11532",
"website": null,
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/proxmox.webp",
"config_path": "",
"description": "This script updates community-scripts managed LXC containers on a Proxmox VE node. It detects the installed service, verifies available update scripts, and applies updates interactively or unattended. Optionally, containers can be backed up before the update process. If additional build resources (CPU/RAM) are required, the script adjusts container resources temporarily and restores them after the update. Containers requiring a reboot will be listed at the end of the process.",
"install_methods": [
{
"type": "default",
"script": "tools/pve/update-apps.sh",
"resources": {
"cpu": null,
"ram": null,
"hdd": null,
"os": null,
"version": null
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "Execute within the Proxmox shell.",
"type": "info"
},
{
"text": "Full Guide can be found here: `https://github.com/community-scripts/ProxmoxVE/discussions/11532`",
"type": "info"
},
{
"text": "Only containers with `community-script` or `proxmox-helper-scripts` tags are listed for update.",
"type": "info"
},
{
"text": "Optionally performs a vzdump backup before updating containers.",
"type": "warning"
},
{
"text": "If required, the script will temporarily increase container CPU/RAM resources for the build process and restore them after completion.",
"type": "info"
},
{
"text": "At the end of the update, containers requiring a reboot will be listed, and you may choose to reboot them directly.",
"type": "info"
},
{
"text": "Use `var_backup=yes|no` to enable/disable backup (skip prompt).",
"type": "info"
},
{
"text": "Use `var_backup_storage=<name>` to set backup storage location.",
"type": "info"
},
{
"text": "Use `var_container=all|all_running|all_stopped|101,102,...` to select containers.",
"type": "info"
},
{
"text": "Use `var_unattended=yes|no` to run updates without interaction.",
"type": "info"
},
{
"text": "Use `var_skip_confirm=yes` to skip initial confirmation dialog.",
"type": "info"
},
{
"text": "Use `var_auto_reboot=yes|no` to auto-reboot containers after update.",
"type": "info"
}
]
}

View File

@@ -29,10 +29,9 @@ import { ScriptSchema } from "./_schemas/schemas";
import Categories from "./_components/categories"; import Categories from "./_components/categories";
import Note from "./_components/note"; import Note from "./_components/note";
import { githubGist, nord } from "react-syntax-highlighter/dist/esm/styles/hljs"; import { nord } from "react-syntax-highlighter/dist/esm/styles/hljs";
import SyntaxHighlighter from "react-syntax-highlighter"; import SyntaxHighlighter from "react-syntax-highlighter";
import { ScriptItem } from "../scripts/_components/script-item"; import { ScriptItem } from "../scripts/_components/script-item";
import { useTheme } from "next-themes";
const initialScript: Script = { const initialScript: Script = {
name: "", name: "",
@@ -59,7 +58,6 @@ const initialScript: Script = {
}; };
export default function JSONGenerator() { export default function JSONGenerator() {
const { theme } = useTheme();
const [script, setScript] = useState<Script>(initialScript); const [script, setScript] = useState<Script>(initialScript);
const [isCopied, setIsCopied] = useState(false); const [isCopied, setIsCopied] = useState(false);
const [isValid, setIsValid] = useState(false); const [isValid, setIsValid] = useState(false);
@@ -359,7 +357,7 @@ export default function JSONGenerator() {
<SyntaxHighlighter <SyntaxHighlighter
language="json" language="json"
style={theme === "light" ? githubGist : nord} style={nord}
className="mt-4 p-4 bg-secondary rounded shadow overflow-x-scroll" className="mt-4 p-4 bg-secondary rounded shadow overflow-x-scroll"
> >
{JSON.stringify(script, null, 2)} {JSON.stringify(script, null, 2)}

View File

@@ -23,34 +23,6 @@ import { Button } from "./ui/button";
import { Badge } from "./ui/badge"; import { Badge } from "./ui/badge";
import Link from "next/link"; import Link from "next/link";
function search(scripts: Script[], query: string): Script[] {
const queryLower = query.toLowerCase().trim();
const searchWords = queryLower.split(/\s+/).filter(Boolean);
return scripts
.map(script => {
const nameLower = script.name.toLowerCase();
const descriptionLower = (script.description || "").toLowerCase();
let score = 0;
for (const word of searchWords) {
if (nameLower.includes(word)) {
score += 10;
}
if (descriptionLower.includes(word)) {
score += 5;
}
}
return { script, score };
})
.filter(({ score }) => score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, 20)
.map(({ script }) => script);
}
export function formattedBadge(type: string) { export function formattedBadge(type: string) {
switch (type) { switch (type) {
case "vm": case "vm":
@@ -79,11 +51,9 @@ function getRandomScript(categories: Category[], previouslySelected: Set<string>
} }
function CommandMenu() { function CommandMenu() {
const [query, setQuery] = React.useState("");
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const [links, setLinks] = React.useState<Category[]>([]); const [links, setLinks] = React.useState<Category[]>([]);
const [isLoading, setIsLoading] = React.useState(false); const [isLoading, setIsLoading] = React.useState(false);
const [results, setResults] = React.useState<Script[]>([]);
const [selectedScripts, setSelectedScripts] = React.useState<Set<string>>(new Set()); const [selectedScripts, setSelectedScripts] = React.useState<Set<string>>(new Set());
const router = useRouter(); const router = useRouter();
@@ -100,27 +70,6 @@ function CommandMenu() {
}); });
}; };
React.useEffect(() => {
if (query.trim() === "") {
fetchSortedCategories();
}
else {
const scriptMap = new Map<string, Script>();
for (const category of links) {
for (const script of category.scripts || []) {
if (!scriptMap.has(script.slug)) {
scriptMap.set(script.slug, script);
}
}
}
const uniqueScripts = Array.from(scriptMap.values());
const filteredResults = search(uniqueScripts, query);
setResults(filteredResults);
}
}, [query]);
React.useEffect(() => { React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) { if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
@@ -248,20 +197,20 @@ function CommandMenu() {
<CommandDialog <CommandDialog
open={open} open={open}
onOpenChange={(open) => { onOpenChange={setOpen}
setOpen(open); filter={(value: string, search: string) => {
if (open) { const searchLower = search.toLowerCase().trim();
setQuery(""); if (!searchLower)
setResults([]); return 1;
} const valueLower = value.toLowerCase();
const searchWords = searchLower.split(/\s+/).filter(Boolean);
// All search words must appear somewhere in the value (name + description)
const allWordsMatch = searchWords.every((word: string) => valueLower.includes(word));
return allWordsMatch ? 1 : 0;
}} }}
> >
<DialogTitle className="sr-only">Search scripts</DialogTitle> <DialogTitle className="sr-only">Search scripts</DialogTitle>
<CommandInput <CommandInput placeholder="Search for a script..." />
placeholder="Search for a script..."
onValueChange={setQuery}
value={query}
/>
<CommandList> <CommandList>
<CommandEmpty> <CommandEmpty>
{isLoading ? ( {isLoading ? (
@@ -284,44 +233,7 @@ function CommandMenu() {
</div> </div>
)} )}
</CommandEmpty> </CommandEmpty>
{Object.entries(uniqueScriptsByCategory).map(([categoryName, scripts]) => (
{results.length > 0 ? (
<CommandGroup heading="Search Results">
{results.map(script => (
<CommandItem
key={`script:${script.slug}`}
value={`${script.name} ${script.type} ${script.description || ""}`}
onSelect={() => {
setOpen(false);
router.push(`/scripts?id=${script.slug}`);
}}
tabIndex={0}
aria-label={`Open script ${script.name}`}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
setOpen(false);
router.push(`/scripts?id=${script.slug}`);
}
}}
>
<div className="flex gap-2" onClick={() => setOpen(false)}>
<Image
src={script.logo || `/${basePath}/logo.png`}
onError={e => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
unoptimized
width={16}
height={16}
alt=""
className="h-5 w-5"
/>
<span>{script.name}</span>
<span>{formattedBadge(script.type)}</span>
</div>
</CommandItem>
))}
</CommandGroup>
) : ( // When no search results, show all scripts grouped by category
Object.entries(uniqueScriptsByCategory).map(([categoryName, scripts]) => (
<CommandGroup key={`category:${categoryName}`} heading={categoryName}> <CommandGroup key={`category:${categoryName}`} heading={categoryName}>
{scripts.map(script => ( {scripts.map(script => (
<CommandItem <CommandItem
@@ -356,8 +268,7 @@ function CommandMenu() {
</CommandItem> </CommandItem>
))} ))}
</CommandGroup> </CommandGroup>
)) ))}
)}
</CommandList> </CommandList>
</CommandDialog> </CommandDialog>
</> </>

View File

@@ -13,20 +13,19 @@ setting_up_container
network_check network_check
update_os update_os
if [ -d /dev/dri ]; then echo ""
echo "" echo ""
echo "" echo -e "🤖 ${BL}Immich Machine Learning Options${CL}"
echo -e "🤖 ${BL}Immich Machine Learning Options${CL}" echo "─────────────────────────────────────────"
echo "─────────────────────────────────────────" echo "Please choose your machine-learning type:"
echo "Please choose your machine-learning type:" echo ""
echo "" echo " 1) CPU only (default)"
echo " 1) CPU only (default)" echo " 2) Intel OpenVINO (requires GPU passthrough)"
echo " 2) Intel OpenVINO (requires GPU passthrough)" echo ""
echo ""
read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE
ML_TYPE="${ML_TYPE:-1}" ML_TYPE="${ML_TYPE:-1}"
if [[ "$ML_TYPE" == "2" ]]; then if [[ "$ML_TYPE" == "2" ]]; then
msg_info "Installing OpenVINO dependencies" msg_info "Installing OpenVINO dependencies"
touch ~/.openvino touch ~/.openvino
$STD apt install -y --no-install-recommends patchelf $STD apt install -y --no-install-recommends patchelf
@@ -48,9 +47,10 @@ if [ -d /dev/dri ]; then
rm -rf "$tmp_dir" rm -rf "$tmp_dir"
dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version
msg_ok "Installed OpenVINO dependencies" msg_ok "Installed OpenVINO dependencies"
fi
fi fi
setup_uv
msg_info "Installing dependencies" msg_info "Installing dependencies"
$STD apt install --no-install-recommends -y \ $STD apt install --no-install-recommends -y \
git \ git \
@@ -144,7 +144,8 @@ msg_info "Installing packages from Debian Testing repo"
$STD apt install -t testing --no-install-recommends -yqq libmimalloc3 libde265-dev $STD apt install -t testing --no-install-recommends -yqq libmimalloc3 libde265-dev
msg_ok "Installed packages from Debian Testing repo" msg_ok "Installed packages from Debian Testing repo"
setup_uv PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')"
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
VCHORD_RELEASE="0.5.3" VCHORD_RELEASE="0.5.3"
@@ -289,9 +290,7 @@ ML_DIR="${APP_DIR}/machine-learning"
GEO_DIR="${INSTALL_DIR}/geodata" GEO_DIR="${INSTALL_DIR}/geodata"
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache} mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v2.5.3" "$SRC_DIR" fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v2.5.2" "$SRC_DIR"
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1]' ${SRC_DIR}/package.json)"
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
msg_info "Installing Immich (patience)" msg_info "Installing Immich (patience)"

View File

@@ -1,99 +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://nginxui.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 \
nginx \
logrotate
msg_ok "Installed Dependencies"
fetch_and_deploy_gh_release "nginx-ui" "0xJacky/nginx-ui" "prebuild" "latest" "/opt/nginx-ui" "nginx-ui-linux-64.tar.gz"
msg_info "Installing Nginx UI"
cp /opt/nginx-ui/nginx-ui /usr/local/bin/nginx-ui
chmod +x /usr/local/bin/nginx-ui
rm -rf /opt/nginx-ui
msg_ok "Installed Nginx UI"
msg_info "Configuring Nginx UI"
mkdir -p /usr/local/etc/nginx-ui
cat <<EOF >/usr/local/etc/nginx-ui/app.ini
[server]
HttpHost = 0.0.0.0
HttpPort = 9000
RunMode = release
JwtSecret = $(openssl rand -hex 32)
[nginx]
AccessLogPath = /var/log/nginx/access.log
ErrorLogPath = /var/log/nginx/error.log
ConfigDir = /etc/nginx
PIDPath = /run/nginx.pid
TestConfigCmd = nginx -t
ReloadCmd = nginx -s reload
RestartCmd = systemctl restart nginx
[app]
PageSize = 10
[cert]
Email =
CADir =
RenewalInterval = 7
RecursiveNameservers =
EOF
msg_ok "Configured Nginx UI"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/nginx-ui.service
[Unit]
Description=Another WebUI for Nginx
Documentation=https://nginxui.com
After=network.target nginx.service
[Service]
Type=simple
ExecStart=/usr/local/bin/nginx-ui --config /usr/local/etc/nginx-ui/app.ini
RuntimeDirectory=nginx-ui
WorkingDirectory=/var/run/nginx-ui
Restart=on-failure
TimeoutStopSec=5
KillMode=mixed
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
msg_ok "Created Service"
msg_info "Creating Initial Admin User"
systemctl start nginx-ui
sleep 3
systemctl stop nginx-ui
sleep 1
/usr/local/bin/nginx-ui reset-password --config /usr/local/etc/nginx-ui/app.ini &>/tmp/nginx-ui-reset.log || true
ADMIN_PASS=$(grep -oP 'Password: \K\S+' /tmp/nginx-ui-reset.log || echo "admin")
echo -e "Nginx-UI Credentials\nUsername: admin\nPassword: $ADMIN_PASS" >~/nginx-ui.creds
rm -f /tmp/nginx-ui-reset.log
msg_ok "Created Initial Admin User"
msg_info "Starting Service"
systemctl enable -q --now nginx-ui
rm -rf /etc/nginx/sites-enabled/default
msg_ok "Started Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -1,213 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://opencloud.eu
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
MAX_ATTEMPTS=3
servers=("opencloud" "collabora" "wopi")
attempt=0
for server in "${servers[@]}"; do
until ((attempt >= MAX_ATTEMPTS)); do
attempt=$((attempt + 1))
read -rp "${TAB3}Enter the FQDN of your ${server^} server (ATTEMPT $attempt/$MAX_ATTEMPTS) (eg $server.domain.tld): " fqdn
if [[ -z "$fqdn" ]]; then
msg_warn "Domain cannot be empty!"
elif [[ "$fqdn" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
msg_warn "IP address not allowed! Please use a FQDN"
elif [[ "$fqdn" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then
export ${server^^}_FQDN="$fqdn"
attempt=0
break
else
msg_warn "Invalid domain format!"
fi
done
if ((attempt >= MAX_ATTEMPTS)); then
msg_error "No more attempts - aborting script!"
exit 1
fi
done
msg_info "Installing Collabora Online"
curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg
cat <<EOF >/etc/apt/sources.list.d/colloboraonline.sources
Types: deb
URIs: https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb
Suites: ./
Signed-By: /etc/apt/keyrings/collaboraonline-release-keyring.gpg
EOF
$STD apt-get update
$STD apt-get install -y coolwsd code-brand
systemctl stop coolwsd
mkdir -p /etc/systemd/system/coolwsd.service.d
cat <<EOF >/etc/systemd/system/coolwsd.service.d/override.conf
[Unit]
Before=opencloud-wopi.service
EOF
systemctl daemon-reload
COOLPASS="$(openssl rand -base64 36)"
$STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPASS"
echo "$COOLPASS" >~/.coolpass
msg_ok "Installed Collabora Online"
fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.1" "/usr/bin" "opencloud-*-linux-amd64"
msg_info "Configuring OpenCloud"
DATA_DIR="/var/lib/opencloud/"
CONFIG_DIR="/etc/opencloud"
ENV_FILE="${CONFIG_DIR}/opencloud.env"
mkdir -p "$DATA_DIR" "$CONFIG_DIR"/assets/apps
curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml
curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak
cat <<EOF >"$ENV_FILE"
OC_URL=https://${OPENCLOUD_FQDN}
OC_INSECURE=false
IDM_CREATE_DEMO_USERS=false
OC_LOG_LEVEL=warning
OC_CONFIG_DIR=${CONFIG_DIR}
OC_BASE_DATA_PATH=${DATA_DIR}
STORAGE_SYSTEM_OC_ROOT=${DATA_DIR}/storage/metadata
## Web
WEB_ASSET_CORE_PATH=${CONFIG_DIR}/web/assets
WEB_ASSET_APPS_PATH=${CONFIG_DIR}/web/assets/apps
WEB_UI_CONFIG_FILE=${CONFIG_DIR}/web/config.json
# WEB_ASSET_THEMES_PATH=${CONFIG_DIR}/web/assets/themes
# WEB_UI_THEME_PATH=
## Frontend
FRONTEND_DISABLE_RADICALE=true
FRONTEND_GROUPWARE_ENABLED=false
GRAPH_INCLUDE_OCM_SHAREES=true
## Proxy
PROXY_TLS=false
PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml
## Collaboration - requires VALID TLS
COLLABORA_DOMAIN=${COLLABORA_FQDN}
COLLABORATION_APP_NAME="CollaboraOnline"
COLLABORATION_APP_PRODUCT="Collabora"
COLLABORATION_APP_ADDR=https://${COLLABORA_FQDN}
COLLABORATION_APP_INSECURE=false
COLLABORATION_HTTP_ADDR=0.0.0.0:9300
COLLABORATION_WOPI_SRC=https://${WOPI_FQDN}
COLLABORATION_JWT_SECRET=
## Notifications - Email settings
# NOTIFICATIONS_SMTP_HOST=
# NOTIFICATIONS_SMTP_PORT=
# NOTIFICATIONS_SMTP_SENDER=
# NOTIFICATIONS_SMTP_USERNAME=
# NOTIFICATIONS_SMTP_PASSWORD=
# NOTIFICATIONS_SMTP_AUTHENTICATION=login
## Encryption method. Possible values are 'starttls', 'ssltls' and 'none'
# NOTIFICATIONS_SMTP_ENCRYPTION=starttls
## Allow insecure connections. Defaults to false.
# NOTIFICATIONS_SMTP_INSECURE=false
## Start additional services at runtime
## Examples: notifications, antivirus etc.
## Do not uncomment unless configured above.
# OC_ADD_RUN_SERVICES="notifications"
## OpenID - via web browser
## uncomment for OpenID in general
# OC_EXCLUDE_RUN_SERVICES=idp
# OC_OIDC_ISSUER=<your auth URL>
# IDP_DOMAIN=<your auth URL>
# PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD=none
# PROXY_OIDC_REWRITE_WELLKNOWN=true
# PROXY_USER_OIDC_CLAIM=preferred_username
# PROXY_USER_CS3_CLAIM=username
## automatically create accounts
# PROXY_AUTOPROVISION_ACCOUNTS=true
# WEB_OIDC_SCOPE=openid profile email groups
# GRAPH_ASSIGN_DEFAULT_USER_ROLE=false
#
## uncomment below if using PocketID
# WEB_OIDC_CLIENT_ID=<generated in PocketID>
# WEB_OIDC_METADATA_URL=<your auth URL>/.well-known/openid-configuration
## Full Text Search - Apache Tika
## Requires a separate install of Tika - see https://community-scripts.github.io/ProxmoxVE/scripts?id=apache-tika
# SEARCH_EXTRACTOR_TYPE=tika
# FRONTEND_FULL_TEXT_SEARCH_ENABLED=true
# SEARCH_EXTRACTOR_TIKA_TIKA_URL=<your-tika-url>
## External storage test - Only NFS v4.2+ is supported
## User files
# STORAGE_USERS_POSIX_ROOT=<path-to-your-bind_mount>
EOF
cat <<EOF >/etc/systemd/system/opencloud.service
[Unit]
Description=OpenCloud server
After=network-online.target
[Service]
Type=simple
User=opencloud
Group=opencloud
EnvironmentFile=${ENV_FILE}
ExecStart=/usr/bin/opencloud server
Restart=always
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/opencloud-wopi.service
[Unit]
Description=OpenCloud WOPI Server
Wants=coolwsd.service
After=opencloud.service coolwsd.service
[Service]
Type=simple
User=opencloud
Group=opencloud
EnvironmentFile=${ENV_FILE}
ExecStartPre=/bin/sleep 10
ExecStart=/usr/bin/opencloud collaboration server
Restart=always
KillSignal=SIGKILL
KillMode=mixed
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target
EOF
$STD sudo -u cool coolconfig set ssl.enable false
$STD sudo -u cool coolconfig set ssl.termination true
$STD sudo -u cool coolconfig set ssl.ssl_verification true
sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OPENCLOUD_FQDN}</content_security_policy>|" /etc/coolwsd/coolwsd.xml
useradd -r -M -s /usr/sbin/nologin opencloud
chown -R opencloud:opencloud "$CONFIG_DIR" "$DATA_DIR"
sudo -u opencloud opencloud init --config-path "$CONFIG_DIR" --insecure no
OPENCLOUD_SECRET="$(sed -n '/jwt/p' "$CONFIG_DIR"/opencloud.yaml | awk '{print $2}')"
sed -i "s/JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE"
msg_ok "Configured OpenCloud"
msg_info "Starting services"
systemctl enable -q --now coolwsd opencloud
sleep 5
systemctl enable -q --now opencloud-wopi
msg_ok "Started services"
motd_ssh
customize
cleanup_lxc

View File

@@ -23,7 +23,7 @@ msg_ok "Installed Dependencies"
PG_VERSION=17 setup_postgresql PG_VERSION=17 setup_postgresql
NODE_VERSION="24" setup_nodejs NODE_VERSION="24" setup_nodejs
PG_DB_NAME="scanopy_db" PG_DB_USER="scanopy" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db PG_DB_NAME="scanopy_db" PG_DB_USER="scanopy" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
fetch_and_deploy_gh_release "Scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy" fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')" TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
@@ -35,11 +35,11 @@ $STD npm ci --no-fund --no-audit
$STD npm run build $STD npm run build
msg_ok "Created frontend UI" msg_ok "Created frontend UI"
msg_info "Building Scanopy Server (patience)" msg_info "Building scanopy-server (patience)"
cd /opt/scanopy/backend cd /opt/scanopy/backend
$STD cargo build --release --bin server $STD cargo build --release --bin server
mv ./target/release/server /usr/bin/scanopy-server mv ./target/release/server /usr/bin/scanopy-server
msg_ok "Built Scanopy Server" msg_ok "Built scanopy-server"
msg_info "Configuring server for first-run" msg_info "Configuring server for first-run"
cat <<EOF >/opt/scanopy/.env cat <<EOF >/opt/scanopy/.env

View File

@@ -14,32 +14,24 @@ network_check
update_os update_os
msg_info "Installing Dependencies" msg_info "Installing Dependencies"
$STD apt install -y coreutils $STD apt install -y \
coreutils
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
msg_info "Setting up SQL Server 2022 Repository" msg_info "Setup SQL Server 2022"
setup_deb822_repo \ curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc >/dev/null
"mssql-server-2022" \ curl -fsSL https://packages.microsoft.com/config/ubuntu/22.04/mssql-server-2022.list | tee /etc/apt/sources.list.d/mssql-server-2022.list >/dev/null
"https://packages.microsoft.com/keys/microsoft.asc" \ $STD apt-get update -y
"https://packages.microsoft.com/ubuntu/22.04/mssql-server-2022" \ $STD apt-get install -y mssql-server
"jammy" \ msg_ok "Setup Server 2022"
"main"
msg_ok "Repository configured"
msg_info "Installing SQL Server 2022"
$STD apt install -y mssql-server
msg_ok "Installed SQL Server 2022"
msg_info "Installing SQL Server Tools" msg_info "Installing SQL Server Tools"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
export ACCEPT_EULA=Y export ACCEPT_EULA=Y
setup_deb822_repo \ curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc >/dev/null
"mssql-release" \ curl -fsSL https://packages.microsoft.com/config/ubuntu/22.04/prod.list | tee /etc/apt/sources.list.d/mssql-release.list >/dev/null
"https://packages.microsoft.com/keys/microsoft.asc" \ $STD apt-get update
"https://packages.microsoft.com/ubuntu/22.04/prod" \ $STD apt-get install -y -qq \
"jammy" \
"main"
$STD apt-get install -y \
mssql-tools18 \ mssql-tools18 \
unixodbc-dev unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >>~/.bash_profile echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >>~/.bash_profile
@@ -57,11 +49,6 @@ msg_info "Start Service"
systemctl enable -q --now mssql-server systemctl enable -q --now mssql-server
msg_ok "Service started" msg_ok "Service started"
msg_info "Cleaning up"
rm -f /etc/profile.d/debuginfod.sh
rm -f /etc/profile.d/debuginfod.csh
msg_ok "Cleaned up"
motd_ssh motd_ssh
customize customize
cleanup_lxc cleanup_lxc

View File

@@ -1,66 +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://www.microsoft.com/en-us/sql-server/sql-server-2025
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 coreutils
msg_ok "Installed Dependencies"
msg_info "Setting up SQL Server 2025 Repository"
setup_deb822_repo \
"mssql-server-2025" \
"https://packages.microsoft.com/keys/microsoft.asc" \
"https://packages.microsoft.com/ubuntu/24.04/mssql-server-2025" \
"noble" \
"main"
msg_ok "Repository configured"
msg_info "Installing SQL Server 2025"
$STD apt install -y mssql-server
msg_ok "Installed SQL Server 2025"
msg_info "Installing SQL Server Tools"
export DEBIAN_FRONTEND=noninteractive
export ACCEPT_EULA=Y
setup_deb822_repo \
"mssql-release" \
"https://packages.microsoft.com/keys/microsoft.asc" \
"https://packages.microsoft.com/ubuntu/24.04/prod" \
"noble" \
"main"
$STD apt-get install -y \
mssql-tools18 \
unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >>~/.bash_profile
source ~/.bash_profile
msg_ok "Installed SQL Server Tools"
read -r -p "${TAB3}Do you want to run the SQL Server setup now? (Later is also possible) <y/N>" prompt
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
/opt/mssql/bin/mssql-conf setup
else
msg_ok "Skipping SQL Server setup. You can run it later with '/opt/mssql/bin/mssql-conf setup'."
fi
msg_info "Starting SQL Server Service"
systemctl enable -q --now mssql-server
msg_ok "Service started"
msg_info "Cleaning up"
rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh
msg_ok "Cleaned up"
motd_ssh
customize
cleanup_lxc

View File

@@ -14,10 +14,10 @@ network_check
update_os update_os
setup_go setup_go
NODE_VERSION="22" setup_nodejs setup_nodejs
fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" "latest" "/opt/wanderer/source/search" fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" "latest" "/opt/wanderer/source/search"
mkdir -p /opt/wanderer/{source,data/pb_data,data/meili_data} mkdir -p /opt/wanderer/{source,data/pb_data,data/meili_data}
fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" fetch_and_deploy_gh_release "wanderer" "Flomp/wanderer" "tarball" "latest" "/opt/wanderer/source"
msg_info "Installing wanderer (patience)" msg_info "Installing wanderer (patience)"
cd /opt/wanderer/source/db cd /opt/wanderer/source/db

View File

@@ -28,210 +28,13 @@
# ============================================================================== # ==============================================================================
# These can be overridden before sourcing this library # These can be overridden before sourcing this library
# Disable 'unbound variable' errors for this library (restored at end)
_OLD_SET_STATE=$(set +o | grep -E 'set -(e|u|o)')
set +u
CLOUDINIT_DEFAULT_USER="${CLOUDINIT_DEFAULT_USER:-root}" CLOUDINIT_DEFAULT_USER="${CLOUDINIT_DEFAULT_USER:-root}"
CLOUDINIT_DNS_SERVERS="${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}" CLOUDINIT_DNS_SERVERS="${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}"
CLOUDINIT_SEARCH_DOMAIN="${CLOUDINIT_SEARCH_DOMAIN:-local}" CLOUDINIT_SEARCH_DOMAIN="${CLOUDINIT_SEARCH_DOMAIN:-local}"
CLOUDINIT_SSH_KEYS="${CLOUDINIT_SSH_KEYS:-}" # Empty by default - user must explicitly provide keys CLOUDINIT_SSH_KEYS="${CLOUDINIT_SSH_KEYS:-/root/.ssh/authorized_keys}"
# ============================================================================== # ==============================================================================
# SECTION 2: SSH KEY DISCOVERY AND SELECTION # SECTION 2: HELPER FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# _ci_ssh_extract_keys_from_file - Extracts valid SSH public keys from a file
# ------------------------------------------------------------------------------
function _ci_ssh_extract_keys_from_file() {
local file="$1"
[[ -f "$file" && -r "$file" ]] || return 0
grep -E '^(ssh-(rsa|ed25519|dss|ecdsa)|ecdsa-sha2-)' "$file" 2>/dev/null || true
}
# ------------------------------------------------------------------------------
# _ci_ssh_discover_files - Scans standard paths for SSH keys
# ------------------------------------------------------------------------------
function _ci_ssh_discover_files() {
local -a cand=()
shopt -s nullglob
cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2)
cand+=(/root/.ssh/*.pub)
cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*)
shopt -u nullglob
printf '%s\0' "${cand[@]}"
}
# ------------------------------------------------------------------------------
# _ci_ssh_build_choices - Builds whiptail checklist from SSH key files
#
# Sets: CI_SSH_CHOICES (array), CI_SSH_COUNT (int), CI_SSH_MAPFILE (path)
# ------------------------------------------------------------------------------
function _ci_ssh_build_choices() {
local -a files=("$@")
CI_SSH_CHOICES=()
CI_SSH_COUNT=0
CI_SSH_MAPFILE="$(mktemp)"
local id key typ fp cmt base
for f in "${files[@]}"; do
[[ -f "$f" && -r "$f" ]] || continue
base="$(basename -- "$f")"
# Skip known_hosts and private keys
case "$base" in
known_hosts | known_hosts.* | config) continue ;;
id_*) [[ "$f" != *.pub ]] && continue ;;
esac
while IFS= read -r key; do
[[ -n "$key" ]] || continue
typ=""
fp=""
cmt=""
read -r _typ _b64 _cmt <<<"$key"
typ="${_typ:-key}"
cmt="${_cmt:-}"
# Get fingerprint via ssh-keygen if available
if command -v ssh-keygen >/dev/null 2>&1; then
fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')"
fi
# Shorten long comments
[[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..."
CI_SSH_COUNT=$((CI_SSH_COUNT + 1))
id="K${CI_SSH_COUNT}"
echo "${id}|${key}" >>"$CI_SSH_MAPFILE"
CI_SSH_CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }${base}" "OFF")
done < <(_ci_ssh_extract_keys_from_file "$f")
done
}
# ------------------------------------------------------------------------------
# configure_cloudinit_ssh_keys - Interactive SSH key selection for Cloud-Init
#
# Usage: configure_cloudinit_ssh_keys
# Sets: CLOUDINIT_SSH_KEYS (path to temporary file with selected keys)
# ------------------------------------------------------------------------------
function configure_cloudinit_ssh_keys() {
local backtitle="Proxmox VE Helper Scripts"
local ssh_key_mode
# Create temp file for selected keys
CLOUDINIT_SSH_KEYS_TEMP="$(mktemp)"
: >"$CLOUDINIT_SSH_KEYS_TEMP"
# Discover keys and build choices
IFS=$'\0' read -r -d '' -a _def_files < <(_ci_ssh_discover_files && printf '\0')
_ci_ssh_build_choices "${_def_files[@]}"
local default_key_count="$CI_SSH_COUNT"
if [[ "$default_key_count" -gt 0 ]]; then
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
"Provision SSH keys for Cloud-Init VM:" 14 72 4 \
"found" "Select from detected keys (${default_key_count})" \
"manual" "Paste a single public key" \
"folder" "Scan another folder (path or glob)" \
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
else
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
"No host keys detected. Choose:" 12 72 3 \
"manual" "Paste a single public key" \
"folder" "Scan another folder (path or glob)" \
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
fi
case "$ssh_key_mode" in
found)
# Show checklist with individual keys
local selection
selection=$(whiptail --backtitle "$backtitle" --title "SELECT SSH KEYS" \
--checklist "Select one or more keys to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
for tag in $selection; do
tag="${tag%\"}"
tag="${tag#\"}"
local line
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
done
local imported
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) selected${CL}"
;;
manual)
local pubkey
pubkey=$(whiptail --backtitle "$backtitle" --title "PASTE SSH PUBLIC KEY" \
--inputbox "Paste your SSH public key (ssh-rsa, ssh-ed25519, etc.):" 10 76 3>&1 1>&2 2>&3) || return 1
if [[ -n "$pubkey" ]]; then
echo "$pubkey" >"$CLOUDINIT_SSH_KEYS_TEMP"
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}1 key added manually${CL}"
else
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (empty input)${CL}"
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
return 0
fi
;;
folder)
local glob_path
glob_path=$(whiptail --backtitle "$backtitle" --title "SCAN FOLDER/GLOB" \
--inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub):" 10 72 3>&1 1>&2 2>&3) || return 1
if [[ -n "$glob_path" ]]; then
shopt -s nullglob
local -a _scan_files=($glob_path)
shopt -u nullglob
if [[ "${#_scan_files[@]}" -gt 0 ]]; then
_ci_ssh_build_choices "${_scan_files[@]}"
if [[ "$CI_SSH_COUNT" -gt 0 ]]; then
local folder_selection
folder_selection=$(whiptail --backtitle "$backtitle" --title "SELECT FOLDER KEYS" \
--checklist "Select key(s) to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
for tag in $folder_selection; do
tag="${tag%\"}"
tag="${tag#\"}"
local line
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
done
local imported
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) from folder${CL}"
else
whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60
fi
else
whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60
fi
fi
;;
none | *)
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (password auth only)${CL}"
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
return 0
;;
esac
# Cleanup mapfile
rm -f "$CI_SSH_MAPFILE" 2>/dev/null
# Set the variable for setup_cloud_init to use
if [[ -s "$CLOUDINIT_SSH_KEYS_TEMP" ]]; then
CLOUDINIT_SSH_KEYS="$CLOUDINIT_SSH_KEYS_TEMP"
else
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP"
fi
return 0
}
# ==============================================================================
# SECTION 3: HELPER FUNCTIONS
# ============================================================================== # ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -341,10 +144,9 @@ function setup_cloud_init() {
local cipassword=$(openssl rand -base64 16) local cipassword=$(openssl rand -base64 16)
qm set "$vmid" --cipassword "$cipassword" >/dev/null qm set "$vmid" --cipassword "$cipassword" >/dev/null
# Add SSH keys only if explicitly provided (not auto-imported from host) # Add SSH keys if available
if [ -n "${CLOUDINIT_SSH_KEYS:-}" ] && [ -f "$CLOUDINIT_SSH_KEYS" ]; then if [ -f "$CLOUDINIT_SSH_KEYS" ]; then
qm set "$vmid" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null 2>&1 || true qm set "$vmid" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null 2>&1 || true
_ci_msg_info "SSH keys imported from: $CLOUDINIT_SSH_KEYS"
fi fi
# Configure network # Configure network
@@ -657,11 +459,6 @@ export -f wait_for_cloud_init 2>/dev/null || true
export -f validate_ip_cidr 2>/dev/null || true export -f validate_ip_cidr 2>/dev/null || true
export -f validate_ip 2>/dev/null || true export -f validate_ip 2>/dev/null || true
# Restore previous shell options if they were saved
if [ -n "${_OLD_SET_STATE:-}" ]; then
eval "$_OLD_SET_STATE"
fi
# ============================================================================== # ==============================================================================
# SECTION 7: EXAMPLES & DOCUMENTATION # SECTION 7: EXAMPLES & DOCUMENTATION
# ============================================================================== # ==============================================================================

View File

@@ -1,264 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/alangrainger/immich-public-proxy
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)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
# Enable error handling
set -Eeuo pipefail
trap 'error_handler' ERR
# ==============================================================================
# CONFIGURATION
# ==============================================================================
APP="Immich Public Proxy"
APP_TYPE="addon"
INSTALL_PATH="/opt/immich-proxy"
CONFIG_PATH="/opt/immich-proxy/app"
DEFAULT_PORT=3000
# Initialize all core functions (colors, formatting, icons, STD mode)
load_functions
# ==============================================================================
# HEADER
# ==============================================================================
function header_info {
clear
cat <<"EOF"
____ _ __ ____
/ _/___ ___ ____ ___ (_)____/ /_ / __ \_________ _ ____ __
/ // __ `__ \/ __ `__ \/ / ___/ __ \______/ /_/ / ___/ __ \| |/_/ / / /
_/ // / / / / / / / / / / / /__/ / / /_____/ ____/ / / /_/ /> </ /_/ /
/___/_/ /_/ /_/_/ /_/ /_/_/\___/_/ /_/ /_/ /_/ \____/_/|_|\__, /
/____/
EOF
}
# ==============================================================================
# OS DETECTION
# ==============================================================================
if [[ -f "/etc/alpine-release" ]]; then
msg_error "Alpine is not supported for ${APP}. Use Debian."
exit 1
elif [[ -f "/etc/debian_version" ]]; then
OS="Debian"
SERVICE_PATH="/etc/systemd/system/immich-proxy.service"
else
echo -e "${CROSS} Unsupported OS detected. Exiting."
exit 1
fi
# ==============================================================================
# UNINSTALL
# ==============================================================================
function uninstall() {
msg_info "Uninstalling ${APP}"
systemctl disable --now immich-proxy.service &>/dev/null || true
rm -f "$SERVICE_PATH"
rm -rf "$INSTALL_PATH"
rm -f "/usr/local/bin/update_immich-public-proxy"
rm -f "$HOME/.immichpublicproxy"
msg_ok "${APP} has been uninstalled"
}
# ==============================================================================
# UPDATE
# ==============================================================================
function update() {
if check_for_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy"; then
msg_info "Stopping service"
systemctl stop immich-proxy.service &>/dev/null || true
msg_ok "Stopped service"
msg_info "Backing up configuration"
cp "$CONFIG_PATH"/.env /tmp/ipp.env.bak 2>/dev/null || true
cp "$CONFIG_PATH"/config.json /tmp/ipp.config.json.bak 2>/dev/null || true
msg_ok "Backed up configuration"
NODE_VERSION="24" setup_nodejs
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy" "tarball" "latest" "$INSTALL_PATH"
msg_info "Restoring configuration"
cp /tmp/ipp.env.bak "$CONFIG_PATH"/.env 2>/dev/null || true
cp /tmp/ipp.config.json.bak "$CONFIG_PATH"/config.json 2>/dev/null || true
rm -f /tmp/ipp.*.bak
msg_ok "Restored configuration"
msg_info "Installing dependencies"
cd "$CONFIG_PATH"
$STD npm install
msg_ok "Installed dependencies"
msg_info "Building ${APP}"
$STD npm run build
msg_ok "Built ${APP}"
msg_info "Starting service"
systemctl start immich-proxy
msg_ok "Started service"
msg_ok "Updated successfully"
exit
fi
}
# ==============================================================================
# INSTALL
# ==============================================================================
function install() {
NODE_VERSION="24" setup_nodejs
# Force fresh download by removing version cache
rm -f "$HOME/.immichpublicproxy"
fetch_and_deploy_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy" "tarball" "latest" "$INSTALL_PATH"
msg_info "Installing dependencies"
cd "$CONFIG_PATH"
$STD npm install
msg_ok "Installed dependencies"
msg_info "Building ${APP}"
$STD npm run build
msg_ok "Built ${APP}"
MAX_ATTEMPTS=3
attempt=0
while true; do
attempt=$((attempt + 1))
read -rp "${TAB3}Enter your LOCAL Immich IP or domain (ex. 192.168.1.100 or immich.local.lan): " DOMAIN
if [[ -z "$DOMAIN" ]]; then
if ((attempt >= MAX_ATTEMPTS)); then
DOMAIN="${LOCAL_IP:-localhost}"
msg_warn "Using fallback: $DOMAIN"
break
fi
msg_warn "Domain cannot be empty! (Attempt $attempt/$MAX_ATTEMPTS)"
elif [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
valid_ip=true
IFS='.' read -ra octets <<<"$DOMAIN"
for octet in "${octets[@]}"; do
if ((octet > 255)); then
valid_ip=false
break
fi
done
if $valid_ip; then
break
else
msg_warn "Invalid IP address!"
fi
elif [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ || "$DOMAIN" == "localhost" ]]; then
break
else
msg_warn "Invalid domain format!"
fi
done
msg_info "Creating configuration"
cat <<EOF >"$CONFIG_PATH"/.env
NODE_ENV=production
IMMICH_URL=http://${DOMAIN}:2283
EOF
chmod 600 "$CONFIG_PATH"/.env
msg_ok "Created configuration"
msg_info "Creating service"
cat <<EOF >"$SERVICE_PATH"
[Unit]
Description=Immich Public Proxy
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=${INSTALL_PATH}
EnvironmentFile=${CONFIG_PATH}/.env
ExecStart=/usr/bin/node ${INSTALL_PATH}/app/server.js
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now immich-proxy
msg_ok "Created and started service"
# Create update script (simple wrapper that calls this addon with type=update)
msg_info "Creating update script"
cat <<'UPDATEEOF' >/usr/local/bin/update_immich-public-proxy
#!/usr/bin/env bash
# Immich Public Proxy Update Script
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/immich-public-proxy.sh)"
UPDATEEOF
chmod +x /usr/local/bin/update_immich-public-proxy
msg_ok "Created update script (/usr/local/bin/update_immich-public-proxy)"
echo ""
msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
echo ""
msg_warn "Additional configuration is available at '/opt/immich-proxy/app/config.json'"
}
# ==============================================================================
# MAIN
# ==============================================================================
# Handle type=update (called from update script)
if [[ "${type:-}" == "update" ]]; then
header_info
if [[ -d "$INSTALL_PATH" && -f "$SERVICE_PATH" ]]; then
update
else
msg_error "${APP} is not installed. Nothing to update."
exit 1
fi
exit 0
fi
header_info
get_lxc_ip
# Check if already installed
if [[ -d "$INSTALL_PATH" && -f "$SERVICE_PATH" ]]; then
msg_warn "${APP} is already installed."
echo ""
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): "
read -r update_prompt
if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then
update
exit 0
fi
msg_warn "No action selected. Exiting."
exit 0
fi
# Fresh installation
msg_warn "${APP} is not installed."
echo ""
echo -e "${TAB}${INFO} This will install:"
echo -e "${TAB} - Node.js 24"
echo -e "${TAB} - Immich Public Proxy"
echo ""
echo -n "${TAB}Install ${APP}? (y/N): "
read -r install_prompt
if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then
install
else
msg_warn "Installation cancelled. Exiting."
exit 0
fi

View File

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

471
tools/pve/update-apps.sh Normal file
View File

@@ -0,0 +1,471 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: BvdBerg01 | Co-Author: remz1337
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/core.func)
# =============================================================================
# CONFIGURATION VARIABLES
# Set these variables to skip interactive prompts (Whiptail dialogs)
# =============================================================================
# var_backup: Enable/disable backup before update
# Options: "yes" | "no" | "" (empty = interactive prompt)
var_backup="${var_backup:-}"
# var_backup_storage: Storage location for backups (only used if var_backup=yes)
# Options: Storage name from /etc/pve/storage.cfg (e.g., "local", "nas-backup")
# Leave empty for interactive selection
var_backup_storage="${var_backup_storage:-}"
# var_container: Which containers to update
# Options:
# - "all" : All containers with community-scripts tags
# - "all_running" : Only running containers with community-scripts tags
# - "all_stopped" : Only stopped containers with community-scripts tags
# - "101,102,109" : Comma-separated list of specific container IDs
# - "" : Interactive selection via Whiptail
var_container="${var_container:-}"
# var_unattended: Run updates without user interaction inside containers
# Options: "yes" | "no" | "" (empty = interactive prompt)
var_unattended="${var_unattended:-}"
# var_skip_confirm: Skip initial confirmation dialog
# Options: "yes" | "no" (default: no)
var_skip_confirm="${var_skip_confirm:-no}"
# var_auto_reboot: Automatically reboot containers that require it after update
# Options: "yes" | "no" | "" (empty = interactive prompt)
var_auto_reboot="${var_auto_reboot:-}"
# var_tags: Optionally override the tags used for auto-detection
# Options: "community-script|proxmox-helper-scripts" (default)
var_tags="${var_tags:-community-script|proxmox-helper-scripts}"
# =============================================================================
# JSON CONFIG EXPORT
# Run with --export-config to output current configuration as JSON
# =============================================================================
function export_config_json() {
cat <<EOF
{
"var_backup": "${var_backup}",
"var_backup_storage": "${var_backup_storage}",
"var_container": "${var_container}",
"var_unattended": "${var_unattended}",
"var_skip_confirm": "${var_skip_confirm}",
"var_auto_reboot": "${var_auto_reboot}",
"var_tags": "${var_tags}"
}
EOF
}
function print_usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Update LXC containers created with community-scripts.
Options:
--help Show this help message
--export-config Export current configuration as JSON
Environment Variables:
var_backup Enable backup before update (yes/no)
var_backup_storage Storage location for backups
var_container Container selection (all/all_running/all_stopped/101,102,...)
var_unattended Run updates unattended (yes/no)
var_skip_confirm Skip initial confirmation (yes/no)
var_auto_reboot Auto-reboot containers if required (yes/no)
var_tags Optionally override auto-detection tags ("prod|smb|community-script")
Examples:
# Run interactively
$(basename "$0")
# Update all running containers unattended with backup
var_backup=yes var_backup_storage=local var_container=all_running var_unattended=yes var_skip_confirm=yes $(basename "$0")
# Update specific containers without backup
var_backup=no var_container=101,102,105 var_unattended=yes var_skip_confirm=yes $(basename "$0")
# Export current configuration
$(basename "$0") --export-config
EOF
}
# Handle command line arguments
case "${1:-}" in
--help|-h)
print_usage
exit 0
;;
--export-config)
export_config_json
exit 0
;;
esac
# =============================================================================
function header_info {
clear
cat <<"EOF"
__ _ ________ __ __ __ __
/ / | |/ / ____/ / / / /___ ____/ /___ _/ /____
/ / | / / / / / / __ \/ __ / __ `/ __/ _ \
/ /___/ / /___ / /_/ / /_/ / /_/ / /_/ / /_/ __/
/_____/_/|_\____/ \____/ .___/\__,_/\__,_/\__/\___/
/_/
EOF
}
function detect_service() {
pushd $(mktemp -d) >/dev/null
pct pull "$1" /usr/bin/update update 2>/dev/null
service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g')
popd >/dev/null
}
function backup_container() {
msg_info "Creating backup for container $1"
vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" >/dev/null 2>&1
status=$?
if [ $status -eq 0 ]; then
msg_ok "Backup created"
else
msg_error "Backup failed for container $1"
exit 1
fi
}
function get_backup_storages() {
STORAGES=$(awk '
/^[a-z]+:/ {
if (name != "") {
if (has_backup || (!has_content && type == "dir")) print name
}
split($0, a, ":")
type = a[1]
name = a[2]
sub(/^ +/, "", name)
has_content = 0
has_backup = 0
}
/^ +content/ {
has_content = 1
if ($0 ~ /backup/) has_backup = 1
}
END {
if (name != "") {
if (has_backup || (!has_content && type == "dir")) print name
}
}
' /etc/pve/storage.cfg)
}
header_info
# Skip confirmation if var_skip_confirm is set to yes
if [[ "$var_skip_confirm" != "yes" ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit
fi
tags_formatted="${var_tags//|/, }"
msg_info "Loading all possible LXC containers from Proxmox VE with tags: ${tags_formatted}. This may take a few seconds..."
NODE=$(hostname)
containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}')
if [ -z "$containers" ]; then
whiptail --title "LXC Container Update" --msgbox "No LXC containers available!" 10 60
exit 1
fi
menu_items=()
FORMAT="%-10s %-15s %-10s"
TAGS="${var_tags:-community-script|proxmox-helper-scripts}"
while read -r container; do
container_id=$(echo $container | awk '{print $1}')
container_name=$(echo $container | awk '{print $2}')
container_status=$(echo $container | awk '{print $3}')
formatted_line=$(printf "$FORMAT" "$container_name" "$container_status")
if pct config "$container_id" | grep -qE "[^-][; ](${TAGS}).*"; then
menu_items+=("$container_id" "$formatted_line" "OFF")
fi
done <<<"$containers"
msg_ok "Loaded ${#menu_items[@]} containers"
# Determine container selection based on var_container
if [[ -n "$var_container" ]]; then
case "$var_container" in
all)
# Select all containers with matching tags
CHOICE=""
for ((i=0; i<${#menu_items[@]}; i+=3)); do
CHOICE="$CHOICE ${menu_items[$i]}"
done
CHOICE=$(echo "$CHOICE" | xargs)
;;
all_running)
# Select only running containers with matching tags
CHOICE=""
for ((i=0; i<${#menu_items[@]}; i+=3)); do
cid="${menu_items[$i]}"
if pct status "$cid" 2>/dev/null | grep -q "running"; then
CHOICE="$CHOICE $cid"
fi
done
CHOICE=$(echo "$CHOICE" | xargs)
;;
all_stopped)
# Select only stopped containers with matching tags
CHOICE=""
for ((i=0; i<${#menu_items[@]}; i+=3)); do
cid="${menu_items[$i]}"
if pct status "$cid" 2>/dev/null | grep -q "stopped"; then
CHOICE="$CHOICE $cid"
fi
done
CHOICE=$(echo "$CHOICE" | xargs)
;;
*)
# Assume comma-separated list of container IDs
CHOICE=$(echo "$var_container" | tr ',' ' ')
;;
esac
if [[ -z "$CHOICE" ]]; then
msg_error "No containers matched the selection criteria: $var_container ${var_tags:-community-script|proxmox-helper-scripts}"
exit 1
fi
msg_ok "Selected containers: $CHOICE"
else
CHOICE=$(whiptail --title "LXC Container Update" \
--checklist "Select LXC containers to update:" 25 60 13 \
"${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"')
if [ -z "$CHOICE" ]; then
whiptail --title "LXC Container Update" \
--msgbox "No containers selected!" 10 60
exit 1
fi
fi
header_info
# Determine backup choice based on var_backup
if [[ -n "$var_backup" ]]; then
BACKUP_CHOICE="$var_backup"
else
BACKUP_CHOICE="no"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then
BACKUP_CHOICE="yes"
fi
fi
# Determine unattended update based on var_unattended
if [[ -n "$var_unattended" ]]; then
UNATTENDED_UPDATE="$var_unattended"
else
UNATTENDED_UPDATE="no"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then
UNATTENDED_UPDATE="yes"
fi
fi
if [ "$BACKUP_CHOICE" == "yes" ]; then
get_backup_storages
if [ -z "$STORAGES" ]; then
msg_error "No storage with 'backup' support found!"
exit 1
fi
# Determine storage based on var_backup_storage
if [[ -n "$var_backup_storage" ]]; then
# Validate that the specified storage exists and supports backups
if echo "$STORAGES" | grep -qw "$var_backup_storage"; then
STORAGE_CHOICE="$var_backup_storage"
msg_ok "Using backup storage: $STORAGE_CHOICE"
else
msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups!"
msg_info "Available storages: $(echo $STORAGES | tr '\n' ' ')"
exit 1
fi
else
MENU_ITEMS=()
for STORAGE in $STORAGES; do
MENU_ITEMS+=("$STORAGE" "")
done
STORAGE_CHOICE=$(whiptail --title "Select storage device" --menu "Select a storage device (Only storage devices with 'backup' support are listed):" 15 50 5 "${MENU_ITEMS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$STORAGE_CHOICE" ]; then
msg_error "No storage selected!"
exit 1
fi
fi
fi
UPDATE_CMD="update;"
if [ "$UNATTENDED_UPDATE" == "yes" ]; then
UPDATE_CMD="export PHS_SILENT=1;update;"
fi
containers_needing_reboot=()
for container in $CHOICE; do
echo -e "${BL}[INFO]${CL} Updating container $container"
if [ "$BACKUP_CHOICE" == "yes" ]; then
backup_container $container
fi
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
status=$(pct status $container)
template=$(pct config $container | grep -q "template:" && echo "true" || echo "false")
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then
echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n"
pct start $container
echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n"
sleep 5
fi
#1) Detect service using the service name in the update command
detect_service $container
#1.1) If update script not detected, return
if [ -z "${service}" ]; then
echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container"
continue
else
echo -e "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}"
fi
#2) Extract service build/update resource requirements from config/installation file
script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${service}.sh)
#2.1) Check if the script downloaded successfully
if [ $? -ne 0 ]; then
echo -e "${RD}[ERROR]${CL} Issue while downloading install script."
echo -e "${YW}[WARN]${CL} Unable to assess build resource requirements. Proceeding with current resources."
fi
config=$(pct config "$container")
build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g')
build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g')
run_cpu=$(echo "$script" | { grep -m 1 "pct set \$CTID -cores" || test $? = 1; } | sed 's|.*cores ||g')
run_ram=$(echo "$script" | { grep -m 1 "pct set \$CTID -memory" || test $? = 1; } | sed 's|.*memory ||g')
current_cpu=$(echo "$config" | grep -m 1 "cores:" | sed 's|cores: ||g')
current_ram=$(echo "$config" | grep -m 1 "memory:" | sed 's|memory: ||g')
#Test if all values are valid (>0)
if [ -z "${run_cpu}" ] || [ "$run_cpu" -le 0 ]; then
#echo "No valid value found for run_cpu. Assuming same as current configuration."
run_cpu=$current_cpu
fi
if [ -z "${run_ram}" ] || [ "$run_ram" -le 0 ]; then
#echo "No valid value found for run_ram. Assuming same as current configuration."
run_ram=$current_ram
fi
if [ -z "${build_cpu}" ] || [ "$build_cpu" -le 0 ]; then
#echo "No valid value found for build_cpu. Assuming same as current configuration."
build_cpu=$current_cpu
fi
if [ -z "${build_ram}" ] || [ "$build_ram" -le 0 ]; then
#echo "No valid value found for build_ram. Assuming same as current configuration."
build_ram=$current_ram
fi
UPDATE_BUILD_RESOURCES=0
if [ "$build_cpu" -gt "$run_cpu" ] || [ "$build_ram" -gt "$run_ram" ]; then
UPDATE_BUILD_RESOURCES=1
fi
#3) if build resources are different than run resources, then:
if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then
pct set "$container" --cores "$build_cpu" --memory "$build_ram"
fi
#4) Update service, using the update command
case "$os" in
alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;;
archlinux) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
fedora | rocky | centos | alma) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
ubuntu | debian | devuan) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
opensuse) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
esac
exit_code=$?
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then
echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n"
pct shutdown $container &
fi
#5) if build resources are different than run resources, then:
if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then
pct set "$container" --cores "$run_cpu" --memory "$run_ram"
fi
if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then
# Get the container's hostname and add it to the list
container_hostname=$(pct exec "$container" hostname)
containers_needing_reboot+=("$container ($container_hostname)")
fi
if [ $exit_code -eq 0 ]; then
msg_ok "Updated container $container"
elif [ "$BACKUP_CHOICE" == "yes" ]; then
msg_info "Restoring LXC from backup"
pct stop $container
LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}')
pct restore $container /var/lib/vz/dump/vzdump-lxc-${container}-*.tar.zst --storage $LXC_STORAGE --force >/dev/null 2>&1
pct start $container
restorestatus=$?
if [ $restorestatus -eq 0 ]; then
msg_ok "Restored LXC from backup"
else
msg_error "Restored LXC from backup failed"
exit 1
fi
else
msg_error "Update failed for container $container. Exiting"
exit 1
fi
done
wait
header_info
echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n"
if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then
echo -e "${RD}The following containers require a reboot:${CL}"
for container_name in "${containers_needing_reboot[@]}"; do
echo "$container_name"
done
# Determine reboot choice based on var_auto_reboot
REBOOT_CHOICE="no"
if [[ -n "$var_auto_reboot" ]]; then
REBOOT_CHOICE="$var_auto_reboot"
else
echo -ne "${INFO} Do you wish to reboot these containers? <yes/No> "
read -r prompt
if [[ ${prompt,,} =~ ^(yes)$ ]]; then
REBOOT_CHOICE="yes"
fi
fi
if [[ "$REBOOT_CHOICE" == "yes" ]]; then
echo -e "${CROSS}${HOLD} ${YWB}Rebooting containers.${CL}"
for container_name in "${containers_needing_reboot[@]}"; do
container=$(echo $container_name | cut -d " " -f 1)
pct reboot ${container}
done
fi
fi

View File

@@ -1,45 +1,71 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG # Copyright (c) 2021-2026 community-scripts ORG
# Author: thost96 (thost96) | michelroegl-brunner | MickLesk # Author: thost96 (thost96) | Co-Author: michelroegl-brunner
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# ============================================================================== source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
# Docker VM - Creates a Docker-ready Virtual Machine
# ==============================================================================
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/api.func) 2>/dev/null function header_info() {
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/vm-core.func) 2>/dev/null clear
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/cloud-init.func) 2>/dev/null || true cat <<"EOF"
load_functions ____ __ _ ____ ___
/ __ \____ _____/ /_____ _____ | | / / |/ /
# ============================================================================== / / / / __ \/ ___/ //_/ _ \/ ___/ | | / / /|_/ /
# SCRIPT VARIABLES / /_/ / /_/ / /__/ ,< / __/ / | |/ / / / /
# ============================================================================== /_____/\____/\___/_/|_|\___/_/ |___/_/ /_/
APP="Docker"
APP_TYPE="vm"
NSAPP="docker-vm"
var_os="debian"
var_version="13"
EOF
}
header_info
echo -e "\n Loading..."
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD="" METHOD=""
NSAPP="docker-vm"
var_os="debian"
var_version="12"
DISK_SIZE="10G" DISK_SIZE="10G"
USE_CLOUD_INIT="no"
OS_TYPE=""
OS_VERSION=""
THIN="discard=on,ssd=1,"
# ============================================================================== YW=$(echo "\033[33m")
# ERROR HANDLING & CLEANUP BL=$(echo "\033[36m")
# ============================================================================== RD=$(echo "\033[01;31m")
BGN=$(echo "\033[4;92m")
GN=$(echo "\033[1;92m")
DGN=$(echo "\033[32m")
CL=$(echo "\033[m")
CL=$(echo "\033[m")
BOLD=$(echo "\033[1m")
BFR="\\r\\033[K"
HOLD=" "
TAB=" "
CM="${TAB}✔️${TAB}${CL}"
CROSS="${TAB}✖️${TAB}${CL}"
INFO="${TAB}💡${TAB}${CL}"
OS="${TAB}🖥️${TAB}${CL}"
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
DISKSIZE="${TAB}💾${TAB}${CL}"
CPUCORE="${TAB}🧠${TAB}${CL}"
RAMSIZE="${TAB}🛠️${TAB}${CL}"
CONTAINERID="${TAB}🆔${TAB}${CL}"
HOSTNAME="${TAB}🏠${TAB}${CL}"
BRIDGE="${TAB}🌉${TAB}${CL}"
GATEWAY="${TAB}🌐${TAB}${CL}"
DEFAULT="${TAB}⚙️${TAB}${CL}"
MACADDRESS="${TAB}🔗${TAB}${CL}"
VLANTAG="${TAB}🏷️${TAB}${CL}"
CREATING="${TAB}🚀${TAB}${CL}"
ADVANCED="${TAB}🧩${TAB}${CL}"
CLOUD="${TAB}☁️${TAB}${CL}"
THIN="discard=on,ssd=1,"
set -e set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
function error_handler() { function error_handler() {
local exit_code="$?" local exit_code="$?"
local line_number="$1" local line_number="$1"
@@ -50,96 +76,140 @@ function error_handler() {
cleanup_vmid cleanup_vmid
} }
# ============================================================================== function get_valid_nextid() {
# OS SELECTION FUNCTIONS local try_id
# ============================================================================== try_id=$(pvesh get /cluster/nextid)
function select_os() { while true; do
if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT OS" --radiolist \ if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
"Choose Operating System for Docker VM" 14 68 4 \ try_id=$((try_id + 1))
"debian13" "Debian 13 (Trixie) - Latest" ON \ continue
"debian12" "Debian 12 (Bookworm) - Stable" OFF \ fi
"ubuntu2404" "Ubuntu 24.04 LTS (Noble)" OFF \ if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
"ubuntu2204" "Ubuntu 22.04 LTS (Jammy)" OFF \ try_id=$((try_id + 1))
3>&1 1>&2 2>&3); then continue
case $OS_CHOICE in fi
debian13) break
OS_TYPE="debian" done
OS_VERSION="13" echo "$try_id"
OS_CODENAME="trixie" }
OS_DISPLAY="Debian 13 (Trixie)"
;; function cleanup_vmid() {
debian12) if qm status $VMID &>/dev/null; then
OS_TYPE="debian" qm stop $VMID &>/dev/null
OS_VERSION="12" qm destroy $VMID &>/dev/null
OS_CODENAME="bookworm"
OS_DISPLAY="Debian 12 (Bookworm)"
;;
ubuntu2404)
OS_TYPE="ubuntu"
OS_VERSION="24.04"
OS_CODENAME="noble"
OS_DISPLAY="Ubuntu 24.04 LTS"
;;
ubuntu2204)
OS_TYPE="ubuntu"
OS_VERSION="22.04"
OS_CODENAME="jammy"
OS_DISPLAY="Ubuntu 22.04 LTS"
;;
esac
echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}${OS_DISPLAY}${CL}"
else
exit_script
fi fi
} }
function select_cloud_init() { function cleanup() {
if [ "$OS_TYPE" = "ubuntu" ]; then popd >/dev/null
USE_CLOUD_INIT="yes" post_update_to_api "done" "none"
echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}yes (Ubuntu requires Cloud-Init)${CL}" rm -rf $TEMP_DIR
return }
fi
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \ TEMP_DIR=$(mktemp -d)
--yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n- User accounts and passwords\n- SSH keys\n- Network settings (DHCP/Static)\n- DNS configuration\n\nYou can also configure these settings later in Proxmox UI.\n\nNote: Debian without Cloud-Init will use nocloud image with console auto-login." 18 68); then pushd $TEMP_DIR >/dev/null
USE_CLOUD_INIT="yes" if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then
echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}yes${CL}" :
else else
USE_CLOUD_INIT="no" header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}no${CL}" fi
function msg_info() {
local msg="$1"
echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
}
function msg_ok() {
local msg="$1"
echo -e "${BFR}${CM}${GN}${msg}${CL}"
}
function msg_error() {
local msg="$1"
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
}
function check_root() {
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
clear
msg_error "Please run this script as root."
echo -e "\nExiting..."
sleep 2
exit
fi fi
} }
function get_image_url() { # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
local arch=$(dpkg --print-architecture) # Supported: Proxmox VE 8.0.x 8.9.x, 9.0 and 9.1
case $OS_TYPE in pve_check() {
debian) local PVE_VER
if [ "$USE_CLOUD_INIT" = "yes" ]; then PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-generic-${arch}.qcow2"
else # Check for Proxmox VE 8.x: allow 8.08.9
echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-nocloud-${arch}.qcow2" if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 9)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 8.0 8.9"
exit 1
fi fi
;; return 0
ubuntu) fi
echo "https://cloud-images.ubuntu.com/${OS_CODENAME}/current/${OS_CODENAME}-server-cloudimg-${arch}.img"
;; # Check for Proxmox VE 9.x: allow 9.0 and 9.1
esac if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 1)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 9.0 9.1"
exit 1
fi
return 0
fi
# All other unsupported versions
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported versions: Proxmox VE 8.0 8.x or 9.0 9.1"
exit 1
}
function arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
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
fi
}
function ssh_check() {
if command -v pveversion >/dev/null 2>&1; then
if [ -n "${SSH_CLIENT:+x}" ]; then
if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then
echo "you've been warned"
else
clear
exit
fi
fi
fi
}
function exit-script() {
clear
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
exit
} }
# ==============================================================================
# SETTINGS FUNCTIONS
# ==============================================================================
function default_settings() { function default_settings() {
select_os
select_cloud_init
VMID=$(get_valid_nextid) VMID=$(get_valid_nextid)
FORMAT="" FORMAT=",efitype=4m"
MACHINE=" -machine q35" MACHINE=""
DISK_CACHE="" DISK_CACHE=""
DISK_SIZE="10G" DISK_SIZE="10G"
HN="docker" HN="docker"
CPU_TYPE=" -cpu host" CPU_TYPE=""
CORE_COUNT="2" CORE_COUNT="2"
RAM_SIZE="4096" RAM_SIZE="4096"
BRG="vmbr0" BRG="vmbr0"
@@ -148,13 +218,12 @@ function default_settings() {
MTU="" MTU=""
START_VM="yes" START_VM="yes"
METHOD="default" METHOD="default"
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}"
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}"
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}"
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
@@ -162,22 +231,12 @@ function default_settings() {
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above settings${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above default settings${CL}"
} }
function advanced_settings() { function advanced_settings() {
select_os
select_cloud_init
# SSH Key selection for Cloud-Init VMs
if [ "$USE_CLOUD_INIT" = "yes" ]; then
configure_cloudinit_ssh_keys || true
fi
METHOD="advanced" METHOD="advanced"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
# VM ID
while true; do while true; do
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$VMID" ]; then if [ -z "$VMID" ]; then
@@ -191,29 +250,27 @@ function advanced_settings() {
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break break
else else
exit_script exit-script
fi fi
done done
# Machine Type
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
"q35" "Q35 (Modern, PCIe)" ON \ "i440fx" "Machine i440fx" ON \
"i440fx" "i440fx (Legacy, PCI)" OFF \ "q35" "Machine q35" OFF \
3>&1 1>&2 2>&3); then 3>&1 1>&2 2>&3); then
if [ $MACH = q35 ]; then if [ $MACH = q35 ]; then
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
FORMAT="" FORMAT=""
MACHINE=" -machine q35" MACHINE=" -machine q35"
else else
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx (Legacy)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
FORMAT=",efitype=4m" FORMAT=",efitype=4m"
MACHINE="" MACHINE=""
fi fi
else else
exit_script exit-script
fi fi
# Disk Size
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ')
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
@@ -223,13 +280,12 @@ function advanced_settings() {
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
else else
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
exit_script exit-script
fi fi
else else
exit_script exit-script
fi fi
# Disk Cache
if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"0" "None (Default)" ON \ "0" "None (Default)" ON \
"1" "Write Through" OFF \ "1" "Write Through" OFF \
@@ -242,25 +298,24 @@ function advanced_settings() {
DISK_CACHE="" DISK_CACHE=""
fi fi
else else
exit_script exit-script
fi fi
# Hostname
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $VM_NAME ]; then if [ -z $VM_NAME ]; then
HN="docker" HN="docker"
else
HN=$(echo ${VM_NAME,,} | tr -d ' ')
fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else else
exit_script HN=$(echo ${VM_NAME,,} | tr -d ' ')
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
fi
else
exit-script
fi fi
# CPU Model
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"1" "Host (Recommended)" ON \ "0" "KVM64 (Default)" ON \
"0" "KVM64" OFF \ "1" "Host" OFF \
3>&1 1>&2 2>&3); then 3>&1 1>&2 2>&3); then
if [ $CPU_TYPE1 = "1" ]; then if [ $CPU_TYPE1 = "1" ]; then
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
@@ -270,78 +325,80 @@ function advanced_settings() {
CPU_TYPE="" CPU_TYPE=""
fi fi
else else
exit_script exit-script
fi fi
# CPU Cores
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $CORE_COUNT ]; then if [ -z $CORE_COUNT ]; then
CORE_COUNT="2" CORE_COUNT="2"
fi
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
else else
exit_script echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
fi
else
exit-script
fi fi
# RAM Size if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $RAM_SIZE ]; then if [ -z $RAM_SIZE ]; then
RAM_SIZE="4096" RAM_SIZE="2048"
fi
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
else else
exit_script echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
fi
else
exit-script
fi fi
# Bridge
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $BRG ]; then if [ -z $BRG ]; then
BRG="vmbr0" BRG="vmbr0"
fi
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
else else
exit_script echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
fi
else
exit-script
fi fi
# MAC Address
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC1 ]; then if [ -z $MAC1 ]; then
MAC="$GEN_MAC" MAC="$GEN_MAC"
else
MAC="$MAC1"
fi
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
else else
exit_script MAC="$MAC1"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
fi
else
exit-script
fi fi
# VLAN if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan (leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $VLAN1 ]; then if [ -z $VLAN1 ]; then
VLAN1="Default" VLAN1="Default"
VLAN="" VLAN=""
else
VLAN=",tag=$VLAN1"
fi
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
else else
exit_script VLAN=",tag=$VLAN1"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
fi
else
exit-script
fi fi
# MTU
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MTU1 ]; then if [ -z $MTU1 ]; then
MTU1="Default" MTU1="Default"
MTU="" MTU=""
else
MTU=",mtu=$MTU1"
fi
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
else else
exit_script MTU=",mtu=$MTU1"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
fi
else
exit-script
fi fi
# Start VM
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
START_VM="yes" START_VM="yes"
@@ -350,7 +407,6 @@ function advanced_settings() {
START_VM="no" START_VM="no"
fi fi
# Confirm
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58); then if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58); then
echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}"
else else
@@ -371,28 +427,13 @@ function start_script() {
advanced_settings advanced_settings
fi fi
} }
# ==============================================================================
# MAIN EXECUTION
# ==============================================================================
header_info
check_root check_root
arch_check arch_check
pve_check pve_check
ssh_check
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then
:
else
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
fi
start_script start_script
post_to_api_vm post_to_api_vm
# ==============================================================================
# STORAGE SELECTION
# ==============================================================================
msg_info "Validating Storage" msg_info "Validating Storage"
while read -r line; do while read -r line; do
TAG=$(echo $line | awk '{print $1}') TAG=$(echo $line | awk '{print $1}')
@@ -405,7 +446,6 @@ while read -r line; do
fi fi
STORAGE_MENU+=("$TAG" "$ITEM" "OFF") STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1') done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then if [ -z "$VALID" ]; then
msg_error "Unable to detect a valid storage location." msg_error "Unable to detect a valid storage location."
@@ -413,8 +453,6 @@ if [ -z "$VALID" ]; then
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]} STORAGE=${STORAGE_MENU[0]}
else else
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
printf "\e[?25h"
while [ -z "${STORAGE:+x}" ]; do while [ -z "${STORAGE:+x}" ]; do
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
"Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \
@@ -424,288 +462,112 @@ else
fi fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image"
# ============================================================================== URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2"
# PREREQUISITES sleep 2
# ==============================================================================
if ! command -v virt-customize &>/dev/null; then
msg_info "Installing libguestfs-tools"
apt-get -qq update >/dev/null
apt-get -qq install libguestfs-tools lsb-release -y >/dev/null
apt-get -qq install dhcpcd-base -y >/dev/null 2>&1 || true
msg_ok "Installed libguestfs-tools"
fi
# ==============================================================================
# IMAGE DOWNLOAD
# ==============================================================================
msg_info "Retrieving the URL for the ${OS_DISPLAY} Qcow2 Disk Image"
URL=$(get_image_url)
CACHE_DIR="/var/lib/vz/template/cache"
CACHE_FILE="$CACHE_DIR/$(basename "$URL")"
mkdir -p "$CACHE_DIR"
msg_ok "${CL}${BL}${URL}${CL}" msg_ok "${CL}${BL}${URL}${CL}"
curl -f#SL -o "$(basename "$URL")" "$URL"
echo -en "\e[1A\e[0K"
FILE=$(basename $URL)
msg_ok "Downloaded ${CL}${BL}${FILE}${CL}"
if [[ ! -s "$CACHE_FILE" ]]; then
curl -f#SL -o "$CACHE_FILE" "$URL"
echo -en "\e[1A\e[0K"
msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}"
else
msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}"
fi
# ==============================================================================
# STORAGE TYPE DETECTION
# ==============================================================================
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in case $STORAGE_TYPE in
nfs | dir) nfs | dir)
DISK_EXT=".qcow2" DISK_EXT=".qcow2"
DISK_REF="$VMID/" DISK_REF="$VMID/"
DISK_IMPORT="--format qcow2" DISK_IMPORT="-format qcow2"
THIN="" THIN=""
;; ;;
btrfs) btrfs)
DISK_EXT=".raw" DISK_EXT=".raw"
DISK_REF="$VMID/" DISK_REF="$VMID/"
DISK_IMPORT="--format raw" DISK_IMPORT="-format raw"
FORMAT=",efitype=4m" FORMAT=",efitype=4m"
THIN="" THIN=""
;; ;;
*)
DISK_EXT=""
DISK_REF=""
DISK_IMPORT="--format raw"
;;
esac esac
for i in {0,1}; do
# ============================================================================== disk="DISK$i"
# IMAGE CUSTOMIZATION WITH DOCKER eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-}
# ============================================================================== eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk}
msg_info "Preparing ${OS_DISPLAY} image with Docker"
WORK_FILE=$(mktemp --suffix=.qcow2)
cp "$CACHE_FILE" "$WORK_FILE"
export LIBGUESTFS_BACKEND_SETTINGS=dns=8.8.8.8,1.1.1.1
DOCKER_PREINSTALLED="no"
# Install qemu-guest-agent and Docker during image customization
msg_info "Installing base packages in image"
if virt-customize -a "$WORK_FILE" --install qemu-guest-agent,curl,ca-certificates >/dev/null 2>&1; then
msg_ok "Installed base packages"
msg_info "Installing Docker (this may take 2-5 minutes)"
if virt-customize -q -a "$WORK_FILE" --run-command "curl -fsSL https://get.docker.com | sh" >/dev/null 2>&1 &&
virt-customize -q -a "$WORK_FILE" --run-command "systemctl enable docker" >/dev/null 2>&1; then
msg_ok "Installed Docker"
msg_info "Configuring Docker daemon"
# Optimize Docker daemon configuration
virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/docker" >/dev/null 2>&1
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/docker/daemon.json << EOF
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF' >/dev/null 2>&1
DOCKER_PREINSTALLED="yes"
msg_ok "Configured Docker daemon"
else
msg_ok "Docker will be installed on first boot"
fi
else
msg_ok "Packages will be installed on first boot"
fi
msg_info "Finalizing image (hostname, SSH config)"
# Set hostname and prepare for unique machine-id
virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1
virt-customize -q -a "$WORK_FILE" --run-command "truncate -s 0 /etc/machine-id" >/dev/null 2>&1
virt-customize -q -a "$WORK_FILE" --run-command "rm -f /var/lib/dbus/machine-id" >/dev/null 2>&1
# Configure SSH for Cloud-Init
if [ "$USE_CLOUD_INIT" = "yes" ]; then
virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true
virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true
else
# Configure auto-login for nocloud images (no Cloud-Init)
virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d" >/dev/null 2>&1 || true
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM
EOF' >/dev/null 2>&1 || true
virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/getty@tty1.service.d" >/dev/null 2>&1 || true
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM
EOF' >/dev/null 2>&1 || true
fi
msg_ok "Finalized image"
# Create first-boot Docker install script (fallback if virt-customize failed)
if [ "$DOCKER_PREINSTALLED" = "no" ]; then
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /root/install-docker.sh << "DOCKERSCRIPT"
#!/bin/bash
exec > /var/log/install-docker.log 2>&1
echo "[$(date)] Starting Docker installation"
for i in {1..30}; do
ping -c 1 8.8.8.8 >/dev/null 2>&1 && break
sleep 2
done done
apt-get update if ! command -v virt-customize &>/dev/null; then
apt-get install -y qemu-guest-agent curl ca-certificates msg_info "Installing Pre-Requisite libguestfs-tools onto Host"
curl -fsSL https://get.docker.com | sh apt-get -qq update >/dev/null
systemctl enable docker apt-get -qq install libguestfs-tools lsb-release -y >/dev/null
systemctl start docker # Workaround for Proxmox VE 9.0 libguestfs issue
apt-get -qq install dhcpcd-base -y >/dev/null 2>&1 || true
mkdir -p /etc/docker msg_ok "Installed libguestfs-tools successfully"
cat > /etc/docker/daemon.json << DAEMON
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
DAEMON
systemctl restart docker
touch /root/.docker-installed
echo "[$(date)] Docker installation completed"
DOCKERSCRIPT
chmod +x /root/install-docker.sh' >/dev/null 2>&1
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/install-docker.service << "DOCKERSERVICE"
[Unit]
Description=Install Docker on First Boot
After=network-online.target
Wants=network-online.target
ConditionPathExists=!/root/.docker-installed
[Service]
Type=oneshot
ExecStart=/root/install-docker.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
DOCKERSERVICE
systemctl enable install-docker.service' >/dev/null 2>&1
fi fi
# Resize disk to target size msg_info "Adding Docker and Docker Compose Plugin to Debian 12 Qcow2 Disk Image"
msg_info "Resizing disk image to ${DISK_SIZE}" virt-customize -q -a "${FILE}" --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,software-properties-common,lsb-release >/dev/null &&
qemu-img resize "$WORK_FILE" "${DISK_SIZE}" >/dev/null 2>&1 virt-customize -q -a "${FILE}" --run-command "mkdir -p /etc/apt/keyrings && curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" >/dev/null &&
msg_ok "Resized disk image" virt-customize -q -a "${FILE}" --run-command "echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable' > /etc/apt/sources.list.d/docker.list" >/dev/null &&
virt-customize -q -a "${FILE}" --run-command "apt-get update -qq && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" >/dev/null &&
virt-customize -q -a "${FILE}" --run-command "systemctl enable docker" >/dev/null &&
virt-customize -q -a "${FILE}" --hostname "${HN}" >/dev/null &&
virt-customize -q -a "${FILE}" --run-command "echo -n > /etc/machine-id" >/dev/null
msg_ok "Added Docker and Docker Compose Plugin to Debian 12 Qcow2 Disk Image successfully"
# ============================================================================== msg_info "Expanding root partition to use full disk space"
# VM CREATION qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1
# ============================================================================== virt-resize --expand /dev/sda1 ${FILE} expanded.qcow2 >/dev/null 2>&1
msg_info "Creating Docker VM shell" mv expanded.qcow2 ${FILE} >/dev/null 2>&1
msg_ok "Expanded image to full size"
msg_info "Creating a Docker VM"
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null
msg_ok "Created VM shell" qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
qm set $VMID \
# ============================================================================== -efidisk0 ${DISK0_REF}${FORMAT} \
# DISK IMPORT -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \
# ============================================================================== -boot order=scsi0 \
msg_info "Importing disk into storage ($STORAGE)" -serial0 socket >/dev/null
qm resize $VMID scsi0 8G >/dev/null
if qm disk import --help >/dev/null 2>&1; then
IMPORT_CMD=(qm disk import)
else
IMPORT_CMD=(qm importdisk)
fi
IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "$WORK_FILE" "$STORAGE" ${DISK_IMPORT:-} 2>&1 || true)"
DISK_REF_IMPORTED="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")"
[[ -z "$DISK_REF_IMPORTED" ]] && DISK_REF_IMPORTED="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)"
[[ -z "$DISK_REF_IMPORTED" ]] && {
msg_error "Unable to determine imported disk reference."
echo "$IMPORT_OUT"
exit 1
}
msg_ok "Imported disk (${CL}${BL}${DISK_REF_IMPORTED}${CL})"
# Clean up work file
rm -f "$WORK_FILE"
# ==============================================================================
# VM CONFIGURATION
# ==============================================================================
msg_info "Attaching EFI and root disk"
qm set "$VMID" \
--efidisk0 "${STORAGE}:0,efitype=4m" \
--scsi0 "${DISK_REF_IMPORTED},${DISK_CACHE}${THIN%,}" \
--boot order=scsi0 \
--serial0 socket >/dev/null
qm set $VMID --agent enabled=1 >/dev/null qm set $VMID --agent enabled=1 >/dev/null
msg_ok "Attached EFI and root disk" DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://Helper-Scripts.com' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a>
# Set VM description <h2 style='font-size: 24px; margin: 20px 0;'>Docker VM</h2>
set_description
# Cloud-Init configuration <p style='margin: 16px 0;'>
if [ "$USE_CLOUD_INIT" = "yes" ]; then <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
msg_info "Configuring Cloud-Init" <img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" </a>
msg_ok "Cloud-Init configured" </p>
fi
# Start VM <span style='margin: 0 10px;'>
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
</span>
<span style='margin: 0 10px;'>
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
</span>
<span style='margin: 0 10px;'>
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
</span>
</div>
EOF
)
qm set $VMID -description "$DESCRIPTION" >/dev/null
msg_ok "Created a Docker VM ${CL}${BL}(${HN})"
if [ "$START_VM" == "yes" ]; then if [ "$START_VM" == "yes" ]; then
msg_info "Starting Docker VM" msg_info "Starting Docker VM"
qm start $VMID >/dev/null 2>&1 qm start $VMID
msg_ok "Started Docker VM" msg_ok "Started Docker VM"
fi fi
# ==============================================================================
# FINAL OUTPUT
# ==============================================================================
VM_IP=""
if [ "$START_VM" == "yes" ]; then
set +e
for i in {1..10}; do
VM_IP=$(qm guest cmd "$VMID" network-get-interfaces 2>/dev/null |
jq -r '.[] | select(.name != "lo") | ."ip-addresses"[]? | select(."ip-address-type" == "ipv4") | ."ip-address"' 2>/dev/null |
grep -v "^127\." | head -1) || true
[ -n "$VM_IP" ] && break
sleep 3
done
set -e
fi
echo -e "\n${INFO}${BOLD}${GN}Docker VM Configuration Summary:${CL}"
echo -e "${TAB}${DGN}VM ID: ${BGN}${VMID}${CL}"
echo -e "${TAB}${DGN}Hostname: ${BGN}${HN}${CL}"
echo -e "${TAB}${DGN}OS: ${BGN}${OS_DISPLAY}${CL}"
[ -n "$VM_IP" ] && echo -e "${TAB}${DGN}IP Address: ${BGN}${VM_IP}${CL}"
if [ "$DOCKER_PREINSTALLED" = "yes" ]; then
echo -e "${TAB}${DGN}Docker: ${BGN}Pre-installed (via get.docker.com)${CL}"
else
echo -e "${TAB}${DGN}Docker: ${BGN}Installing on first boot${CL}"
echo -e "${TAB}${YW}⚠️ Wait 2-3 minutes for installation to complete${CL}"
echo -e "${TAB}${YW}⚠️ Check progress: ${BL}cat /var/log/install-docker.log${CL}"
fi
if [ "$USE_CLOUD_INIT" = "yes" ]; then
display_cloud_init_info "$VMID" "$HN" 2>/dev/null || true
fi
post_update_to_api "done" "none" post_update_to_api "done" "none"
msg_ok "Completed successfully!\n" msg_ok "Completed successfully!\n"

View File

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