Compare commits

..
Author SHA1 Message Date
MickLesk d5bcc1f929 tracearr: fetch the newest protomaps basemap build
Tracearr pins a daily build that protomaps deletes after a few days, so
every install got a map without base tiles.
2026-10-03 20:52:26 +02:00
57 changed files with 3024 additions and 2265 deletions
-84
View File
@@ -1,87 +1,3 @@
## 2026-09-30
### 🆕 New Scripts
- Shoko ([#17617](https://github.com/community-scripts/ProxmoxVE/pull/17617))
- SIlo ([#17614](https://github.com/community-scripts/ProxmoxVE/pull/17614))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(blocky): restart on failure and wait for network-online [@haplo](https://github.com/haplo) ([#17601](https://github.com/community-scripts/ProxmoxVE/pull/17601))
- SparkyFitness: update sed replacement [@tomfrenzel](https://github.com/tomfrenzel) ([#17604](https://github.com/community-scripts/ProxmoxVE/pull/17604))
### 💾 Core
- vm: autologin on the serial console for images without cloud-init [@MickLesk](https://github.com/MickLesk) ([core#80](https://github.com/community-scripts/core/pull/80))
- download_gpg_key: make dearmored keyrings world-readable [@MickLesk](https://github.com/MickLesk) ([core#79](https://github.com/community-scripts/core/pull/79))
## 2026-09-29
### 🆕 New Scripts
- Anki Sync Server ([#17416](https://github.com/community-scripts/ProxmoxVE/pull/17416))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(kaneo): build with Vite+ instead of Turbo [@tinsever](https://github.com/tinsever) ([#17599](https://github.com/community-scripts/ProxmoxVE/pull/17599))
- tianji: raise the Node heap for the static build on install [@MickLesk](https://github.com/MickLesk) ([#17582](https://github.com/community-scripts/ProxmoxVE/pull/17582))
- fix(romm): snapshot Redis hourly like the upstream image [@DenislavDenev](https://github.com/DenislavDenev) ([#17562](https://github.com/community-scripts/ProxmoxVE/pull/17562))
- checkmate: bump Node.js from 22 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#17554](https://github.com/community-scripts/ProxmoxVE/pull/17554))
- #### 🔧 Refactor
- Drop the scripts base pin from every ct/ script [@MickLesk](https://github.com/MickLesk) ([#17585](https://github.com/community-scripts/ProxmoxVE/pull/17585))
- odoo: move to Debian 13 and stay on the installed major on update [@MickLesk](https://github.com/MickLesk) ([#17581](https://github.com/community-scripts/ProxmoxVE/pull/17581))
- manyfold: use upstream f3d for headless renders on amd64 [@MickLesk](https://github.com/MickLesk) ([#17583](https://github.com/community-scripts/ProxmoxVE/pull/17583))
### 💾 Core
- Default the scripts base to ProxmoxVE [@MickLesk](https://github.com/MickLesk) ([core#76](https://github.com/community-scripts/core/pull/76))
### 🧰 Tools
- #### 🐞 Bug Fixes
- all-templates: handle the arch column of pveam available [@MickLesk](https://github.com/MickLesk) ([#17579](https://github.com/community-scripts/ProxmoxVE/pull/17579))
### 📂 Github
- Skip automated core pull requests in the changelog [@MickLesk](https://github.com/MickLesk) ([#17589](https://github.com/community-scripts/ProxmoxVE/pull/17589))
- Post a test command for vm/, tools/ and turnkey/ changes too [@MickLesk](https://github.com/MickLesk) ([#17587](https://github.com/community-scripts/ProxmoxVE/pull/17587))
- Remove the PocketBase AI bot [@MickLesk](https://github.com/MickLesk) ([#17591](https://github.com/community-scripts/ProxmoxVE/pull/17591))
## 2026-09-28
### 🚀 Updated Scripts
- Immich: Pin version to 3.2.4 [@vhsdream](https://github.com/vhsdream) ([#17564](https://github.com/community-scripts/ProxmoxVE/pull/17564))
- #### 🐞 Bug Fixes
- fix(autocaliweb): wire .env into all service units, not just autocali… [@bobartlett](https://github.com/bobartlett) ([#17561](https://github.com/community-scripts/ProxmoxVE/pull/17561))
- Zipline: Change cp command [@Mraedis](https://github.com/Mraedis) ([#17559](https://github.com/community-scripts/ProxmoxVE/pull/17559))
- Borg-UI: start through upstream's start.sh so migrations run [@MickLesk](https://github.com/MickLesk) ([#17563](https://github.com/community-scripts/ProxmoxVE/pull/17563))
- linkding: serve favicons and preview images [@MickLesk](https://github.com/MickLesk) ([#17557](https://github.com/community-scripts/ProxmoxVE/pull/17557))
- SparkyFitness: Fill in the nginx resolver 1.7.3 added [@MickLesk](https://github.com/MickLesk) ([#17556](https://github.com/community-scripts/ProxmoxVE/pull/17556))
- #### 🔧 Refactor
- Plane: Replace MinIO installation with Silo [@MickLesk](https://github.com/MickLesk) ([#17347](https://github.com/community-scripts/ProxmoxVE/pull/17347))
## 2026-09-27
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(aurral): bump to Node 26 and bypass strict engine pin [@JodyVanden](https://github.com/JodyVanden) ([#17543](https://github.com/community-scripts/ProxmoxVE/pull/17543))
- Tracearr: fetch map tiles on install/update [@durzo](https://github.com/durzo) ([#17544](https://github.com/community-scripts/ProxmoxVE/pull/17544))
- fix(autocaliweb): define INSTALL_DIR before first use [@bobartlett](https://github.com/bobartlett) ([#17545](https://github.com/community-scripts/ProxmoxVE/pull/17545))
## 2026-09-26
### 🚀 Updated Scripts
-74
View File
@@ -1,74 +0,0 @@
## 2026-10-03
### 🆕 New Scripts
- Keeper.sh ([#17659](https://github.com/community-scripts/ProxmoxVE/pull/17659))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- hermesagent: download the installer before running it [@MickLesk](https://github.com/MickLesk) ([#17666](https://github.com/community-scripts/ProxmoxVE/pull/17666))
- frigate: raise the Node heap for the web build [@MickLesk](https://github.com/MickLesk) ([#17667](https://github.com/community-scripts/ProxmoxVE/pull/17667))
- Tracearr: workaround for failing map tiles download and data preservation [@durzo](https://github.com/durzo) ([#17661](https://github.com/community-scripts/ProxmoxVE/pull/17661))
- fix(nginxproxymanager): clean yarn cache after builds to prevent disk growth [@PsycoStea](https://github.com/PsycoStea) ([#17653](https://github.com/community-scripts/ProxmoxVE/pull/17653))
- #### 🔧 Refactor
- Several Scripts: refactor msg_ blocks, generate passwords with helper, add CLEAN_INSTALL_KEEP [@MickLesk](https://github.com/MickLesk) ([#17655](https://github.com/community-scripts/ProxmoxVE/pull/17655))
### 💾 Core
- detect_repo_source: read the license header and fall back to ProxmoxVE [@MickLesk](https://github.com/MickLesk) ([core#85](https://github.com/community-scripts/core/pull/85))
## 2026-10-02
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- zerobyte: upgrade Bun on update [@MickLesk](https://github.com/MickLesk) ([#17646](https://github.com/community-scripts/ProxmoxVE/pull/17646))
- sure: keep Active Storage uploads across updates [@MickLesk](https://github.com/MickLesk) ([#17648](https://github.com/community-scripts/ProxmoxVE/pull/17648))
- twenty: run upstreams upgrade command instead of the removed setup-db.ts [@MickLesk](https://github.com/MickLesk) ([#17647](https://github.com/community-scripts/ProxmoxVE/pull/17647))
- #### 🔧 Refactor
- NPM: Refactor / Fix npm openresty user and certbot version [@MickLesk](https://github.com/MickLesk) ([#17649](https://github.com/community-scripts/ProxmoxVE/pull/17649))
- several scripts: use the release helpers instead of hand-rolled version checks and downloads [@MickLesk](https://github.com/MickLesk) ([#17625](https://github.com/community-scripts/ProxmoxVE/pull/17625))
### 💾 Core
- QoL: message blocks, clean-install keep list, password helper, network limits [@MickLesk](https://github.com/MickLesk) ([core#61](https://github.com/community-scripts/core/pull/61))
## 2026-10-01
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- romm: keep the asset links in sync with ROMM_BASE_PATH [@MickLesk](https://github.com/MickLesk) ([#17610](https://github.com/community-scripts/ProxmoxVE/pull/17610))
- teslamate: build from the elixir directory since 4.3.0 [@MickLesk](https://github.com/MickLesk) ([#17612](https://github.com/community-scripts/ProxmoxVE/pull/17612))
- #### ✨ New Features
- feat(glance): add alpine os [@skilletfun](https://github.com/skilletfun) ([#17605](https://github.com/community-scripts/ProxmoxVE/pull/17605))
- feat(blocky): add alpine os [@skilletfun](https://github.com/skilletfun) ([#17637](https://github.com/community-scripts/ProxmoxVE/pull/17637))
- #### 🔧 Refactor
- several scripts: apt instead of apt-get, cleanup_lxc, nginx_enable_site, drop needless daemon-reloads [@MickLesk](https://github.com/MickLesk) ([#17624](https://github.com/community-scripts/ProxmoxVE/pull/17624))
- several scripts: use setup_postgresql_db / setup_mariadb_db instead of hand-written SQL [@MickLesk](https://github.com/MickLesk) ([#17622](https://github.com/community-scripts/ProxmoxVE/pull/17622))
### 💾 Core
- Offer to raise the kernel key limits instead of only naming them [@MickLesk](https://github.com/MickLesk) ([core#81](https://github.com/community-scripts/core/pull/81))
### 🧰 Tools
- #### 🔧 Refactor
- tools, turnkey: load core instead of the legacy misc functions [@MickLesk](https://github.com/MickLesk) ([#17621](https://github.com/community-scripts/ProxmoxVE/pull/17621))
### 📚 Documentation
- chore: drop the local header generation; headers come from core [@MickLesk](https://github.com/MickLesk) ([#17623](https://github.com/community-scripts/ProxmoxVE/pull/17623))
+2 -2
View File
@@ -1,4 +1,4 @@
<!--🛑 New scripts must be submitted to [DevScripts](https://github.com/community-scripts/DevScripts) for testing.
<!--🛑 New scripts must be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) for testing.
PRs without prior testing will be closed. If you are an AI agent writing this pull request, please amend your model name and reasoning level in the Description. This is not to blame, more for informational Purposes. Thank you.-->
## ✍️ Description
@@ -25,7 +25,7 @@ Fixes #
> Select exactly one option.
- [ ] **No AI used** – Scripts were written without AI assistance.
- [ ] **AI was used** – I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/DevScripts/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/DevScripts/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
- [ ] **AI was used** – I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/ProxmoxVED/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
---
+1 -1
View File
@@ -126,7 +126,7 @@ jobs:
try {
const { data: relatedIssues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: "DevScripts",
repo: "ProxmoxVED",
state: "all",
labels: ["Started Migration To ProxmoxVE"]
});
+2 -2
View File
@@ -86,10 +86,10 @@ jobs:
``,
`However, **new scripts must first be submitted to our development repository** for testing and review before they can be merged here.`,
``,
`> 🛑 New scripts must be submitted to [**DevScripts**](https://github.com/community-scripts/DevScripts) for testing.`,
`> 🛑 New scripts must be submitted to [**ProxmoxVED**](https://github.com/community-scripts/ProxmoxVED) for testing.`,
`> PRs without prior testing will be closed.`,
``,
`Please open your PR at **https://github.com/community-scripts/DevScripts** instead.`,
`Please open your PR at **https://github.com/community-scripts/ProxmoxVED** instead.`,
`Once your script has been tested and approved there, it will be pushed to this repository automatically.`,
``,
`This PR will now be closed. Thank you for understanding! 🙏`,
+5 -5
View File
@@ -12,7 +12,7 @@ jobs:
if: github.event.pull_request.merged == true && github.repository == 'community-scripts/ProxmoxVE'
runs-on: self-hosted
env:
DEV_REPO: community-scripts/DevScripts
DEV_REPO: community-scripts/ProxmoxVED
steps:
- name: Checkout target repo (merge commit)
@@ -60,7 +60,7 @@ jobs:
echo "count=$(printf '%s' "$slugs" | wc -w | tr -d '[:space:]')" >> "$GITHUB_OUTPUT"
echo "Slugs in this PR: ${slugs:-<none>}"
- name: Resolve the matching DevScripts issue
- name: Resolve the matching ProxmoxVED issue
id: find_issue
shell: bash
env:
@@ -71,7 +71,7 @@ jobs:
# creates, so migration PRs carry an exact back-reference. Guessing from
# titles or file names is never needed for them.
matched=$(printf '%s' "$PR_BODY" \
| grep -oE 'community-scripts/(ProxmoxVED|DevScripts)#[0-9]+' \
| grep -oE 'community-scripts/ProxmoxVED#[0-9]+' \
| sed 's/.*#//' || true)
if [ -n "$matched" ]; then
@@ -96,7 +96,7 @@ jobs:
[ -n "$row" ] || continue
num=$(printf '%s' "$row" | jq -r '.number')
# Same slug derivation the DevScripts workflows use, so both
# Same slug derivation the ProxmoxVED workflows use, so both
# sides agree on what "alpine-cinny" means.
name=$(printf '%s' "$row" | jq -r '.body // ""' \
| sed -n '/^###[[:space:]]*Name of the Script/,/^###/p' \
@@ -124,7 +124,7 @@ jobs:
echo "issue_numbers=$matched" >> "$GITHUB_OUTPUT"
echo "Closing: ${matched:-<none>}"
- name: Comment on and close matching DevScripts issues
- name: Comment on and close matching ProxmoxVED issues
if: steps.find_issue.outputs.issue_numbers != ''
shell: bash
env:
+161 -161
View File
@@ -110,9 +110,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
@@ -126,14 +123,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
<details>
<summary><h4>October (3 entries)</h4></summary>
[View October 2026 Changelog](.github/changelogs/2026/10.md)
</details>
<details>
<summary><h4>September (30 entries)</h4></summary>
<summary><h4>September (26 entries)</h4></summary>
[View September 2026 Changelog](.github/changelogs/2026/09.md)
@@ -559,149 +549,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-10-08
### 🆕 New Scripts
- Unifi-OS-Server VM ([#17752](https://github.com/community-scripts/ProxmoxVE/pull/17752))
- ZimaOS VM ([#17778](https://github.com/community-scripts/ProxmoxVE/pull/17778))
### 🚀 Updated Scripts
- Revert "Immich: Pin to v3.3.0" [@MickLesk](https://github.com/MickLesk) ([#17767](https://github.com/community-scripts/ProxmoxVE/pull/17767))
- #### 🐞 Bug Fixes
- Homarr: replace the musl better-sqlite3 that v2.3.0 ships for Debian [@MickLesk](https://github.com/MickLesk) ([#17789](https://github.com/community-scripts/ProxmoxVE/pull/17789))
- CLIProxyAPI: keep plugins and data across updates [@MickLesk](https://github.com/MickLesk) ([#17790](https://github.com/community-scripts/ProxmoxVE/pull/17790))
- Kima-Hub: install the Python services into a uv venv [@MickLesk](https://github.com/MickLesk) ([#17791](https://github.com/community-scripts/ProxmoxVE/pull/17791))
- #### ✨ New Features
- Immich: Pin to 3.3.0 / Update ML Python to 3.13 [@MickLesk](https://github.com/MickLesk) ([#17770](https://github.com/community-scripts/ProxmoxVE/pull/17770))
- #### 🔧 Refactor
- Refactor: OPNsense VM [@MickLesk](https://github.com/MickLesk) ([#17785](https://github.com/community-scripts/ProxmoxVE/pull/17785))
- Refactor: Nextcloud VM (Turnkey) [@MickLesk](https://github.com/MickLesk) ([#17787](https://github.com/community-scripts/ProxmoxVE/pull/17787))
- Refactor: OpenWrt VM [@MickLesk](https://github.com/MickLesk) ([#17774](https://github.com/community-scripts/ProxmoxVE/pull/17774))
- Refactor: Ubuntu VM [@MickLesk](https://github.com/MickLesk) ([#17776](https://github.com/community-scripts/ProxmoxVE/pull/17776))
### 💾 Core
- Tolerate a missing datacenter.cfg in vm_keyboard_default [@MickLesk](https://github.com/MickLesk) ([core#124](https://github.com/community-scripts/core/pull/124))
- Take the ISO storage from the disk pool when it can hold ISOs [@MickLesk](https://github.com/MickLesk) ([core#122](https://github.com/community-scripts/core/pull/122))
- Shared VM helpers: disk import, releases, checksums, first boot, IP by MAC [@MickLesk](https://github.com/MickLesk) ([core#117](https://github.com/community-scripts/core/pull/117))
- tools: forge truncated release list [@MickLesk](https://github.com/MickLesk) ([core#118](https://github.com/community-scripts/core/pull/118))
- Remember a kept cached image until the upstream changes [@MickLesk](https://github.com/MickLesk) ([core#119](https://github.com/community-scripts/core/pull/119))
- Choose the VM keyboard layout and carry the host's timezone into prepared images [@MickLesk](https://github.com/MickLesk) ([core#120](https://github.com/community-scripts/core/pull/120))
- VM's: run first-boot units without debconf dialogs [@MickLesk](https://github.com/MickLesk) ([core#121](https://github.com/community-scripts/core/pull/121))
## 2026-10-07
### 🆕 New Scripts
- Fedora VM ([#17747](https://github.com/community-scripts/ProxmoxVE/pull/17747))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- AudioMuse-AI: replace the venv on update without asking [@MickLesk](https://github.com/MickLesk) ([#17738](https://github.com/community-scripts/ProxmoxVE/pull/17738))
- paperclip: run as a dedicated non-root user so Claude Code can skip permissions [@samvandenbossche](https://github.com/samvandenbossche) ([#17707](https://github.com/community-scripts/ProxmoxVE/pull/17707))
- Thingsboard: update Java version from 17 to 25 [@CerberusStyle](https://github.com/CerberusStyle) ([#17733](https://github.com/community-scripts/ProxmoxVE/pull/17733))
- #### ✨ New Features
- Immich: Pin to v3.3.0 [@vhsdream](https://github.com/vhsdream) ([#17759](https://github.com/community-scripts/ProxmoxVE/pull/17759))
- #### 💥 Breaking Changes
- OpenCloud: bump to v8.1.0, rebuild search index on upgrade [@SimKaiLong](https://github.com/SimKaiLong) ([#17710](https://github.com/community-scripts/ProxmoxVE/pull/17710))
- #### 🔧 Refactor
- Pangolin: Unpin Release, stable enough, Bump to latest [@MickLesk](https://github.com/MickLesk) ([#17740](https://github.com/community-scripts/ProxmoxVE/pull/17740))
- Refactor: Archlinux-VM [@MickLesk](https://github.com/MickLesk) ([#17741](https://github.com/community-scripts/ProxmoxVE/pull/17741))
- Refactor: MikroTik RouterOS [@MickLesk](https://github.com/MickLesk) ([#17748](https://github.com/community-scripts/ProxmoxVE/pull/17748))
### 💾 Core
- Fix Fedora and BLS cloud image console setup [@MickLesk](https://github.com/MickLesk) ([core#116](https://github.com/community-scripts/core/pull/116))
- Keep setup_nodejs alive when the caller's directory is gone [@MickLesk](https://github.com/MickLesk) ([core#114](https://github.com/community-scripts/core/pull/114))
## 2026-10-06
### 🆕 New Scripts
- Pricebuddy ([#17708](https://github.com/community-scripts/ProxmoxVE/pull/17708))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Webtrees: stop serving data/ and the source folders directly [@MickLesk](https://github.com/MickLesk) ([#17724](https://github.com/community-scripts/ProxmoxVE/pull/17724))
### 💾 Core
- Incus: check the architecture on Incus hosts too [@MickLesk](https://github.com/MickLesk) ([core#113](https://github.com/community-scripts/core/pull/113))
- Incus: Pass the app's var_* settings into Incus containers [@MickLesk](https://github.com/MickLesk) ([core#112](https://github.com/community-scripts/core/pull/112))
- Incus: stub storage_content_check on Incus [@MickLesk](https://github.com/MickLesk) ([core#104](https://github.com/community-scripts/core/pull/104))
- Incus: map Proxmox template versions to Incus image names [@MickLesk](https://github.com/MickLesk) ([core#111](https://github.com/community-scripts/core/pull/111))
- Incus: read storage names from .vars files [@MickLesk](https://github.com/MickLesk) ([core#110](https://github.com/community-scripts/core/pull/110))
- Incus: show the spinner inside containers again [@MickLesk](https://github.com/MickLesk) ([core#109](https://github.com/community-scripts/core/pull/109))
- Incus: set the hostname with sh, so it works before bash is installed [@MickLesk](https://github.com/MickLesk) ([core#102](https://github.com/community-scripts/core/pull/102))
- Incus: reserve a plain address on Incus, leave the gateway to the network [@MickLesk](https://github.com/MickLesk) ([core#101](https://github.com/community-scripts/core/pull/101))
- Incus: Wait for the network on Alpine OS too [@MickLesk](https://github.com/MickLesk) ([core#105](https://github.com/community-scripts/core/pull/105))
- Incus: do not fail the build when hostname -I is missing [@MickLesk](https://github.com/MickLesk) ([core#103](https://github.com/community-scripts/core/pull/103))
- Incus: Fuse always in unprivileged Containers, attach TUN only when container lacks it [@MickLesk](https://github.com/MickLesk) ([core#108](https://github.com/community-scripts/core/pull/108))
- Incus: accept mode=generated [@MickLesk](https://github.com/MickLesk) ([core#106](https://github.com/community-scripts/core/pull/106))
- Incus: pass the full install environment into Incus containers (quoted) [@MickLesk](https://github.com/MickLesk) ([core#107](https://github.com/community-scripts/core/pull/107))
## 2026-10-05
### 🚀 Updated Scripts
- Point to DevScripts, the renamed ProxmoxVED [@MickLesk](https://github.com/MickLesk) ([#17694](https://github.com/community-scripts/ProxmoxVE/pull/17694))
- #### 🐞 Bug Fixes
- wanderer: set the proxy secret and install plugins in their own directories [@MickLesk](https://github.com/MickLesk) ([#17698](https://github.com/community-scripts/ProxmoxVE/pull/17698))
- discourse: serve stylesheets and repair the update [@MickLesk](https://github.com/MickLesk) ([#17697](https://github.com/community-scripts/ProxmoxVE/pull/17697))
- wikijs: bump Node.js from 24 to 26 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#17689](https://github.com/community-scripts/ProxmoxVE/pull/17689))
- jotty: bump Node.js from 22 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#17688](https://github.com/community-scripts/ProxmoxVE/pull/17688))
### 💾 Core
- Read releases from github.com when the API is rate limited, resume stalled downloads [@MickLesk](https://github.com/MickLesk) ([core#99](https://github.com/community-scripts/core/pull/99))
- Check for template and container storage before the settings menu [@MickLesk](https://github.com/MickLesk) ([core#98](https://github.com/community-scripts/core/pull/98))
- Check /etc/pve before the settings menu [@MickLesk](https://github.com/MickLesk) ([core#97](https://github.com/community-scripts/core/pull/97))
- Check container settings before pct create rejects them [@MickLesk](https://github.com/MickLesk) ([core#96](https://github.com/community-scripts/core/pull/96))
- PVE: Smart Diagnosis - say why a container would not start [@MickLesk](https://github.com/MickLesk) ([core#95](https://github.com/community-scripts/core/pull/95))
- Retry template downloads that pveam reports as done but are not [@MickLesk](https://github.com/MickLesk) ([core#94](https://github.com/community-scripts/core/pull/94))
- Check the OS release against pve-container before creating anything [@MickLesk](https://github.com/MickLesk) ([core#93](https://github.com/community-scripts/core/pull/93))
- Rename "ProxmoxVED"-Links to "DevScripts" [@MickLesk](https://github.com/MickLesk) ([core#91](https://github.com/community-scripts/core/pull/91))
### 🧰 Tools
- #### 🐞 Bug Fixes
- monitor-all: read container IPs from the host side [@jasonobrien](https://github.com/jasonobrien) ([#17686](https://github.com/community-scripts/ProxmoxVE/pull/17686))
## 2026-10-04
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- immich: read the Intel runtime from the pinned release [@MickLesk](https://github.com/MickLesk) ([#17677](https://github.com/community-scripts/ProxmoxVE/pull/17677))
### 💾 Core
- better explain unsupported version & upgrade path for pve [@MickLesk](https://github.com/MickLesk) ([core#86](https://github.com/community-scripts/core/pull/86))
- Quote PostgreSQL identifiers and drop spaces from the creds file name [@MickLesk](https://github.com/MickLesk) ([core#89](https://github.com/community-scripts/core/pull/89))
- Find the default Rust toolchain in current rustup output [@MickLesk](https://github.com/MickLesk) ([core#88](https://github.com/community-scripts/core/pull/88))
## 2026-10-03
### 🆕 New Scripts
@@ -712,8 +559,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🐞 Bug Fixes
- hermesagent: download the installer before running it [@MickLesk](https://github.com/MickLesk) ([#17666](https://github.com/community-scripts/ProxmoxVE/pull/17666))
- frigate: raise the Node heap for the web build [@MickLesk](https://github.com/MickLesk) ([#17667](https://github.com/community-scripts/ProxmoxVE/pull/17667))
- Tracearr: workaround for failing map tiles download and data preservation [@durzo](https://github.com/durzo) ([#17661](https://github.com/community-scripts/ProxmoxVE/pull/17661))
- fix(nginxproxymanager): clean yarn cache after builds to prevent disk growth [@PsycoStea](https://github.com/PsycoStea) ([#17653](https://github.com/community-scripts/ProxmoxVE/pull/17653))
@@ -721,10 +566,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Several Scripts: refactor msg_ blocks, generate passwords with helper, add CLEAN_INSTALL_KEEP [@MickLesk](https://github.com/MickLesk) ([#17655](https://github.com/community-scripts/ProxmoxVE/pull/17655))
### 💾 Core
- detect_repo_source: read the license header and fall back to ProxmoxVE [@MickLesk](https://github.com/MickLesk) ([core#85](https://github.com/community-scripts/core/pull/85))
## 2026-10-02
### 🚀 Updated Scripts
@@ -1367,4 +1208,163 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
### 💾 Core
- setup_go: resolve bare major.minor versions to the latest patch release [@MickLesk](https://github.com/MickLesk) ([core#24](https://github.com/community-scripts/core/pull/24))
- setup_go: resolve bare major.minor versions to the latest patch release [@MickLesk](https://github.com/MickLesk) ([core#24](https://github.com/community-scripts/core/pull/24))
## 2026-09-03
### 🚀 Updated Scripts
- Immich: bump libheif and libraw [@vhsdream](https://github.com/vhsdream) ([#16996](https://github.com/community-scripts/ProxmoxVE/pull/16996))
- #### 🐞 Bug Fixes
- romm: allow git-based npm dependency for rom-patcher under npm v12 [@MickLesk](https://github.com/MickLesk) ([#16990](https://github.com/community-scripts/ProxmoxVE/pull/16990))
- Pin Go to the version each project declares in go.mod [@MickLesk](https://github.com/MickLesk) ([#16976](https://github.com/community-scripts/ProxmoxVE/pull/16976))
- Authentik update 2026.8.1 [@thieneret](https://github.com/thieneret) ([#16980](https://github.com/community-scripts/ProxmoxVE/pull/16980))
## 2026-09-02
### 🆕 New Scripts
- OwnTracks-Recorder ([#16967](https://github.com/community-scripts/ProxmoxVE/pull/16967))
- PeerTube ([#16966](https://github.com/community-scripts/ProxmoxVE/pull/16966))
- Kaneo ([#16965](https://github.com/community-scripts/ProxmoxVE/pull/16965))
- JDownloader ([#16964](https://github.com/community-scripts/ProxmoxVE/pull/16964))
- ReadMeABook ([#16963](https://github.com/community-scripts/ProxmoxVE/pull/16963))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Enhance backup process in teddycloud.sh [@MickLesk](https://github.com/MickLesk) ([#16946](https://github.com/community-scripts/ProxmoxVE/pull/16946))
- mealie: copy Nuxt generate output and use pnpm when lockfile is present [@mjhuff](https://github.com/mjhuff) ([#16938](https://github.com/community-scripts/ProxmoxVE/pull/16938))
- OpenThread-BR: Clean build directory before update [@tomfrenzel](https://github.com/tomfrenzel) ([#16949](https://github.com/community-scripts/ProxmoxVE/pull/16949))
- #### 🔧 Refactor
- Scripts: use shared core bootstrap for final 114 Script Batch [@MickLesk](https://github.com/MickLesk) ([#16953](https://github.com/community-scripts/ProxmoxVE/pull/16953))
- Scripts: use shared core bootstrap for next 115 Script Batch [@MickLesk](https://github.com/MickLesk) ([#16952](https://github.com/community-scripts/ProxmoxVE/pull/16952))
- Scripts: use shared core bootstrap for next 115 Script Batch [@MickLesk](https://github.com/MickLesk) ([#16951](https://github.com/community-scripts/ProxmoxVE/pull/16951))
- Scripts: use shared core bootstrap for next 100 Script Batch [@MickLesk](https://github.com/MickLesk) ([#16950](https://github.com/community-scripts/ProxmoxVE/pull/16950))
### 💾 Core
- setup_meilisearch: generate a 32-byte master key [@MickLesk](https://github.com/MickLesk) ([core#20](https://github.com/community-scripts/core/pull/20))
- setup_meilisearch: generate a 32-byte master key [@MickLesk](https://github.com/MickLesk) ([core#19](https://github.com/community-scripts/core/pull/19))
- setup_nodejs: restore the npm allowScripts bypass lost in the migration [@MickLesk](https://github.com/MickLesk) ([core#18](https://github.com/community-scripts/core/pull/18))
## 2026-09-01
### 🆕 New Scripts
- Mailpit ([#16942](https://github.com/community-scripts/ProxmoxVE/pull/16942))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Revert Aurral to Node22 [@Copilot](https://github.com/Copilot) ([#16930](https://github.com/community-scripts/ProxmoxVE/pull/16930))
- jellyfin: verify repo suite via fallback chain and use ensure_dependencies for clearer apt failures [@MickLesk](https://github.com/MickLesk) ([#16916](https://github.com/community-scripts/ProxmoxVE/pull/16916))
- #### 🔧 Refactor
- Move the next 25 scripts onto the core engine [@MickLesk](https://github.com/MickLesk) ([#16934](https://github.com/community-scripts/ProxmoxVE/pull/16934))
### 💾 Core
- Generate app headers [@github-actions[bot]](https://github.com/github-actions[bot]) ([core#15](https://github.com/community-scripts/core/pull/15))
- Report the exit code the engine actually saw [@MickLesk](https://github.com/MickLesk) ([core#17](https://github.com/community-scripts/core/pull/17))
- Silence npm's update notice so the real error stays visible [@MickLesk](https://github.com/MickLesk) ([core#16](https://github.com/community-scripts/core/pull/16))
### 🧰 Tools
- #### 🐞 Bug Fixes
- fix(add-iptag): merge live IPv4 addresses for running LXCs [@phillf](https://github.com/phillf) ([#16691](https://github.com/community-scripts/ProxmoxVE/pull/16691))
## 2026-08-31
### 🆕 New Scripts
- Aurral ([#16908](https://github.com/community-scripts/ProxmoxVE/pull/16908))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- openwebui: add UV_HTTP_TIMEOUT and retry loop to prevent uv install hangs [@MickLesk](https://github.com/MickLesk) ([#16917](https://github.com/community-scripts/ProxmoxVE/pull/16917))
- bambuddy: force asyncio loop, uvloop breaks camera proxy handlers [@MickLesk](https://github.com/MickLesk) ([#16904](https://github.com/community-scripts/ProxmoxVE/pull/16904))
- bookorbit: raise service start timeout, migration can exceed systemd default [@MickLesk](https://github.com/MickLesk) ([#16860](https://github.com/community-scripts/ProxmoxVE/pull/16860))
- node-red: remove --unsafe-perm flag [@MickLesk](https://github.com/MickLesk) ([#16859](https://github.com/community-scripts/ProxmoxVE/pull/16859))
- directus: add build-essential dependency [@MickLesk](https://github.com/MickLesk) ([#16858](https://github.com/community-scripts/ProxmoxVE/pull/16858))
- #### ✨ New Features
- haos-vm: add optional x86-64-v2-AES CPU option, future HA numpy builds need it [@MickLesk](https://github.com/MickLesk) ([#16868](https://github.com/community-scripts/ProxmoxVE/pull/16868))
- #### 🔧 Refactor
- omv: migrate to new package repo host (packages.openmediavault.org) is dead [@MickLesk](https://github.com/MickLesk) ([#16918](https://github.com/community-scripts/ProxmoxVE/pull/16918))
- yuvomi/aurral: bump NODE_VERSION [@MickLesk](https://github.com/MickLesk) ([#16914](https://github.com/community-scripts/ProxmoxVE/pull/16914))
- vaultwarden: relax cargo release profile via env vars to avoid build OOM [@MickLesk](https://github.com/MickLesk) ([#16915](https://github.com/community-scripts/ProxmoxVE/pull/16915))
- kima-hub/maintainerr/planka/spliit: bump NODE_VERSION [@MickLesk](https://github.com/MickLesk) ([#16905](https://github.com/community-scripts/ProxmoxVE/pull/16905))
- gatus: pin Go toolchain to gatus's own go.mod version [@MickLesk](https://github.com/MickLesk) ([#16893](https://github.com/community-scripts/ProxmoxVE/pull/16893))
- immich: tolerate enable-maintenance-mode crash, same as disable-maintenance-mode [@MickLesk](https://github.com/MickLesk) ([#16866](https://github.com/community-scripts/ProxmoxVE/pull/16866))
- move the hardware-accelerated scripts onto new core [@MickLesk](https://github.com/MickLesk) ([#16864](https://github.com/community-scripts/ProxmoxVE/pull/16864))
- ntopng: use ntop.org's documented apt repo per Debian codename, not always apt-stable [@MickLesk](https://github.com/MickLesk) ([#16862](https://github.com/community-scripts/ProxmoxVE/pull/16862))
- Refactor: OpenGist [@MickLesk](https://github.com/MickLesk) ([#16861](https://github.com/community-scripts/ProxmoxVE/pull/16861))
### 💾 Core
- Refactor VM script settings and fix load_functions errors [@MickLesk](https://github.com/MickLesk) ([core#13](https://github.com/community-scripts/core/pull/13))
- Ask ProxmoxVE to refresh its changelog on merge [@MickLesk](https://github.com/MickLesk) ([core#11](https://github.com/community-scripts/core/pull/11))
- Lift the settings summary and the mode fork into vm-core [@MickLesk](https://github.com/MickLesk) ([core#12](https://github.com/community-scripts/core/pull/12))
### 🧰 Tools
- #### 🐞 Bug Fixes
- filebrowser-quantum: strip removed disableIndexing key on update, restart service [@MickLesk](https://github.com/MickLesk) ([#16892](https://github.com/community-scripts/ProxmoxVE/pull/16892))
### 📚 Documentation
- Include engine pull requests in the changelog [@MickLesk](https://github.com/MickLesk) ([#16867](https://github.com/community-scripts/ProxmoxVE/pull/16867))
## 2026-08-30
### 🆕 New Scripts
- SolidInvoice ([#16869](https://github.com/community-scripts/ProxmoxVE/pull/16869))
- Super-Productivity ([#16870](https://github.com/community-scripts/ProxmoxVE/pull/16870))
- Defguard ([#16871](https://github.com/community-scripts/ProxmoxVE/pull/16871))
- OpenBao ([#16872](https://github.com/community-scripts/ProxmoxVE/pull/16872))
### 🚀 Updated Scripts
- #### 🔧 Refactor
- general: remove gitea links overall [@MickLesk](https://github.com/MickLesk) ([#16863](https://github.com/community-scripts/ProxmoxVE/pull/16863))
## 2026-08-29
### 📚 Documentation
- github action: post the command that tests a ct/ or install/ change [@MickLesk](https://github.com/MickLesk) ([#16833](https://github.com/community-scripts/ProxmoxVE/pull/16833))
## 2026-08-28
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- homepage: set CI=true so pnpm can self-heal node_modules without a TTY [@MickLesk](https://github.com/MickLesk) ([#16841](https://github.com/community-scripts/ProxmoxVE/pull/16841))
- droppedneedle: launch via automatic_upgrade orchestrator [@MickLesk](https://github.com/MickLesk) ([#16834](https://github.com/community-scripts/ProxmoxVE/pull/16834))
- calibre-web: point cps at explicit db path, src-layout package has no default anymore [@MickLesk](https://github.com/MickLesk) ([#16832](https://github.com/community-scripts/ProxmoxVE/pull/16832))
- podman: pull portainer images before systemd unit start to avoid start timeout [@MickLesk](https://github.com/MickLesk) ([#16831](https://github.com/community-scripts/ProxmoxVE/pull/16831))
- gitea: make gitea own its home dir instead of toggling group perms [@MickLesk](https://github.com/MickLesk) ([#16830](https://github.com/community-scripts/ProxmoxVE/pull/16830))
- pve-ups: fix: add pip binary [@CrazyWolf13](https://github.com/CrazyWolf13) ([#16820](https://github.com/community-scripts/ProxmoxVE/pull/16820))
- #### 🔧 Refactor
- Refactor: Calibre-Web database creation [@MickLesk](https://github.com/MickLesk) ([#16842](https://github.com/community-scripts/ProxmoxVE/pull/16842))
- mastodon: read .ruby-version dynamically instead of hardcoding 4.0.5 [@MickLesk](https://github.com/MickLesk) ([#16829](https://github.com/community-scripts/ProxmoxVE/pull/16829))
+6 -6
View File
@@ -9,13 +9,13 @@ For detailed coding standards and full documentation, visit **[community-scripts
## How Can I Help?
> [!IMPORTANT]
> **New scripts** must always be submitted to [DevScripts](https://github.com/community-scripts/DevScripts) first — not to this repository.
> **New scripts** must always be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) first — not to this repository.
> PRs with new scripts opened directly against ProxmoxVE **will be closed without review**.
> **Bug fixes, improvements, and features for existing scripts** go here (ProxmoxVE).
| I want to… | Where to go |
| :------------------------------------------ | :------------------------------------------------------------------------------------------- |
| **Add a brand-new script** | [DevScripts](https://github.com/community-scripts/DevScripts) — testing repo for new scripts |
| **Add a brand-new script** | [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) — testing repo for new scripts |
| **Fix a bug or improve an existing script** | This repo (ProxmoxVE) — open a PR here |
| **Add a feature to an existing script** | This repo (ProxmoxVE) — open a PR here |
| Report a bug or broken script | [Open an Issue](https://github.com/community-scripts/ProxmoxVE/issues) |
@@ -55,13 +55,13 @@ Use existing scripts in [`ct/`](ct/) and [`install/`](install/) as reference. Fu
New scripts are **not accepted directly in this repository**. The workflow is:
1. Fork [DevScripts](https://github.com/community-scripts/DevScripts) and clone it
1. Fork [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) and clone it
2. Create a branch: `git switch -c feat/myapp`
3. Write your two script files:
- `ct/myapp.sh`
- `install/myapp-install.sh`
4. Test thoroughly in DevScripts — run the script against a real Proxmox instance
5. Open a PR in **DevScripts** for review and testing
4. Test thoroughly in ProxmoxVED — run the script against a real Proxmox instance
5. Open a PR in **ProxmoxVED** for review and testing
6. Once accepted and verified there, the script will be promoted to ProxmoxVE by maintainers
Follow the coding standards at [community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution).
@@ -193,5 +193,5 @@ create` still ran and the container was still built.
- **Website metadata** (name, description, logo, tags) is managed via the website — use the "Report Issue" link on any script page to request changes. Do not submit metadata changes via repo files.
- **JSON files** in `json/` define script properties used by the website. See existing files for structure reference.
- Keep PRs small and focused. One fix or feature per PR is ideal.
- PRs with **new scripts** opened against ProxmoxVE will be closed — submit them to [DevScripts](https://github.com/community-scripts/DevScripts) instead.
- PRs with **new scripts** opened against ProxmoxVE will be closed — submit them to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) instead.
- PRs that fail CI checks will not be merged.
+2 -2
View File
@@ -93,7 +93,7 @@ This project runs on community contributions. Whether you want to write new scri
| I want to… | Go here |
| ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Add a **new** script | [DevScripts](https://github.com/community-scripts/DevScripts) — new scripts are tested here first |
| Add a **new** script | [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) — new scripts are tested here first |
| Fix or improve an **existing** script | [Contributing Guidelines](CONTRIBUTING.md) — open a PR in this repo |
| Report a bug or broken script | [Issues](https://github.com/community-scripts/ProxmoxVE/issues) |
| Request a new script or feature | [Discussions](https://github.com/community-scripts/ProxmoxVE/discussions) |
@@ -102,7 +102,7 @@ This project runs on community contributions. Whether you want to write new scri
### Before you open a PR
- **New scripts go to [DevScripts](https://github.com/community-scripts/DevScripts), not here.** PRs with new scripts opened directly against this repo will be closed.
- **New scripts go to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED), not here.** PRs with new scripts opened directly against this repo will be closed.
- Bug fixes and improvements to existing scripts belong in this repo — read the [Contributing Guidelines](CONTRIBUTING.md) first.
- Keep PRs focused. One fix or feature per PR.
- Document what your script installs and any non-obvious decisions in the corresponding JSON metadata file.
+1 -1
View File
@@ -58,7 +58,7 @@ function update_script() {
msg_info "Updating Python Environment (${AUDIOMUSE_BACKEND})"
cd /opt/audiomuse-ai
$STD uv venv --clear --seed --python "$PY_VERSION" /opt/audiomuse-ai/.venv
$STD uv venv --seed --python "$PY_VERSION" /opt/audiomuse-ai/.venv
$STD uv pip install --python /opt/audiomuse-ai/.venv \
-r "/opt/audiomuse-ai/requirements/${REQ_COMMON}" \
-r "/opt/audiomuse-ai/requirements/${REQ_ACCEL}"
+1 -6
View File
@@ -48,12 +48,7 @@ function update_script() {
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
$STD pnpm install --frozen-lockfile
# vite's bundler (rolldown) crashes at random on some builds, rolldown#10860.
for attempt in 1 2 3; do
$STD pnpm --filter client run build-only && break
[[ $attempt -eq 3 ]] && { msg_error "Client build failed three times"; exit 1; }
msg_warn "Client build failed (attempt $attempt), retrying"
done
$STD pnpm --filter client run build-only
$STD pnpm --filter server run build
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
mkdir -p /opt/bookorbit/server/migrations
+1 -1
View File
@@ -39,7 +39,7 @@ function update_script() {
create_backup /opt/cliproxyapi/config.yaml
CLEAN_INSTALL=1 CLEAN_INSTALL_KEEP="plugins data" fetch_and_deploy_gh_release "cliproxyapi" "router-for-me/CLIProxyAPI" "prebuild" "latest" "/opt/cliproxyapi" "CLIProxyAPI_*_linux_$(arch_resolve "amd64" "aarch64").tar.gz"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cliproxyapi" "router-for-me/CLIProxyAPI" "prebuild" "latest" "/opt/cliproxyapi" "CLIProxyAPI_*_linux_$(arch_resolve "amd64" "aarch64").tar.gz"
restore_backup
+14 -21
View File
@@ -36,35 +36,28 @@ function update_script() {
exit
fi
if grep -q 'X-Accel-Mapping' /etc/nginx/sites-available/discourse 2>/dev/null; then
msg_info "Fixing stylesheet delivery in Nginx"
sed -i '/X-Accel-Mapping/d' /etc/nginx/sites-available/discourse
$STD systemctl reload nginx
msg_ok "Fixed stylesheet delivery in Nginx"
fi
msg_info "Stopping Service"
systemctl stop discourse
msg_ok "Stopped Service"
msg_info "Stopping Services"
systemctl stop discourse discourse-sidekiq
msg_ok "Stopped Services"
create_backup /opt/discourse/.env
msg_info "Updating Discourse"
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
cd /opt/discourse
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
set -a
source /opt/discourse/.env
set +a
$STD git pull origin main
$STD bundle install
$STD pnpm install
git pull origin main
$STD bundle install --deployment --without test development
$STD yarn install
$STD runuser -u postgres -- psql -d discourse -c "CREATE EXTENSION IF NOT EXISTS vector;"
$STD bundle exec rails db:migrate
$STD bundle exec rails assets:precompile
$STD bundle exec rails db:migrate
msg_ok "Updated Discourse"
msg_info "Starting Services"
systemctl start discourse discourse-sidekiq
msg_ok "Started Services"
restore_backup
msg_info "Starting Service"
systemctl start discourse
msg_ok "Started Service"
msg_ok "Updated successfully!"
exit
}
-12
View File
@@ -85,18 +85,6 @@ EOF
msg_ok "Started Services"
msg_ok "Updated successfully!"
fi
# v2.3.0's Debian build ships a musl better-sqlite3 (homarr-labs/homarr#7097).
mapfile -t MUSL_MODULES < <(find /opt/homarr -name better_sqlite3.node -exec grep -aqE "ld-musl|libc\.musl" {} \; -print)
if ((${#MUSL_MODULES[@]})); then
msg_info "Replacing musl better-sqlite3 build"
BSQ_VERSION=$(jq -r .version /opt/homarr/node_modules/better-sqlite3/package.json)
curl_with_retry "https://github.com/WiseLibs/better-sqlite3/releases/download/v${BSQ_VERSION}/better-sqlite3-v${BSQ_VERSION}-node-v$(node -p process.versions.modules)-linux-$(arch_resolve "x64" "arm64").tar.gz" /tmp/better-sqlite3.tar.gz
tar -xzf /tmp/better-sqlite3.tar.gz -C /tmp build/Release/better_sqlite3.node
for module in "${MUSL_MODULES[@]}"; do cp /tmp/build/Release/better_sqlite3.node "$module"; done
rm -rf /tmp/better-sqlite3.tar.gz /tmp/build
systemctl restart homarr
msg_ok "Replaced musl better-sqlite3 build"
fi
exit
}
+29 -23
View File
@@ -77,17 +77,14 @@ EOF
BASE_DIR=${STAGING_DIR}/base-images
SOURCE_DIR=${STAGING_DIR}/image-source
cd /tmp
RELEASE="v3.3.0"
if [[ -f ~/.intel_version ]]; then
INTEL_SRC="https://raw.githubusercontent.com/immich-app/immich/${RELEASE}/machine-learning"
curl -fsSL "${INTEL_SRC}/scripts/install-intel-runtime.sh" -o ./intel-runtime 2>/dev/null ||
curl_with_retry "${INTEL_SRC}/Dockerfile" "./intel-runtime"
readarray -t INTEL_URLS < <(grep -oE 'https://github\.com/intel/[^[:space:]]+\.deb' ./intel-runtime)
INTEL_RELEASE="$(grep -oE 'intel-opencl-icd_[^_]+' ./intel-runtime | cut -d_ -f2)" || true
rm -f ./intel-runtime
if [[ -z "$INTEL_RELEASE" ]]; then
msg_warn "No Intel runtime found for Immich ${RELEASE}, keeping the installed one"
elif [[ "$INTEL_RELEASE" != "$(cat ~/.intel_version)" ]]; then
curl_with_retry "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/machine-learning/Dockerfile" "Dockerfile"
readarray -t INTEL_URLS < <(
sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $3}'
sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}'
)
INTEL_RELEASE="$(grep "intel-opencl-icd_" ./Dockerfile | awk -F '_' '{print $2}')"
if [[ "$INTEL_RELEASE" != "$(cat ~/.intel_version)" ]]; then
msg_info "Updating Intel OpenVINO dependencies"
for url in "${INTEL_URLS[@]}"; do
curl_with_retry "$url" "$(basename "$url")"
@@ -99,6 +96,7 @@ EOF
rm ./*.deb
$STD apt-mark hold libigdgmm12
dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version
rm -f ./Dockerfile
msg_ok "Updated Intel OpenVINO dependencies"
fi
fi
@@ -114,6 +112,7 @@ EOF
msg_ok "Image-processing libraries up to date"
fi
RELEASE="v3.2.4"
if check_for_gh_release "Immich" "immich-app/immich" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
if [[ $(cat ~/.immich) > "2.5.1" ]]; then
msg_info "Enabling Maintenance Mode"
@@ -274,13 +273,12 @@ EOF
msg_info "Updating Intel OpenVINO machine-learning"
for attempt in $(seq 1 3); do
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
done
patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-$(arch_resolve "x86_64" "aarch64")-linux-gnu.so"
msg_ok "Updated Intel OpenVINO machine-learning"
else
ML_PYTHON="python3.13"
ML_PYTHON="python3.11"
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
for attempt in $(seq 1 3); do
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
@@ -290,8 +288,7 @@ EOF
msg_info "Updating machine-learning"
for attempt in $(seq 1 3); do
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
done
msg_ok "Updated machine-learning"
fi
@@ -335,10 +332,6 @@ EOF
sed -i -e '$a\' "$INSTALL_DIR"/.env
echo "IMMICH_HELMET_FILE=true" >>"$INSTALL_DIR"/.env
fi
if ! grep -q 'MODEL_REVISION' "$INSTALL_DIR"/.env; then
sed -i -e '$a\' "$INSTALL_DIR"/.env
echo "MACHINE_LEARNING_MODEL_REVISION=v2" >>"$INSTALL_DIR"/.env
fi
if grep -q 'ExecStart=/usr/bin/node' /etc/systemd/system/immich-web.service; then
sed -i '/^EnvironmentFile=/d' /etc/systemd/system/immich-web.service
@@ -347,12 +340,25 @@ EOF
fi
# MickLesk temporary patch for HEIC thumbnail gen
msg_info "Patching media.repository.js"
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
if grep -qF "(0, sharp_1.default)(input).metadata()" "$MEDIA_REPO_JS" 2>/dev/null; then
msg_info "Patching media.repository.js"
sed -i '0,/(0, sharp_1\.default)(input)\.metadata()/s//(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()/' "$MEDIA_REPO_JS"
msg_ok "Patched media.repository.js"
if [[ -f "$MEDIA_REPO_JS" ]]; then
python3 - <<'PY'
from pathlib import Path
p = Path('/opt/immich/app/dist/repositories/media.repository.js')
s = p.read_text()
old = "(0, sharp_1.default)(input).metadata()"
new = "(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()"
if new in s:
print('hotfix already there')
elif old in s:
p.write_text(s.replace(old, new, 1))
print('hotfix applied')
else:
print('pattern not found, skipped')
PY
fi
msg_ok "Patched media.repository.js"
# chown excluding upload dir contents (may be a mount with restricted permissions)
chown immich:immich "$INSTALL_DIR"
+1 -1
View File
@@ -38,7 +38,7 @@ function update_script() {
create_backup /opt/jotty/.env /opt/jotty/data /opt/jotty/config
NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs
NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "latest" "/opt/jotty" "jotty_*_prebuild.tar.gz"
restore_backup
+1 -30
View File
@@ -31,9 +31,8 @@ function update_script() {
exit
fi
RELEASE="v8.1.0"
RELEASE="v7.4.0"
if check_for_gh_release "OpenCloud" "opencloud-eu/opencloud" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
OLD_VERSION="$(cat ~/.opencloud 2>/dev/null)"
msg_info "Stopping services"
systemctl stop opencloud opencloud-wopi
msg_ok "Stopped services"
@@ -71,34 +70,6 @@ function update_script() {
msg_info "Starting services"
systemctl start opencloud opencloud-wopi
msg_ok "Started services"
if [[ -z "$OLD_VERSION" || "${OLD_VERSION#v}" =~ ^[0-7]\. ]]; then
# The index CLI cancels the rebuild when interrupted, so it runs as its own unit and the
# update only waits for it. Restart covers a search service that is still starting.
msg_info "Rebuilding search index (safe to interrupt, it continues in the background)"
REINDEX_START="$(date '+%Y-%m-%d %H:%M:%S')"
systemctl reset-failed opencloud-reindex &>/dev/null || true
$STD systemd-run --unit=opencloud-reindex --uid=opencloud --gid=opencloud \
-p EnvironmentFile=/etc/opencloud/opencloud.env -p Restart=on-failure -p RestartSec=15 \
-p StartLimitIntervalSec=900 -p StartLimitBurst=20 \
/usr/bin/opencloud search index --all-spaces --force-rescan --insecure
while [[ "$(systemctl show -p ActiveState --value opencloud-reindex)" =~ ^(active|activating)$ ]]; do
sleep 10
done
if [[ "$(systemctl show -p ActiveState --value opencloud-reindex)" == "failed" ]]; then
msg_ok "Stopped waiting for the search index rebuild"
msg_warn "The rebuild did not complete, see: journalctl -u opencloud-reindex"
msg_warn "Retry with: systemctl reset-failed opencloud-reindex; systemd-run --unit=opencloud-reindex \
--uid=opencloud --gid=opencloud -p EnvironmentFile=/etc/opencloud/opencloud.env \
/usr/bin/opencloud search index --all-spaces --force-rescan --insecure"
else
msg_ok "Rebuilt search index"
if journalctl -u opencloud-reindex --since "$REINDEX_START" --no-pager | grep -qE '/[0-9]+ ERROR'; then
msg_warn "Some spaces could not be indexed, see: journalctl -u opencloud-reindex"
fi
msg_warn "Once search finds older files, remove the old index: rm -rf /var/lib/opencloud/search/bleve"
fi
fi
msg_ok "Updated successfully"
fi
exit
+4 -3
View File
@@ -7,6 +7,7 @@ source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_
# Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin
APP="Pangolin"
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.23.0}"
var_tags="${var_tags:-proxy}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
@@ -37,7 +38,7 @@ function update_script() {
ensure_dependencies build-essential python3
if ! command -v psql &>/dev/null; then
msg_error "This installation uses SQLite and cannot be upgraded to latest Pangolin."
msg_error "This installation uses SQLite and cannot be upgraded to Pangolin ${PANGOLIN_VERSION}."
echo -e "${INFO}${YW}Starting with Pangolin 1.20.0, PostgreSQL is required as the database backend.${CL}"
echo -e "${INFO}${YW}An automatic migration of your existing SQLite data is not supported.${CL}"
echo -e "${INFO}${YW}Please create a new LXC with the Pangolin install script, which sets up PostgreSQL automatically.${CL}"
@@ -47,7 +48,7 @@ function update_script() {
NODE_VERSION="24" setup_nodejs
if check_for_gh_release "pangolin" "fosrl/pangolin"; then
if check_for_gh_release "pangolin" "fosrl/pangolin" "$PANGOLIN_VERSION" "Pinned to a tested release because Pangolin's schema changes have repeatedly broken unattended updates. To try a newer version at your own risk, run: 'export PANGOLIN_VERSION=<tag>' and re-run update. If it breaks, please open an issue at https://github.com/community-scripts/ProxmoxVE/issues with the error log."; then
msg_info "Stopping Service"
systemctl stop pangolin
systemctl stop gerbil
@@ -56,7 +57,7 @@ function update_script() {
DB_URL=$(sed -n 's/.*connection_string: "\(.*\)".*/\1/p' /opt/pangolin/config/config.yml)
create_backup /opt/pangolin/config
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
msg_info "Updating Pangolin"
+1 -29
View File
@@ -15,8 +15,6 @@ var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-yes}"
var_unprivileged="${var_unprivileged:-1}"
export var_paperclip_user="${var_paperclip_user:-}"
export var_paperclip_pass="${var_paperclip_pass:-}"
header_info "$APP"
variables
@@ -61,35 +59,9 @@ function update_script() {
@openai/codex@latest
msg_ok "Updated Agent CLIs"
# Claude Code refuses --dangerously-skip-permissions as root; migrate existing installs to a dedicated user
PAPERCLIP_USER=$(sed -n 's/^User=//p' /etc/systemd/system/paperclip.service)
if [[ -z "$PAPERCLIP_USER" || "$PAPERCLIP_USER" == "root" ]]; then
PAPERCLIP_USER="${var_paperclip_user:-paperclip}"
if [[ "$PAPERCLIP_USER" == "root" || ! "$PAPERCLIP_USER" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]; then
msg_error "Invalid var_paperclip_user '${PAPERCLIP_USER}' (must be a non-root lowercase Linux username)"
exit 1
fi
PAPERCLIP_USER_HOME="/home/${PAPERCLIP_USER}"
id -u "$PAPERCLIP_USER" &>/dev/null || useradd -m -d "$PAPERCLIP_USER_HOME" -s /bin/bash "$PAPERCLIP_USER"
passwd -S "$PAPERCLIP_USER" 2>/dev/null | grep -q " P " || passwd -l "$PAPERCLIP_USER" &>/dev/null
mkdir -p "${PAPERCLIP_USER_HOME}/.claude" "${PAPERCLIP_USER_HOME}/.codex"
[[ -d /root/.claude ]] && cp -a /root/.claude/. "${PAPERCLIP_USER_HOME}/.claude/"
[[ -d /root/.codex ]] && cp -a /root/.codex/. "${PAPERCLIP_USER_HOME}/.codex/"
sed -i \
-e "s|^User=.*|User=${PAPERCLIP_USER}\nGroup=${PAPERCLIP_USER}|" \
-e "s|^Environment=HOME=.*|Environment=HOME=${PAPERCLIP_USER_HOME}|" \
-e "s|^Environment=CODEX_HOME=.*|Environment=CODEX_HOME=${PAPERCLIP_USER_HOME}/.codex|" \
-e "s|/root/.local/bin|${PAPERCLIP_USER_HOME}/.local/bin|" \
/etc/systemd/system/paperclip.service
systemctl daemon-reload
fi
PAPERCLIP_USER_HOME=$(getent passwd "$PAPERCLIP_USER" | cut -d: -f6)
chmod 600 /opt/paperclip-ai/.env
chown -R "${PAPERCLIP_USER}:${PAPERCLIP_USER}" /opt/paperclip-ai /opt/paperclip-data "$PAPERCLIP_USER_HOME"
msg_info "Running Database Migrations"
set -a && source /opt/paperclip-ai/.env && set +a
$STD runuser -u "$PAPERCLIP_USER" -- env HOME="$PAPERCLIP_USER_HOME" bash -c 'cd /opt/paperclip-ai && pnpm db:migrate'
$STD pnpm db:migrate
msg_ok "Ran Database Migrations"
msg_info "Starting Service"
-106
View File
@@ -1,106 +0,0 @@
#!/usr/bin/env bash
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/jez500/pricebuddy
APP="PriceBuddy"
var_tags="${var_tags:-shopping;price-tracker}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-3072}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-no}" # the bundled scraper runs Google Chrome, which is amd64-only
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/pricebuddy ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "pricebuddy" "jez500/pricebuddy"; then
msg_info "Stopping PriceBuddy Worker"
systemctl stop pricebuddy-worker
msg_ok "Stopped PriceBuddy Worker"
setup_composer
NODE_VERSION="22" setup_nodejs
create_backup /opt/pricebuddy/.env /opt/pricebuddy/storage
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pricebuddy" "jez500/pricebuddy" "tarball"
restore_backup
msg_info "Updating PriceBuddy"
cd /opt/pricebuddy
sed -i "s/^APP_VERSION=.*/APP_VERSION=$(cat ~/.pricebuddy)/" .env
$STD composer install --no-dev --optimize-autoloader --no-interaction
$STD npm install
$STD npm run build
$STD php artisan storage:link
$STD php artisan migrate --force
$STD php artisan optimize
$STD php artisan icons:cache
$STD php artisan buddy:regenerate-price-cache
chown -R www-data:www-data /opt/pricebuddy
msg_ok "Updated PriceBuddy"
msg_info "Starting PriceBuddy Worker"
systemctl start pricebuddy-worker
msg_ok "Started PriceBuddy Worker"
msg_ok "Updated successfully!"
fi
if check_for_gh_release "seleniumbase-scrapper" "jez500/seleniumbase-scrapper"; then
msg_info "Stopping SeleniumBase Scrapper"
systemctl stop seleniumbase-scrapper
msg_ok "Stopped SeleniumBase Scrapper"
msg_info "Updating Google Chrome"
apt_update_safe
upgrade_packages_with_retry google-chrome-stable
msg_ok "Updated Google Chrome"
PYTHON_VERSION="3.12" setup_uv
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "seleniumbase-scrapper" "jez500/seleniumbase-scrapper" "tarball"
msg_info "Updating SeleniumBase Scrapper"
$STD uv venv --python 3.12 /opt/seleniumbase-scrapper/.venv
$STD uv pip install --python /opt/seleniumbase-scrapper/.venv/bin/python \
"seleniumbase==$(sed -n 's/^ARG SELENIUMBASE_VERSION=v//p' /opt/seleniumbase-scrapper/Dockerfile)" \
flask \
beautifulsoup4
$STD /opt/seleniumbase-scrapper/.venv/bin/seleniumbase get chromedriver
msg_ok "Updated SeleniumBase Scrapper"
msg_info "Starting SeleniumBase Scrapper"
systemctl start seleniumbase-scrapper
msg_ok "Started SeleniumBase Scrapper"
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW}Access it using the following URL:${CL}"
echo -e "${GATEWAY}${BGN}http://${IP}${CL}"
echo -e "${INFO}${YW}Log in as admin@example.com - the password is APP_USER_PASSWORD in /opt/pricebuddy/.env${CL}"
-1
View File
@@ -30,7 +30,6 @@ function update_script() {
exit
fi
JAVA_VERSION="25" setup_java
if check_for_gh_release "thingsboard" "thingsboard/thingsboard"; then
msg_info "Stopping Service"
systemctl stop thingsboard
+3
View File
@@ -122,6 +122,9 @@ EOF
$STD pnpm install --frozen-lockfile --force
$STD pnpm turbo telemetry disable
$STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web
if BASEMAP_BUILD=$(curl -fsSL https://build-metadata.protomaps.dev/builds.json | jq -r 'map(.key) | sort | last | rtrimstr(".pmtiles")'); then
sed -i "s/^BUILD=.*/BUILD=${BASEMAP_BUILD}/" scripts/fetch-basemap.sh
fi
$STD ./scripts/fetch-basemap.sh || msg_warn "Basemap download failed, keeping the existing map"
[[ -d /opt/tracearr/data ]] && mv /opt/tracearr/data /opt/tracearr.data
rm -rf /opt/tracearr
+1 -1
View File
@@ -3,7 +3,7 @@ _cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../cor
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Joost van den Berg (montagneId)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# License: MIT | https://github.com/montagneid/ProxmoxVED/raw/main/LICENSE
# Source: https://github.com/umbraco/Umbraco-CMS
APP="Umbraco"
+1 -18
View File
@@ -148,23 +148,6 @@ EOF
msg_ok "Migrated wanderer services"
fi
if ! grep -q '^POCKETBASE_PROXY_SECRET=' /opt/wanderer/.env; then
msg_info "Adding POCKETBASE_PROXY_SECRET"
echo "POCKETBASE_PROXY_SECRET=$(openssl rand -hex 32)" >>/opt/wanderer/.env
systemctl restart wanderer-web
msg_ok "Added POCKETBASE_PROXY_SECRET"
fi
if [[ -f /opt/wanderer_data/plugins/plugin.json ]]; then
msg_info "Reinstalling wanderer plugins, one directory each"
find /opt/wanderer_data/plugins -maxdepth 1 -type f -delete
for plugin in hammerhead komoot strava; do
rm -f ~/.wanderer-plugin-${plugin}
fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "latest" "/opt/wanderer_data/plugins/${plugin}" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}"
done
msg_ok "Reinstalled wanderer plugins"
fi
if check_for_gh_release "wanderer" "open-wanderer/wanderer"; then
msg_info "Stopping service"
systemctl stop wanderer-web
@@ -187,7 +170,7 @@ EOF
[[ -e /opt/wanderer/db/data/plugins ]] || ln -sfn /opt/wanderer_data/plugins /opt/wanderer/db/data/plugins
msg_info "Installing wanderer plugins"
for plugin in hammerhead komoot strava; do
fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "${CHECK_UPDATE_RELEASE:-latest}" "/opt/wanderer_data/plugins/${plugin}" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}"
fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "${CHECK_UPDATE_RELEASE:-latest}" "/opt/wanderer_data/plugins" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}"
done
msg_ok "Installed wanderer plugins"
msg_ok "Updated wanderer"
-14
View File
@@ -31,20 +31,6 @@ function update_script() {
exit
fi
if ! grep -q "@private" /etc/caddy/Caddyfile; then
msg_info "Blocking direct access to webtrees data"
cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak
sed -i '\|root \* /opt/webtrees|a\ @private path /app/* /data/* /modules_v4/* /resources/* /vendor/* /.*\n respond @private 403' /etc/caddy/Caddyfile
if grep -q "@private" /etc/caddy/Caddyfile && caddy validate --config /etc/caddy/Caddyfile &>/dev/null; then
rm -f /etc/caddy/Caddyfile.bak
systemctl reload-or-restart caddy
msg_ok "Blocked direct access to webtrees data"
else
mv /etc/caddy/Caddyfile.bak /etc/caddy/Caddyfile
msg_warn "Could not patch /etc/caddy/Caddyfile - deny /data/ there by hand"
fi
fi
if check_for_gh_release "webtrees" "fisharebest/webtrees"; then
msg_info "Stopping Service"
PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
+1 -1
View File
@@ -30,7 +30,7 @@ function update_script() {
exit
fi
NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
NODE_VERSION="24" NODE_MODULE="yarn,node-gyp" setup_nodejs
if check_for_gh_release "wikijs" "requarks/wiki"; then
msg_info "Verifying whether ${APP}' new release is v3.x+ and current install uses SQLite."
+1 -6
View File
@@ -33,12 +33,7 @@ PNPM_VERSION=$(jq -r '.packageManager | ltrimstr("pnpm@")' /opt/bookorbit/packag
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
$STD pnpm install --frozen-lockfile
# vite's bundler (rolldown) crashes at random on some builds, rolldown#10860.
for attempt in 1 2 3; do
$STD pnpm --filter client run build-only && break
[[ $attempt -eq 3 ]] && { msg_error "Client build failed three times"; exit 1; }
msg_warn "Client build failed (attempt $attempt), retrying"
done
$STD pnpm --filter client run build-only
$STD pnpm --filter server run build
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
mkdir -p /opt/bookorbit/server/migrations
+1
View File
@@ -203,6 +203,7 @@ server {
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Accel-Mapping /opt/discourse/public/=/downloads/;
}
}
EOF
+1 -1
View File
@@ -236,7 +236,7 @@ $STD bash /opt/frigate/.devcontainer/initialize.sh
$STD make version
cd /opt/frigate/web
$STD npm install
NODE_OPTIONS="--max-old-space-size=4096" $STD npm run build
$STD npm run build
mv /opt/frigate/web/dist/BASE_PATH/monacoeditorwork/* /opt/frigate/web/dist/assets/
rm -rf /opt/frigate/web/dist/BASE_PATH
cp -r /opt/frigate/web/dist/* /opt/frigate/web/
+1 -3
View File
@@ -45,13 +45,11 @@ if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then
fi
msg_info "Installing Hermes Agent"
curl -fsSL https://hermes-agent.nousresearch.com/install.sh -o /tmp/hermes-install.sh
$STD setsid --wait bash -c '
set -a; source /etc/default/hermes; set +a
export npm_config_yes=true
bash /tmp/hermes-install.sh --skip-setup --hermes-home /home/hermes/.hermes --dir /home/hermes/.hermes/hermes-agent
bash <(curl -fsSL https://hermes-agent.nousresearch.com/install.sh) --skip-setup --hermes-home /home/hermes/.hermes --dir /home/hermes/.hermes/hermes-agent
'
rm -f /tmp/hermes-install.sh
chown -R hermes:hermes /home/hermes
chmod 750 /home/hermes
chmod 700 /home/hermes/.hermes
-12
View File
@@ -25,18 +25,6 @@ NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/
setup_nodejs
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-debian-$(arch_resolve).tar.gz"
# v2.3.0's Debian build ships a musl better-sqlite3 (homarr-labs/homarr#7097).
mapfile -t MUSL_MODULES < <(find /opt/homarr -name better_sqlite3.node -exec grep -aqE "ld-musl|libc\.musl" {} \; -print)
if ((${#MUSL_MODULES[@]})); then
msg_info "Replacing musl better-sqlite3 build"
BSQ_VERSION=$(jq -r .version /opt/homarr/node_modules/better-sqlite3/package.json)
curl_with_retry "https://github.com/WiseLibs/better-sqlite3/releases/download/v${BSQ_VERSION}/better-sqlite3-v${BSQ_VERSION}-node-v$(node -p process.versions.modules)-linux-$(arch_resolve "x64" "arm64").tar.gz" /tmp/better-sqlite3.tar.gz
tar -xzf /tmp/better-sqlite3.tar.gz -C /tmp build/Release/better_sqlite3.node
for module in "${MUSL_MODULES[@]}"; do cp /tmp/build/Release/better_sqlite3.node "$module"; done
rm -rf /tmp/better-sqlite3.tar.gz /tmp/build
msg_ok "Replaced musl better-sqlite3 build"
fi
msg_info "Installing Homarr"
mkdir -p /opt/homarr_db
touch /opt/homarr_db/db.sqlite
+26 -17
View File
@@ -13,7 +13,6 @@ setting_up_container
network_check
update_os
RELEASE="v3.3.0"
if lscpu | grep -q 'GenuineIntel'; then
echo ""
echo ""
@@ -34,11 +33,11 @@ if lscpu | grep -q 'GenuineIntel'; then
msg_info "Installing Intel OpenVINO dependencies"
tmp_dir=$(mktemp -d)
$STD pushd "$tmp_dir"
INTEL_SRC="https://raw.githubusercontent.com/immich-app/immich/${RELEASE}/machine-learning"
curl -fsSL "${INTEL_SRC}/scripts/install-intel-runtime.sh" -o ./intel-runtime 2>/dev/null ||
curl_with_retry "${INTEL_SRC}/Dockerfile" "./intel-runtime"
readarray -t INTEL_URLS < <(grep -oE 'https://github\.com/intel/[^[:space:]]+\.deb' ./intel-runtime)
rm -f ./intel-runtime
curl_with_retry "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/machine-learning/Dockerfile" "Dockerfile"
readarray -t INTEL_URLS < <(
sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $3}'
sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}'
)
for url in "${INTEL_URLS[@]}"; do
curl_with_retry "$url" "$(basename "$url")"
done
@@ -354,7 +353,7 @@ ML_DIR="${APP_DIR}/machine-learning"
GEO_DIR="${INSTALL_DIR}/geodata"
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "${RELEASE}" "$SRC_DIR"
fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "v3.2.4" "$SRC_DIR"
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs
@@ -442,13 +441,12 @@ if [[ -f ~/.openvino ]]; then
msg_info "Installing Intel OpenVINO machine-learning"
for attempt in $(seq 1 3); do
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
done
patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-$(arch_resolve "x86_64" "aarch64")-linux-gnu.so"
msg_ok "Installed Intel OpenVINO machine-learning"
else
ML_PYTHON="python3.13"
ML_PYTHON="python3.11"
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
for attempt in $(seq 1 3); do
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
@@ -458,8 +456,7 @@ else
msg_info "Installing machine-learning"
for attempt in $(seq 1 3); do
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
done
msg_ok "Installed machine-learning"
fi
@@ -490,12 +487,25 @@ ln -s "$GEO_DIR" "$APP_DIR"
msg_ok "Installed GeoNames data"
# MickLesk temporary patch for HEIC thumbnail gen
msg_info "Patching media.repository.js"
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
if grep -qF "(0, sharp_1.default)(input).metadata()" "$MEDIA_REPO_JS" 2>/dev/null; then
msg_info "Patching media.repository.js"
sed -i '0,/(0, sharp_1\.default)(input)\.metadata()/s//(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()/' "$MEDIA_REPO_JS"
msg_ok "Patched media.repository.js"
if [[ -f "$MEDIA_REPO_JS" ]]; then
python3 - <<'PY'
from pathlib import Path
p = Path('/opt/immich/app/dist/repositories/media.repository.js')
s = p.read_text()
old = "(0, sharp_1.default)(input).metadata()"
new = "(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()"
if new in s:
print('hotfix already there')
elif old in s:
p.write_text(s.replace(old, new, 1))
print('hotfix applied')
else:
print('pattern not found, skipped')
PY
fi
msg_ok "Patched media.repository.js"
mkdir -p /var/log/immich
touch /var/log/immich/{web.log,ml.log}
@@ -526,7 +536,6 @@ MACHINE_LEARNING_CACHE_FOLDER=${INSTALL_DIR}/cache
## - inference speed while reducing accuracy
## - Default is FP32
# MACHINE_LEARNING_OPENVINO_PRECISION=FP16
MACHINE_LEARNING_MODEL_REVISION=v2
IMMICH_MEDIA_LOCATION=${UPLOAD_DIR}
EOF
+1 -1
View File
@@ -13,7 +13,7 @@ setting_up_container
network_check
update_os
NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs
NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs
fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "latest" "/opt/jotty" "jotty_*_prebuild.tar.gz"
msg_info "Setup jotty"
+6 -5
View File
@@ -20,14 +20,15 @@ $STD apt install -y \
openssl \
ffmpeg \
python3 \
python3-pip \
python3-dev \
python3-numpy \
redis-server
msg_ok "Installed Dependencies"
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
PG_DB_NAME="kima" PG_DB_USER="kima" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
NODE_VERSION="24" setup_nodejs
PYTHON_VERSION="3.13" setup_uv
msg_info "Configuring Redis"
systemctl enable -q --now redis-server
@@ -36,8 +37,8 @@ msg_ok "Configured Redis"
fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball"
msg_info "Installing Python Dependencies"
$STD uv venv --python 3.13 /opt/kima-hub/venv
$STD uv pip install --no-cache --python /opt/kima-hub/venv \
export PIP_BREAK_SYSTEM_PACKAGES=1
$STD pip3 install --no-cache-dir \
tensorflow \
essentia-tensorflow \
redis \
@@ -173,7 +174,7 @@ Environment=BATCH_SIZE=10
Environment=SLEEP_INTERVAL=5
Environment=NUM_WORKERS=2
Environment=THREADS_PER_WORKER=1
ExecStart=/opt/kima-hub/venv/bin/python /opt/kima-hub/services/audio-analyzer/analyzer.py
ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer/analyzer.py
Restart=on-failure
RestartSec=10
@@ -196,7 +197,7 @@ Environment=BACKEND_URL=http://localhost:3006
Environment=MUSIC_PATH=/music
Environment=SLEEP_INTERVAL=5
Environment=NUM_WORKERS=1
ExecStart=/opt/kima-hub/venv/bin/python /opt/kima-hub/services/audio-analyzer-clap/analyzer.py
ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer-clap/analyzer.py
Restart=on-failure
RestartSec=10
+1 -1
View File
@@ -2,7 +2,7 @@
# Copyright (c) 2021-2026 community-scripts ORG
# Author: (AminGholizad)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# License: MIT | https://github.com/AminGholizad/ProxmoxVED/raw/main/LICENSE
# Source: https://github.com/PanSalut/Koffan
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
+1 -1
View File
@@ -64,7 +64,7 @@ $STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPA
echo "$COOLPASS" >~/.coolpass
msg_ok "Installed Collabora Online"
fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v8.1.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)"
fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v7.4.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)"
mv /usr/bin/OpenCloud /usr/bin/opencloud
msg_info "Configuring OpenCloud"
+4 -1
View File
@@ -22,10 +22,13 @@ msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs
PG_VERSION="17" setup_postgresql
PG_DB_NAME="pangolin" PG_DB_USER="pangolin" setup_postgresql_db
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball"
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.23.0}"
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
# Read the variable first and prompt only when it is unset, so the install can
# be supplied up front. Same convention as install/forgejo-runner-install.sh.
pango_url="${var_pangolin_url:-}"
if [[ -z "$pango_url" ]]; then
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
+9 -28
View File
@@ -46,21 +46,8 @@ msg_info "Configuring Paperclip"
PAPERCLIP_HOME="/opt/paperclip-data"
PAPERCLIP_CONFIG="${PAPERCLIP_HOME}/instances/default/config.json"
PAPERCLIP_USER="${var_paperclip_user:-paperclip}"
# Claude Code refuses --dangerously-skip-permissions as root, so run as a dedicated user
if [[ "$PAPERCLIP_USER" == "root" || ! "$PAPERCLIP_USER" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]; then
msg_error "Invalid var_paperclip_user '${PAPERCLIP_USER}' (must be a non-root lowercase Linux username)"
exit 1
fi
PAPERCLIP_USER_HOME="/home/${PAPERCLIP_USER}"
id -u "$PAPERCLIP_USER" &>/dev/null || useradd -m -d "$PAPERCLIP_USER_HOME" -s /bin/bash "$PAPERCLIP_USER"
if [[ -n "${var_paperclip_pass:-}" ]]; then
printf '%s:%s\n' "$PAPERCLIP_USER" "$var_paperclip_pass" | chpasswd
else
passwd -l "$PAPERCLIP_USER" &>/dev/null
fi
unset var_paperclip_pass
mkdir -p /opt/paperclip-data "${PAPERCLIP_USER_HOME}/.claude" "${PAPERCLIP_USER_HOME}/.codex"
mkdir -p /opt/paperclip-data
mkdir -p /root/.claude /root/.codex
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
cat <<EOF >/opt/paperclip-ai/.env
DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}
@@ -75,13 +62,11 @@ PAPERCLIP_DEPLOYMENT_EXPOSURE=private
PAPERCLIP_PUBLIC_URL=http://${LOCAL_IP}:3100
BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
EOF
chmod 600 /opt/paperclip-ai/.env
chown -R "${PAPERCLIP_USER}:${PAPERCLIP_USER}" /opt/paperclip-ai /opt/paperclip-data "$PAPERCLIP_USER_HOME"
msg_ok "Configured Paperclip"
msg_info "Running Database Migrations"
set -a && source /opt/paperclip-ai/.env && set +a
$STD runuser -u "$PAPERCLIP_USER" -- env HOME="$PAPERCLIP_USER_HOME" pnpm db:migrate
$STD pnpm db:migrate
msg_ok "Ran Database Migrations"
msg_info "Bootstrapping Paperclip"
@@ -92,8 +77,7 @@ for PAPERCLIP_ONBOARD_CMD in \
"pnpm paperclipai onboard --yes --bind lan" \
"pnpm paperclipai onboard --yes"; do
rm -f "$PAPERCLIP_ONBOARD_LOG"
setsid runuser -u "$PAPERCLIP_USER" -- env \
HOME="$PAPERCLIP_USER_HOME" \
setsid env \
PAPERCLIP_HOME="$PAPERCLIP_HOME" \
PAPERCLIP_CONFIG="$PAPERCLIP_CONFIG" \
bash -c 'cd /opt/paperclip-ai && exec "$@"' _ $PAPERCLIP_ONBOARD_CMD \
@@ -125,8 +109,7 @@ if [[ ! -f "$PAPERCLIP_CONFIG" ]]; then
fi
if grep -q 'authenticated' $PAPERCLIP_CONFIG; then
chown "${PAPERCLIP_USER}:${PAPERCLIP_USER}" /opt/paperclip-ai
runuser -u "$PAPERCLIP_USER" -- env HOME="$PAPERCLIP_USER_HOME" bash -c 'cd /opt/paperclip-ai && pnpm paperclipai auth bootstrap-ceo' >"$PAPERCLIP_BOOTSTRAP_LOG" 2>&1 || true
pnpm paperclipai auth bootstrap-ceo >"$PAPERCLIP_BOOTSTRAP_LOG" 2>&1 || true
PAPERCLIP_INVITE_URL=$(awk -F'Invite URL: ' '/Invite URL:/ {print $2; exit}' "$PAPERCLIP_BOOTSTRAP_LOG")
PAPERCLIP_INVITE_EXPIRY=$(awk -F'Expires: ' '/Expires:/ {print $2; exit}' "$PAPERCLIP_BOOTSTRAP_LOG")
if [[ -n "$PAPERCLIP_INVITE_URL" ]]; then
@@ -148,7 +131,6 @@ else
fi
rm -f "$PAPERCLIP_ONBOARD_LOG" "$PAPERCLIP_BOOTSTRAP_LOG"
msg_ok "Bootstrapped Paperclip"
echo -e "${INFO}${YW} Authenticate Claude Code and Codex as the service user: ${BGN}su - ${PAPERCLIP_USER}${CL}"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/paperclip.service
@@ -159,13 +141,12 @@ Requires=postgresql.service
[Service]
Type=simple
User=${PAPERCLIP_USER}
Group=${PAPERCLIP_USER}
User=root
WorkingDirectory=/opt/paperclip-ai
EnvironmentFile=/opt/paperclip-ai/.env
Environment=HOME=${PAPERCLIP_USER_HOME}
Environment=CODEX_HOME=${PAPERCLIP_USER_HOME}/.codex
Environment=PATH=${PAPERCLIP_USER_HOME}/.local/bin:/usr/local/bin:/usr/bin:/bin
Environment=HOME=/root
Environment=CODEX_HOME=/root/.codex
Environment=PATH=/root/.local/bin:/usr/local/bin:/usr/bin:/bin
Environment=DISABLE_AUTOUPDATER=1
ExecStart=/usr/bin/env pnpm paperclipai run
Restart=on-failure
-168
View File
@@ -1,168 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/jez500/pricebuddy
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 \
cron \
xvfb
msg_ok "Installed Dependencies"
PHP_VERSION="8.4" PHP_FPM="YES" PHP_MEMORY_LIMIT="2048M" setup_php
setup_composer
NODE_VERSION="22" setup_nodejs
setup_mariadb
MARIADB_DB_NAME="pricebuddy" MARIADB_DB_USER="pricebuddy" setup_mariadb_db
PYTHON_VERSION="3.12" setup_uv
msg_info "Installing Google Chrome"
setup_deb822_repo \
"google-chrome" \
"https://dl.google.com/linux/linux_signing_key.pub" \
"https://dl.google.com/linux/chrome/deb/" \
"stable"
$STD apt install -y google-chrome-stable
# the package adds its own .list for the same repo, which apt rejects next to the deb822 source
rm -f /etc/apt/sources.list.d/google-chrome.list
msg_ok "Installed Google Chrome"
fetch_and_deploy_gh_release "seleniumbase-scrapper" "jez500/seleniumbase-scrapper" "tarball"
msg_info "Setting up SeleniumBase Scrapper"
$STD uv venv --python 3.12 /opt/seleniumbase-scrapper/.venv
# the SeleniumBase release upstream builds and tests its image against
$STD uv pip install --python /opt/seleniumbase-scrapper/.venv/bin/python \
"seleniumbase==$(sed -n 's/^ARG SELENIUMBASE_VERSION=v//p' /opt/seleniumbase-scrapper/Dockerfile)" \
flask \
beautifulsoup4
$STD /opt/seleniumbase-scrapper/.venv/bin/seleniumbase get chromedriver
msg_ok "Set up SeleniumBase Scrapper"
fetch_and_deploy_gh_release "pricebuddy" "jez500/pricebuddy" "tarball"
msg_info "Setting up PriceBuddy"
cd /opt/pricebuddy
cat <<EOF >/opt/pricebuddy/.env
APP_NAME=PriceBuddy
APP_ENV=production
APP_KEY=base64:$(openssl rand -base64 32)
APP_DEBUG=false
APP_URL=http://${LOCAL_IP}
APP_VERSION=$(cat ~/.pricebuddy)
DB_CONNECTION=mariadb
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=pricebuddy
DB_USERNAME=pricebuddy
DB_PASSWORD=${MARIADB_DB_PASS}
SCRAPER_BASE_URL=http://127.0.0.1:3000
APP_USER_EMAIL=admin@example.com
APP_USER_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16)
EOF
$STD composer install --no-dev --optimize-autoloader --no-interaction
$STD npm install
$STD npm run build
$STD php artisan storage:link
# reads APP_USER_* for the admin via env(), so it has to run before optimize caches the config
$STD php artisan buddy:init-db
$STD php artisan optimize
$STD php artisan icons:cache
chown -R www-data:www-data /opt/pricebuddy
chmod 600 /opt/pricebuddy/.env
msg_ok "Set up PriceBuddy"
msg_info "Configuring Nginx"
PHP_SOCK=$(get_php_fpm_socket)
cat <<EOF >/etc/nginx/sites-available/pricebuddy
server {
listen 80;
server_name _;
root /opt/pricebuddy/public;
index index.php;
charset utf-8;
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php\$ {
fastcgi_pass unix:${PHP_SOCK};
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
EOF
nginx_enable_site "pricebuddy"
msg_ok "Configured Nginx"
msg_info "Creating Services"
cat <<EOF >/etc/systemd/system/seleniumbase-scrapper.service
[Unit]
Description=SeleniumBase Scrapper for PriceBuddy
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/seleniumbase-scrapper/api
Environment=API_HOST=127.0.0.1
Environment=API_PORT=3000
ExecStart=/opt/seleniumbase-scrapper/.venv/bin/python /opt/seleniumbase-scrapper/api/server.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/pricebuddy-worker.service
[Unit]
Description=PriceBuddy Queue Worker
After=network.target mariadb.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/pricebuddy
ExecStart=/usr/bin/php /opt/pricebuddy/artisan queue:work
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/cron.d/pricebuddy
* * * * * www-data cd /opt/pricebuddy && php artisan schedule:run >/dev/null 2>&1
EOF
systemctl enable -q --now seleniumbase-scrapper pricebuddy-worker
msg_ok "Created Services"
motd_ssh
customize
cleanup_lxc
+1 -1
View File
@@ -20,7 +20,7 @@ $STD apt install -y \
fonts-dejavu-core
msg_ok "Installed Dependencies"
JAVA_VERSION="25" setup_java
JAVA_VERSION="17" setup_java
PG_VERSION="16" setup_postgresql
PG_DB_NAME="thingsboard_db" PG_DB_USER="thingsboard" setup_postgresql_db
fetch_and_deploy_gh_release "thingsboard" "thingsboard/thingsboard" "binary" "latest" "/tmp" "thingsboard-*.deb"
+3
View File
@@ -67,6 +67,9 @@ cd /opt/tracearr.build
$STD pnpm install --frozen-lockfile --force
$STD pnpm turbo telemetry disable
$STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web
if BASEMAP_BUILD=$(curl -fsSL https://build-metadata.protomaps.dev/builds.json | jq -r 'map(.key) | sort | last | rtrimstr(".pmtiles")'); then
sed -i "s/^BUILD=.*/BUILD=${BASEMAP_BUILD}/" scripts/fetch-basemap.sh
fi
$STD ./scripts/fetch-basemap.sh || msg_warn "Basemap download failed, the map will show without base tiles"
mkdir -p /opt/tracearr/{packages/shared,packages/emails,apps/server,apps/web,apps/server/src/db}
cp -rf package.json /opt/tracearr/
+1 -1
View File
@@ -2,7 +2,7 @@
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Joost van den Berg (montagneId)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# License: MIT | https://github.com/montagneid/ProxmoxVED/raw/main/LICENSE
# Source: https://github.com/umbraco/Umbraco-CMS
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
+1 -2
View File
@@ -33,7 +33,7 @@ msg_ok "Installed wanderer"
msg_info "Installing wanderer plugins"
for plugin in hammerhead komoot strava; do
fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "latest" "/opt/wanderer_data/plugins/${plugin}" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}"
fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "latest" "/opt/wanderer_data/plugins" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}"
done
msg_ok "Installed wanderer plugins"
@@ -49,7 +49,6 @@ PB_URL=${LOCAL_IP}:8090
PUBLIC_POCKETBASE_URL=http://${LOCAL_IP}:8090
PUBLIC_VALHALLA_URL=https://valhalla1.openstreetmap.de
POCKETBASE_ENCRYPTION_KEY=${POCKETBASE_KEY}
POCKETBASE_PROXY_SECRET=$(openssl rand -hex 32)
PB_DB_LOCATION=/opt/wanderer_data/pb_data
MEILI_DB_PATH=/opt/wanderer_data/meili_data
EOF
-3
View File
@@ -36,9 +36,6 @@ PHP_SOCK=$(get_php_fpm_socket)
cat <<EOF >/etc/caddy/Caddyfile
:80 {
root * /opt/webtrees
# Caddy ignores data/.htaccess; media must go through webtrees so its privacy rules apply.
@private path /app/* /data/* /modules_v4/* /resources/* /vendor/* /.*
respond @private 403
php_fastcgi unix/${PHP_SOCK}
file_server
encode gzip
+1 -1
View File
@@ -17,7 +17,7 @@ msg_info "Installing Dependencies"
$STD apt install -y git
msg_ok "Installed Dependencies"
NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
NODE_VERSION="24" NODE_MODULE="yarn,node-gyp" setup_nodejs
PG_VERSION="17" setup_postgresql
PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" PG_DB_EXTENSIONS="pg_trgm" setup_postgresql_db
fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
+2 -3
View File
@@ -110,9 +110,8 @@ while true; do
pct start "$instance" >/dev/null 2>&1
continue
fi
# Read the IP from the host side; pct exec depends on tools inside the
# guest, which minimal/OCI images may lack.
IP=$(lxc-info -n "$instance" -iH 2>/dev/null | awk '/^[0-9.]+$/ && !/^(127|169\.254)\./ {print; exit}')
# Not every container names its interface eth0.
IP=$(pct exec "$instance" -- ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n1)
if [ -z "$IP" ] || ! ping -c 1 -W 2 "$IP" >/dev/null 2>&1; then
echo "$(date): CT $instance is not responding, restarting..."
pct stop "$instance" >/dev/null 2>&1
+522 -55
View File
@@ -1,42 +1,218 @@
#!/usr/bin/env bash
#!/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
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
function header_info {
clear
cat <<"EOF"
___ __ __ _ _ ____ ___
/ | __________/ /_ / / (_)___ __ ___ __ | | / / |/ /
/ /| | / ___/ ___/ __ \ / / / / __ \/ / / / |/_/ | | / / /|_/ /
/ ___ |/ / / /__/ / / / / /___/ / / / / /_/ /> < | |/ / / / /
/_/ |_/_/ \___/_/ /_/ /_____/_/_/ /_/\__,_/_/|_| |___/_/ /_/
EOF
}
header_info
echo -e "\n Loading..."
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
APP="Arch Linux"
APP_TYPE="vm"
NSAPP="archlinux-vm"
var_os="arch-linux"
var_version="n.d."
THIN="discard=on,ssd=1,"
YW=$(echo "\033[33m")
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")
header_info
echo -e "\n Loading..."
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
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
post_update_to_api "failed" "${exit_code}"
echo -e "\n$error_message\n"
cleanup_vmid
}
vm_require_arch amd64
function get_valid_nextid() {
local try_id
try_id=$(pvesh get /cluster/nextid)
while true; do
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
try_id=$((try_id + 1))
continue
fi
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
try_id=$((try_id + 1))
continue
fi
break
done
echo "$try_id"
}
function cleanup_vmid() {
if qm status $VMID &>/dev/null; then
qm stop $VMID &>/dev/null
qm destroy $VMID &>/dev/null
fi
}
function cleanup() {
local exit_code=$?
popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR
}
TEMP_DIR=$(mktemp -d)
pushd $TEMP_DIR >/dev/null
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Arch Linux VM" --yesno "This will create a New Arch Linux VM. Proceed?" 10 58; then
:
else
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
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
}
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.0–8.9
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 105
fi
return 0
fi
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 2)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
exit 105
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.2"
exit 105
}
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
}
function default_settings() {
vm_apply_machine_type "i440fx"
VMID=$(get_valid_nextid)
DISK_SIZE="10G"
MACHINE=""
DISK_SIZE="4G"
DISK_CACHE=""
HN="arch-linux"
CPU_TYPE=""
@@ -48,28 +224,219 @@ function default_settings() {
MTU=""
START_VM="yes"
METHOD="default"
vm_echo_default_settings
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${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 Cache: ${BGN}None${CL}"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${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 "${CREATING}${BOLD}${DGN}Creating a Arch Linux VM using the above default settings${CL}"
}
function advanced_settings() {
METHOD="advanced"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "i440fx"
vm_prompt_disk_size "10G"
vm_prompt_disk_cache "none"
vm_prompt_hostname "arch-linux"
vm_prompt_cpu_model "kvm64"
vm_prompt_cpu_cores "1"
vm_prompt_ram "1024"
vm_prompt_bridge "vmbr0"
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
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 [ -z "$VMID" ]; then
VMID=$(get_valid_nextid)
fi
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
sleep 2
continue
fi
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break
else
exit-script
fi
done
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
"i440fx" "Machine i440fx" ON \
"q35" "Machine q35" OFF \
3>&1 1>&2 2>&3); then
if [ "$MACH" = q35 ]; then
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
MACHINE=" -machine q35"
else
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
MACHINE=""
fi
else
exit-script
fi
if vm_confirm_advanced_settings "Ready to create a Arch Linux VM?"; 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 ' ')
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
DISK_SIZE="${DISK_SIZE}G"
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
else
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
exit-script
fi
else
exit-script
fi
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 \
"1" "Write Through" OFF \
3>&1 1>&2 2>&3); then
if [ "$DISK_CACHE" = "1" ]; then
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
DISK_CACHE="cache=writethrough,"
else
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
DISK_CACHE=""
fi
else
exit-script
fi
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 arch-linux --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$VM_NAME" ]; then
HN="arch-linux"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
if [ "$HN" != "${VM_NAME,,}" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
fi
else
exit-script
fi
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"0" "KVM64 (Default)" ON \
"1" "Host" OFF \
3>&1 1>&2 2>&3); then
if [ "$CPU_TYPE1" = "1" ]; then
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
CPU_TYPE=" -cpu host"
else
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
CPU_TYPE=""
fi
else
exit-script
fi
while true; do
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 CORE_COUNT="2"; fi
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
else
exit-script
fi
done
while true; do
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 [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
else
exit-script
fi
done
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
BRG="vmbr0"
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
else
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
fi
else
exit-script
fi
while true; do
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
MAC="$GEN_MAC"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
break
fi
if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
MAC="$MAC1"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
else
exit-script
fi
done
while true; do
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
VLAN1="Default"
VLAN=""
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
break
fi
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
VLAN=",tag=$VLAN1"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
else
exit-script
fi
done
while true; do
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
MTU1="Default"
MTU=""
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
MTU=",mtu=$MTU1"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
else
exit-script
fi
done
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}"
START_VM="yes"
else
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
START_VM="no"
fi
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Arch Linux VM?" --no-button Do-Over 10 58); then
echo -e "${CREATING}${BOLD}${DGN}Creating a Arch Linux VM using the above advanced settings${CL}"
else
header_info
@@ -78,54 +445,154 @@ function advanced_settings() {
fi
}
function start_script() {
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
header_info
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}"
default_settings
else
header_info
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
vm_preflight
vm_start_script "Use Default Settings?\n\nDefaults:\n• 1 CPU Core\n• 1 GB RAM\n• 10 GB Disk" 13 58
check_root
arch_check
pve_check
ssh_check
start_script
post_to_api_vm
vm_select_storage "$HN"
msg_info "Validating Storage"
while read -r line; do
TAG=$(echo "$line" | awk '{print $1}')
TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
ITEM=" Type: $TYPE Free: $FREE "
OFFSET=2
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
fi
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
msg_error "Unable to detect a valid storage location."
exit
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
else
while [ -z "${STORAGE:+x}" ]; do
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" \
16 $(($MSG_MAX_LENGTH + 23)) 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
done
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Retrieving the URL for the Arch Linux .iso File"
URL=https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso
FILENAME="archlinux-x86_64.iso"
vm_select_iso_storage "$FILENAME" "$HN"
CACHE_FILE="$ISO_PATH"
sleep 2
msg_ok "${CL}${BL}${URL}${CL}"
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
curl -f#SL -o "$(basename "$URL")" "$URL"
echo -en "\e[1A\e[0K"
FILE=$(basename $URL)
msg_ok "Downloaded ${CL}${BL}${FILE}${CL}"
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in
nfs | dir | cifs)
DISK_EXT=".qcow2"
DISK_REF="$VMID/"
DISK_IMPORT="--format qcow2"
THIN=""
;;
btrfs)
DISK_EXT=".raw"
DISK_REF="$VMID/"
DISK_IMPORT="--format raw"
THIN=""
;;
*)
DISK_EXT=""
DISK_REF=""
DISK_IMPORT="--format raw"
;;
esac
msg_info "Creating a Arch Linux VM"
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
# The ISO is the installer, not the system: an empty disk takes scsi0 and wins
# the boot order as soon as something is on it.
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" "${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 226
}
msg_ok "Imported disk (${CL}${BL}${DISK_REF_IMPORTED}${CL})"
qm set $VMID \
-efidisk0 ${STORAGE}:0,efitype=4m \
-scsi0 ${STORAGE}:${DISK_SIZE%G},${DISK_CACHE}${THIN%,} \
-cdrom "$ISO_VOLUME" \
-boot order='scsi0;ide2' \
-scsi0 ${DISK_REF_IMPORTED},${DISK_CACHE}${THIN%,} \
-ide2 ${STORAGE}:cloudinit \
-boot order=scsi0 \
-serial0 socket >/dev/null
set_description
DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://community-scripts.org' 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>
<h2 style='font-size: 24px; margin: 20px 0;'>Arch Linux VM</h2>
<p style='margin: 16px 0;'>
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
<img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
</a>
</p>
<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
if [ -n "$DISK_SIZE" ]; then
msg_info "Resizing disk to $DISK_SIZE GB"
qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
else
msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB"
qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null
fi
msg_ok "Created a Arch Linux VM ${CL}${BL}(${HN})"
if [ "$START_VM" == "yes" ]; then
msg_info "Starting Arch Linux VM"
$STD qm start $VMID
qm start $VMID
msg_ok "Started Arch Linux VM"
fi
post_update_to_api "done" "none"
echo -e "\n${INFO}${BOLD}${GN}Arch Linux 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}Disk Size: ${BGN}${DISK_SIZE}${CL}"
echo -e "\n${INFO}${BOLD}${YW}Next Steps:${CL}"
echo -e "${TAB}1. Open the VM Console in Proxmox - the live ISO logs in as root by itself"
echo -e "${TAB}2. Run ${BL}archinstall${CL}, or install by hand, onto ${BL}/dev/sda${CL}"
echo -e "${TAB}3. Detach the ISO afterwards (${BL}qm set ${VMID} --ide2 none${CL}) -- the boot"
echo -e "${TAB} order already prefers the disk"
echo -e "${TAB}4. The ISO carries no Cloud-Init and no guest agent. Install"
echo -e "${TAB} ${BL}qemu-guest-agent${CL} in the guest for Proxmox to show its IP"
msg_ok "Completed successfully!\n"
-183
View File
@@ -1,183 +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://fedoraproject.org/cloud/
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
APP="Fedora"
APP_TYPE="vm"
NSAPP="fedora-vm"
var_os="fedora"
var_version=" "
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
THIN="discard=on,ssd=1,"
header_info
echo -e "\n Loading..."
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
vm_preflight
# Fedora Cloud Base sets no password and has no console login, so the only
# question is which credentials.
CLOUDINIT_REQUIRED=1
vm_prompt_cloud_init "fedora"
function default_settings() {
VMID=$(get_valid_nextid)
vm_apply_machine_type "q35"
DISK_SIZE="20G"
DISK_CACHE=""
HN="fedora"
CPU_TYPE=" -cpu host"
CORE_COUNT="2"
RAM_SIZE="2048"
BRG="vmbr0"
MAC="$GEN_MAC"
VLAN=""
MTU=""
START_VM="yes"
METHOD="default"
echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}"
vm_echo_default_settings
}
function advanced_settings() {
METHOD="advanced"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "q35"
vm_prompt_disk_size "20G" "Set Disk Size in GiB"
vm_prompt_disk_cache "none"
vm_prompt_hostname "fedora"
vm_prompt_cpu_model "host"
vm_prompt_cpu_cores "2"
vm_prompt_ram "2048"
vm_prompt_bridge "vmbr0"
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
if vm_confirm_advanced_settings "Ready to create a Fedora VM?"; then
echo -e "${CREATING}${BOLD}${DGN}Creating a Fedora VM using the above advanced settings${CL}"
else
header_info
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 20 GB Disk\n• Cloud-Init enabled" 14 58
post_to_api_vm
vm_select_storage "$HN"
if ! command -v virt-customize &>/dev/null; then
msg_info "Installing libguestfs-tools"
$STD apt-get update
$STD apt-get install -y libguestfs-tools
msg_ok "Installed libguestfs-tools"
fi
msg_info "Retrieving the URL for the Fedora Cloud Base image"
MIRROR="https://dl.fedoraproject.org/pub/fedora/linux/releases"
FEDORA_RELEASE=$(curl -fsSL "$MIRROR/" 2>/dev/null | grep -oP 'href="\K[0-9]+(?=/")' | sort -rn | head -1)
[[ -z "$FEDORA_RELEASE" ]] && FEDORA_RELEASE="44"
FEDORA_ARCH="$(vm_arch_resolve x86_64 aarch64)"
IMAGE_DIR="${MIRROR}/${FEDORA_RELEASE}/Cloud/${FEDORA_ARCH}/images"
FILE=$(curl -fsSL "${IMAGE_DIR}/" 2>/dev/null | grep -oP 'href="\KFedora-Cloud-Base-Generic-[^"]+\.'"${FEDORA_ARCH}"'\.qcow2(?=")' | sort -V | tail -1)
if [[ -z "$FILE" ]]; then
msg_error "Could not determine the current Fedora Cloud image"
exit 1
fi
msg_ok "Fedora ${CL}${BL}${FEDORA_RELEASE}${CL} ${GN}(${FILE})"
URL="${IMAGE_DIR}/${FILE}"
CACHE_FILE="$(vm_image_cache_path "$URL")"
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
WORK_FILE=$(mktemp --suffix=.qcow2)
cp "$CACHE_FILE" "$WORK_FILE"
popd >/dev/null
rm -rf "$TEMP_DIR"
vm_prepare_cloud_image "$WORK_FILE" "$HN" || true
msg_info "Customizing ${FILE}"
CUSTOMIZE_FAILURES_BEFORE=${#_VM_PREPARE_FAILED[@]}
_vm_customize "Fedora serial console" "$WORK_FILE" --run-command "systemctl enable serial-getty@ttyS0.service"
_vm_customize "Fedora SELinux relabel" "$WORK_FILE" --selinux-relabel
if ((${#_VM_PREPARE_FAILED[@]} == CUSTOMIZE_FAILURES_BEFORE)); then
msg_ok "Customized image"
else
msg_warn "Fedora image customization incomplete; see warnings above"
fi
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
vm_apply_storage_layout "$STORAGE_TYPE"
vm_define_disk_references 3
if [[ "$DISK_IMPORT_FORMAT" == "raw" ]]; then
msg_info "Converting image to raw format"
RAW_FILE=$(mktemp --suffix=.raw)
qemu-img convert -f qcow2 -O raw "$WORK_FILE" "$RAW_FILE"
rm -f "$WORK_FILE"
WORK_FILE="$RAW_FILE"
msg_ok "Converted image to raw format"
fi
msg_info "Creating a Fedora VM"
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
vm_alloc_efi_disk "$DISK0"
pvesm alloc "$STORAGE" "$VMID" "$DISK2" 4M 1>&/dev/null
qm importdisk "$VMID" "$WORK_FILE" "$STORAGE" -format "$DISK_IMPORT_FORMAT" 1>&/dev/null
qm set "$VMID" \
-efidisk0 "${DISK0_REF}${FORMAT:-}" \
-scsi0 "${DISK1_REF}",${DISK_CACHE}${THIN}size="${DISK_SIZE}" \
-tpmstate0 "${DISK2_REF}",version=v2.0 \
-boot order=scsi0 \
-serial0 socket >/dev/null
set_description
rm -f "$WORK_FILE"
msg_ok "Created a Fedora VM ${CL}${BL}(${HN})"
vm_resize_disk
vm_provision "$VMID" || true
if [ "$START_VM" == "yes" ]; then
msg_info "Starting Fedora VM"
$STD qm start "$VMID"
msg_ok "Started Fedora VM"
fi
post_update_to_api "done" "none"
echo -e "\n${INFO}${BOLD}${GN}Fedora 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}Release: ${BGN}Fedora ${FEDORA_RELEASE} (${FEDORA_ARCH})${CL}"
if [ -n "${CLOUDINIT_CRED_FILE:-}" ]; then
echo -e "${TAB}${DGN}Cloud-Init credentials: ${BGN}${CLOUDINIT_CRED_FILE}${CL}"
fi
msg_ok "Completed successfully!\n"
+488 -42
View File
@@ -1,43 +1,219 @@
#!/usr/bin/env bash
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
function header_info {
clear
cat <<"EOF"
__ ____ __ __ _ __ ____ __ ____ _____ ________ ______
/ |/ (_) /___________ / /_(_) /__ / __ \____ __ __/ /____ _____/ __ \/ ___/ / ____/ / / / __ \
/ /|_/ / / //_/ ___/ __ \/ __/ / //_/ / /_/ / __ \/ / / / __/ _ \/ ___/ / / /\__ \ / / / /_/ / /_/ /
/ / / / / ,< / / / /_/ / /_/ / ,< / _, _/ /_/ / /_/ / /_/ __/ / / /_/ /___/ / / /___/ __ / _, _/
/_/ /_/_/_/|_/_/ \____/\__/_/_/|_| /_/ |_|\____/\__,_/\__/\___/_/ \____//____/ \____/_/ /_/_/ |_|
EOF
}
header_info
echo -e "Loading..."
GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
APP="MikroTik RouterOS"
APP_TYPE="vm"
NSAPP="mikrotik-routeros"
var_os="mikrotik"
var_version=" "
DISK_SIZE="1G"
YW=$(echo "\033[33m")
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,"
header_info
echo -e "Loading..."
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
post_update_to_api "failed" "${exit_code}"
echo -e "\n$error_message\n"
cleanup_vmid
}
vm_require_arch amd64
function get_valid_nextid() {
local try_id
try_id=$(pvesh get /cluster/nextid)
while true; do
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
try_id=$((try_id + 1))
continue
fi
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
try_id=$((try_id + 1))
continue
fi
break
done
echo "$try_id"
}
function cleanup_vmid() {
if qm status $VMID &>/dev/null; then
qm stop $VMID &>/dev/null
qm destroy $VMID &>/dev/null
fi
}
function cleanup() {
local exit_code=$?
popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR
}
TEMP_DIR=$(mktemp -d)
pushd $TEMP_DIR >/dev/null
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a Mikrotik RouterOS CHR VM. Proceed?" 10 58; then
:
else
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
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
}
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.0–8.9
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 105
fi
return 0
fi
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 2)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
exit 105
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.2"
exit 105
}
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
}
function default_settings() {
vm_apply_machine_type "i440fx"
VMID=$(get_valid_nextid)
FORMAT=",efitype=4m"
MACHINE=""
DISK_SIZE="8G"
DISK_CACHE=""
HN="mikrotik-routeros-chr"
@@ -51,7 +227,20 @@ function default_settings() {
START_VM="yes"
CLOUD_INIT="no"
METHOD="default"
vm_echo_default_settings
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${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 Cache: ${BGN}None${CL}"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${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 "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS VM using the above default settings${CL}"
}
function get_mikrotik_version() {
@@ -118,23 +307,204 @@ function get_mikrotik_version() {
function advanced_settings() {
METHOD="advanced"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "i440fx"
vm_prompt_disk_size "8G"
vm_prompt_disk_cache "none"
vm_prompt_hostname "mikrotik-routeros-chr"
vm_prompt_cpu_model "kvm64"
vm_prompt_cpu_cores "2"
vm_prompt_ram "512"
vm_prompt_bridge "vmbr0"
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
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 [ -z "$VMID" ]; then
VMID=$(get_valid_nextid)
fi
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
sleep 2
continue
fi
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break
else
exit-script
fi
done
if vm_confirm_advanced_settings "Ready to create a MikroTik RouterOS VM?"; then
echo -e "${CREATING}${BOLD}${DGN}Creating a MikroTik RouterOS VM using the above advanced settings${CL}"
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
"i440fx" "Machine i440fx" ON \
"q35" "Machine q35" OFF \
3>&1 1>&2 2>&3); then
if [ $MACH = q35 ]; then
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
FORMAT=""
MACHINE=" -machine q35"
else
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
FORMAT=",efitype=4m"
MACHINE=""
fi
else
exit-script
fi
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 ' ')
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
DISK_SIZE="${DISK_SIZE}G"
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
else
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
exit-script
fi
else
exit-script
fi
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 \
"1" "Write Through" OFF \
3>&1 1>&2 2>&3); then
if [ $DISK_CACHE = "1" ]; then
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
DISK_CACHE="cache=writethrough,"
else
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
DISK_CACHE=""
fi
else
exit-script
fi
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $VM_NAME ]; then
HN="mikrotik-routeros-chr"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
if [ "$HN" != "${VM_NAME,,}" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
fi
else
exit-script
fi
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"0" "KVM64 (Default)" ON \
"1" "Host" OFF \
3>&1 1>&2 2>&3); then
if [ $CPU_TYPE1 = "1" ]; then
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
CPU_TYPE=" -cpu host"
else
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
CPU_TYPE=""
fi
else
exit-script
fi
while true; do
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 CORE_COUNT="2"; fi
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
else
exit-script
fi
done
while true; do
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 [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
else
exit-script
fi
done
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
BRG="vmbr0"
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
else
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
fi
else
exit-script
fi
while true; do
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
MAC="$GEN_MAC"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
break
fi
if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
MAC="$MAC1"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
else
exit-script
fi
done
while true; do
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
VLAN1="Default"
VLAN=""
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
break
fi
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
VLAN=",tag=$VLAN1"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
else
exit-script
fi
done
while true; do
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
MTU1="Default"
MTU=""
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
MTU=",mtu=$MTU1"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
else
exit-script
fi
done
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}"
START_VM="yes"
else
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
START_VM="no"
fi
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Mikrotik RouterOS CHR VM?" --no-button Do-Over 10 58); then
echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS CHR VM using the above advanced settings${CL}"
else
header_info
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
@@ -142,12 +512,54 @@ function advanced_settings() {
fi
}
function start_script() {
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
header_info
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}"
default_settings
else
header_info
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
vm_preflight
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 512 MB RAM\n• 8 GB Disk" 13 58
check_root
arch_check
pve_check
ssh_check
start_script
post_to_api_vm
vm_select_storage "$HN"
msg_info "Validating Storage"
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
ITEM=" Type: $TYPE Free: $FREE "
OFFSET=2
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
fi
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
echo -e "\n${RD}⚠ Unable to detect a valid storage location.${CL}"
echo -e "Exiting..."
exit
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
else
while [ -z "${STORAGE:+x}" ]; do
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
"Which storage pool would you like to use for the Mikrotik RouterOS CHR VM?\n\n" \
16 $(($MSG_MAX_LENGTH + 23)) 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
done
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Getting URL for Latest Mikrotik RouterOS CHR Disk Image"
MIK_VER=$(get_mikrotik_version s)
@@ -164,14 +576,12 @@ URL=https://download.mikrotik.com/routeros/$MIK_VER/chr-$MIK_VER.img.zip
sleep 2
msg_ok "Downloading from URL: ${CL}${BL}${URL}${CL}"
# A mirror serving an error page returns 200, so size decides whether this
# is an image. Anything real here is far above 5 MB.
CACHE_FILE="$(vm_image_cache_path "$URL")"
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((5 * 1024 * 1024)) || exit 115
curl -f#SL -o "$(basename "$URL")" "$URL"
echo -en "\e[1A\e[0K"
FILE=$(basename $URL)
msg_ok "Downloaded ${CL}${BL}$FILE${CL}"
msg_info "Extracting Mikrotik RouterOS CHR Disk Image"
# Decompress out of the cache rather than over it, gunzip eats its input.
FILE="$(basename "${CACHE_FILE%.zip}")"
gunzip -c -S .zip "$CACHE_FILE" >"$FILE"
gunzip -f -S .zip $FILE
STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in
nfs | dir)
@@ -204,18 +614,54 @@ msg_info "Creating Mikrotik RouterOS CHR VM"
qm create $VMID -tablet 0 -localtime 1 -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
qm importdisk $VMID "$FILE" $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
qm set $VMID \
-scsi0 "$DISK_REF" \
-boot order=scsi0 >/dev/null
set_description
vm_resize_disk
DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://community-scripts.org' 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>
<h2 style='font-size: 24px; margin: 20px 0;'>Mikrotik RouterOS CHR</h2>
<p style='margin: 16px 0;'>
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
<img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
</a>
</p>
<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
if [ -n "$DISK_SIZE" ]; then
msg_info "Resizing disk to $DISK_SIZE GB"
qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
else
msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB"
qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null
fi
msg_ok "Mikrotik RouterOS CHR VM ${CL}${BL}(${HN})"
if [ "$START_VM" == "yes" ]; then
msg_info "Starting Mikrotik RouterOS CHR VM"
$STD qm start $VMID
qm start $VMID
msg_ok "Started Mikrotik RouterOS CHR VM"
fi
post_update_to_api "done" "none"
+537 -58
View File
@@ -1,42 +1,218 @@
#!/usr/bin/env bash
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster) | MickLesk (CanbiZ)
# Author: tteck (tteckster)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
function header_info {
clear
cat <<"EOF"
______ __ __ _ __ __ __ __ _ ____ ___
/_ __/_ _________ / //_/__ __ __ / |/ /____ __/ /_____/ /__ __ _____/ / | | / / |/ /
/ / / // / __/ _ \/ ,< / -_) // / / / -_) \ / __/ __/ / _ \/ // / _ / | |/ / /|_/ /
/_/ \_,_/_/ /_//_/_/|_|\__/\_, / /_/|_/\__/_\_\\__/\__/_/\___/\_,_/\_,_/ |___/_/ /_/
/___/
EOF
}
header_info
echo -e "\n Loading..."
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
APP="Nextcloud"
APP_TYPE="vm"
NSAPP="nextcloud-vm"
var_os="turnkey-nextcloud"
var_version="19.0"
var_version="n.d."
YW=$(echo "\033[33m")
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,"
header_info
echo -e "\n Loading..."
set -Eeo pipefail
shopt -s inherit_errexit
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
post_update_to_api "failed" "${exit_code}"
echo -e "\n$error_message\n"
cleanup_vmid
}
vm_require_arch amd64
function get_valid_nextid() {
local try_id
try_id=$(pvesh get /cluster/nextid)
while true; do
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
try_id=$((try_id + 1))
continue
fi
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
try_id=$((try_id + 1))
continue
fi
break
done
echo "$try_id"
}
function cleanup_vmid() {
if qm status $VMID &>/dev/null; then
qm stop $VMID &>/dev/null
qm destroy $VMID &>/dev/null
fi
}
function cleanup() {
local exit_code=$?
popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR
}
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
pushd $TEMP_DIR >/dev/null
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Nextcloud VM" --yesno "This will create a New Nextcloud VM. Proceed?" 10 58; then
:
else
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
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
}
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.0–8.9
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 105
fi
return 0
fi
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 2)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
exit 105
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.2"
exit 105
}
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
}
function default_settings() {
vm_apply_machine_type "i440fx"
VMID=$(get_valid_nextid)
FORMAT=",efitype=4m"
MACHINE=""
DISK_SIZE="10G"
DISK_CACHE=""
HN="nextcloud-vm"
@@ -49,28 +225,221 @@ function default_settings() {
MTU=""
START_VM="yes"
METHOD="default"
vm_echo_default_settings
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${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 Cache: ${BGN}None${CL}"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${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 "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above default settings${CL}"
}
function advanced_settings() {
METHOD="advanced"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "i440fx"
vm_prompt_disk_size "10G"
vm_prompt_disk_cache "none"
vm_prompt_hostname "nextcloud-vm"
vm_prompt_cpu_model "kvm64"
vm_prompt_cpu_cores "2"
vm_prompt_ram "2048"
vm_prompt_bridge "vmbr0"
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
vm_prompt_keyboard
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
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 [ -z "$VMID" ]; then
VMID=$(get_valid_nextid)
fi
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
sleep 2
continue
fi
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break
else
exit-script
fi
done
if vm_confirm_advanced_settings "Ready to create a Nextcloud VM?"; then
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
"i440fx" "Machine i440fx" ON \
"q35" "Machine q35" OFF \
3>&1 1>&2 2>&3); then
if [ $MACH = q35 ]; then
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
FORMAT=""
MACHINE=" -machine q35"
else
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
FORMAT=",efitype=4m"
MACHINE=""
fi
else
exit-script
fi
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 ' ')
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
DISK_SIZE="${DISK_SIZE}G"
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
else
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
exit-script
fi
else
exit-script
fi
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 \
"1" "Write Through" OFF \
3>&1 1>&2 2>&3); then
if [ $DISK_CACHE = "1" ]; then
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
DISK_CACHE="cache=writethrough,"
else
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
DISK_CACHE=""
fi
else
exit-script
fi
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $VM_NAME ]; then
HN="nextcloud-vm"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
if [ "$HN" != "${VM_NAME,,}" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
fi
else
exit-script
fi
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"0" "KVM64 (Default)" ON \
"1" "Host" OFF \
3>&1 1>&2 2>&3); then
if [ $CPU_TYPE1 = "1" ]; then
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
CPU_TYPE=" -cpu host"
else
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
CPU_TYPE=""
fi
else
exit-script
fi
while true; do
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 CORE_COUNT="2"; fi
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
else
exit-script
fi
done
while true; do
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 [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
else
exit-script
fi
done
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
BRG="vmbr0"
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
else
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
fi
else
exit-script
fi
while true; do
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
MAC="$GEN_MAC"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
break
fi
if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
MAC="$MAC1"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
else
exit-script
fi
done
while true; do
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
VLAN1="Default"
VLAN=""
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
break
fi
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
VLAN=",tag=$VLAN1"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
else
exit-script
fi
done
while true; do
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
MTU1="Default"
MTU=""
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
MTU=",mtu=$MTU1"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
else
exit-script
fi
done
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}"
START_VM="yes"
else
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
START_VM="no"
fi
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Nextcloud VM?" --no-button Do-Over 10 58); then
echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above advanced settings${CL}"
else
header_info
@@ -79,35 +448,145 @@ function advanced_settings() {
fi
}
vm_preflight
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 10 GB Disk" 13 58
function start_script() {
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
header_info
echo -e "${BL}Using Default Settings${CL}"
default_settings
else
header_info
echo -e "${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
check_root
arch_check
pve_check
ssh_check
start_script
post_to_api_vm
vm_select_storage "$HN"
msg_info "Retrieving the ${APP} installer ISO"
URL=https://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-nextcloud-19.0-trixie-amd64.iso
msg_info "Validating Storage"
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
ITEM=" Type: $TYPE Free: $FREE "
OFFSET=2
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
fi
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
msg_error "Unable to detect a valid storage location."
exit
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
else
while [ -z "${STORAGE:+x}" ]; do
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" \
16 $(($MSG_MAX_LENGTH + 23)) 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
done
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Retrieving the URL for the $NAME Disk Image"
URL=http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-nextcloud-18.1-bookworm-amd64.iso
sleep 2
msg_ok "${CL}${BL}${URL}${CL}"
vm_select_iso_storage "$(basename "$URL")" "$HN"
vm_fetch_image "$URL" "$ISO_PATH" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
curl -f#SL -o "$(basename "$URL")" "$URL"
echo -en "\e[1A\e[0K"
FILE=$(basename $URL)
msg_ok "Downloaded ${CL}${BL}${FILE}${CL}"
vm_claim_vmid
msg_info "Creating a ${APP}"
qm create $VMID -agent 1${MACHINE} -tablet 0 -bios seabios${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 \
-scsi0 "${STORAGE}:${DISK_SIZE%G},${DISK_CACHE}${THIN%,}" \
-cdrom "$ISO_VOLUME" -boot order='scsi0;ide2' >/dev/null
vm_mark_created
set_description
STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in
nfs | dir)
DISK_EXT=".raw"
DISK_REF="$VMID/"
DISK_IMPORT="-format raw"
THIN=""
;;
btrfs)
DISK_EXT=".raw"
DISK_REF="$VMID/"
DISK_IMPORT="-format raw"
FORMAT=",efitype=4m"
THIN=""
;;
*)
DISK_EXT=""
DISK_REF=""
DISK_IMPORT="-format raw"
;;
esac
for i in {0,1,2}; do
disk="DISK$i"
eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-}
eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk}
done
msg_ok "Created a ${APP} ${CL}${BL}(${HN})"
vm_start_vm "$APP"
vm_print_summary "Version=TurnKey Nextcloud ${var_version}" "Installer=$(basename "$URL")" "Web UI=https://<VM-IP>/" "Webmin=https://<VM-IP>:12321/"
vm_next_steps \
"Open the VM Console in Proxmox and complete the TurnKey installer." \
"Set the root, Nextcloud admin, and database credentials when prompted." \
"After installation, detach the ISO: qm set ${VMID} --ide2 none." \
"Start or reboot the VM, then open https://<VM-IP>/ for Nextcloud." \
"Manage the appliance through Webmin at https://<VM-IP>:12321/."
vm_finish "VM created; complete the TurnKey Nextcloud installation in the Proxmox console."
msg_info "Creating a $NAME"
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios seabios${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
pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null
pvesm alloc $STORAGE $VMID $DISK1 12G 1>&/dev/null
qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
qm set $VMID \
-efidisk0 ${DISK0_REF}${FORMAT} \
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \
-scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \
-boot order='scsi1;scsi0' >/dev/null
DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://community-scripts.org' 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>
<h2 style='font-size: 24px; margin: 20px 0;'>Nextcloud VM</h2>
<p style='margin: 16px 0;'>
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
<img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
</a>
</p>
<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
if [ -n "$DISK_SIZE" ]; then
msg_info "Resizing disk to $DISK_SIZE GB"
qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
else
msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB"
qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null
fi
msg_ok "Created a $NAME ${CL}${BL}(${HN})"
if [ "$START_VM" == "yes" ]; then
msg_info "Starting $NAME"
qm start $VMID
msg_ok "Started $NAME"
fi
post_update_to_api "done" "none"
msg_ok "Completed successfully!\n"
+506 -179
View File
@@ -1,17 +1,30 @@
#!/usr/bin/env bash
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster) | Jon Spriggs (jontheniceguy) | MickLesk (CanbiZ)
# Author: tteck (tteckster)
# Jon Spriggs (jontheniceguy)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Based on work from https://i12bretro.github.io/tutorials/0405.html
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/DevScripts/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
function header_info {
clear
cat <<"EOF"
____ _ __ __
/ __ \____ ___ ____| | / /____/ /_
/ / / / __ \/ _ \/ __ \ | /| / / ___/ __/
/ /_/ / /_/ / __/ / / / |/ |/ / / / /_
\____/ .___/\___/_/ /_/|__/|__/_/ \__/
/_/ W I R E L E S S F R E E D O M
EOF
}
header_info
echo -e "\n Loading..."
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
APP="OpenWrt"
APP_TYPE="vm"
NSAPP="openwrt-vm"
var_os="openwrt"
var_version=" "
@@ -19,27 +32,94 @@ DISK_SIZE="1G"
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
YW=$(echo "\033[33m")
BL=$(echo "\033[36m")
HA=$(echo "\033[1;34m")
RD=$(echo "\033[01;31m")
BGN=$(echo "\033[4;92m")
GN=$(echo "\033[1;92m")
DGN=$(echo "\033[32m")
CL=$(echo "\033[m")
header_info
echo -e "\n Loading..."
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}"
set -Eeo pipefail
shopt -s inherit_errexit
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
post_update_to_api "failed" "$exit_code"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
cleanup_vmid
}
vm_require_arch amd64
function get_valid_nextid() {
local try_id
try_id=$(pvesh get /cluster/nextid)
while true; do
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
try_id=$((try_id + 1))
continue
fi
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
try_id=$((try_id + 1))
continue
fi
break
done
echo "$try_id"
}
function cleanup_vmid() {
if qm status $VMID &>/dev/null; then
qm stop $VMID &>/dev/null
qm destroy $VMID &>/dev/null
fi
}
function cleanup() {
local exit_code=$?
popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR
}
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
vm_preflight
vm_require_tools gzip
pushd $TEMP_DIR >/dev/null
function send_line_to_vm() {
echo -e "${DGN}Sending line: ${YW}$1${CL}"
for ((i = 0; i < ${#1}; i++)); do
@@ -91,7 +171,7 @@ function send_line_to_vm() {
"U") character="shift-u" ;;
"V") character="shift-v" ;;
"W") character="shift-w" ;;
"X") character="shift-x" ;;
"X") character="shift=x" ;;
"Y") character="shift-y" ;;
"Z") character="shift-z" ;;
"!") character="shift-1" ;;
@@ -110,92 +190,97 @@ function send_line_to_vm() {
qm sendkey $VMID ret
}
function validate_ip_octets() {
local octet='(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])'
[[ "$1" =~ ^${octet}\.${octet}\.${octet}\.${octet}$ ]]
TEMP_DIR=$(mktemp -d)
pushd $TEMP_DIR >/dev/null
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "OpenWrt VM" --yesno "This will create a New OpenWrt VM. Proceed?" 10 58); then
:
else
header_info && echo -e "⚠ User exited script \n" && exit
fi
function msg_info() {
local msg="$1"
echo -ne " ${HOLD} ${YW}${msg}..."
}
function prompt_router_input() {
local var_name="$1" title="$2" prompt="$3" default_value="$4" value
if vm_dialog inputbox "$title" "$prompt" 8 58 "$default_value" --cancel-button Exit-Script; then
value="$VM_DIALOG_RESULT"
[[ -n "$value" ]] || value="$default_value"
printf -v "$var_name" '%s' "$value"
else
exit_script
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}"
}
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.0–8.9
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 105
fi
return 0
fi
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 2)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
exit 105
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.2"
exit 105
}
function arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
echo -e "\n ${CROSS} This script will not work with PiMox! \n"
echo -e "Exiting..."
sleep 2
exit
fi
}
function prompt_router_ip() {
local var_name="$1" title="$2" prompt="$3" default_value="$4" label="$5" value
prompt_router_input "$var_name" "$title" "$prompt" "$default_value"
value="${!var_name}"
if ! validate_ip_octets "$value"; then
msg_error "Invalid ${label} format. Needs to be 0.0.0.0, was $value"
exit 1
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
echo -e "${DGN}Using ${label}: ${BGN}$value${CL}"
}
function prompt_router_mac() {
local var_name="$1" title="$2" prompt="$3" default_value="$4" label="$5" value
prompt_router_input "$var_name" "$title" "$prompt" "$default_value"
value="${!var_name}"
if ! validate_mac_address "$value"; then
msg_error "Invalid ${label}: $value"
exit 1
fi
echo -e "${DGN}Using ${label}: ${BGN}$value${CL}"
}
function prompt_router_vlan() {
local var_name="$1" label_var="$2" title="$3" prompt="$4" default_value="$5" input_value
while true; do
prompt_router_input "$label_var" "$title" "$prompt" "$default_value"
input_value="${!label_var}"
if [ -z "$input_value" ] || [ "$input_value" = "Default" ]; then
printf -v "$var_name" '%s' ""
printf -v "$label_var" '%s' "Default"
echo -e "${DGN}Using ${title}: ${BGN}Default${CL}"
break
fi
if validate_vlan_tag "$input_value"; then
printf -v "$var_name" '%s' ",tag=$input_value"
echo -e "${DGN}Using ${title}: ${BGN}$input_value${CL}"
break
fi
vm_dialog msgbox "INVALID INPUT" "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
done
}
function prompt_router_mtu() {
local input_value
while true; do
prompt_router_input "MTU_VALUE" "MTU SIZE" "Set Interface MTU Size (leave blank for default)" ""
input_value="$MTU_VALUE"
if [ -z "$input_value" ]; then
MTU=""
MTU_VALUE="Default"
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
break
fi
if validate_mtu "$input_value"; then
MTU=",mtu=$input_value"
echo -e "${DGN}Using Interface MTU Size: ${BGN}$input_value${CL}"
break
fi
vm_dialog msgbox "INVALID INPUT" "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
done
function exit-script() {
clear
echo -e "⚠ User exited script \n"
exit
}
function default_settings() {
VMID=$(get_valid_nextid)
vm_apply_machine_type "i440fx"
HN="openwrt"
CORE_COUNT="1"
RAM_SIZE="256"
CPU_TYPE=""
DISK_CACHE=""
BRG="vmbr0"
LAN_BRG="vmbr0"
MAC=$GEN_MAC
@@ -221,30 +306,198 @@ function default_settings() {
function advanced_settings() {
METHOD="advanced"
CPU_TYPE=""
DISK_CACHE=""
vm_apply_machine_type "i440fx"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_hostname "openwrt"
vm_prompt_cpu_cores "1"
vm_prompt_ram "256"
vm_prompt_disk_size "1G"
vm_prompt_keyboard
vm_prompt_verbose "no"
prompt_router_input "BRG" "WAN BRIDGE" "Set a WAN Bridge" "vmbr0"
echo -e "${DGN}Using WAN Bridge: ${BGN}$BRG${CL}"
prompt_router_input "LAN_BRG" "LAN BRIDGE" "Set a LAN Bridge" "vmbr0"
echo -e "${DGN}Using LAN Bridge: ${BGN}$LAN_BRG${CL}"
prompt_router_ip "LAN_IP_ADDR" "LAN IP ADDRESS" "Set a router IP" "${LAN_IP_ADDR:-192.168.1.1}" "LAN IP ADDRESS"
prompt_router_ip "LAN_NETMASK" "LAN NETMASK" "Set a router netmask" "${LAN_NETMASK:-255.255.255.0}" "LAN NETMASK"
prompt_router_mac "MAC" "WAN MAC ADDRESS" "Set a WAN MAC Address" "$GEN_MAC" "WAN MAC address"
prompt_router_mac "LAN_MAC" "LAN MAC ADDRESS" "Set a LAN MAC Address" "$GEN_MAC_LAN" "LAN MAC address"
prompt_router_vlan "VLAN" "VLAN1" "WAN VLAN" "Set a WAN Vlan (leave blank for default)" ""
prompt_router_vlan "LAN_VLAN" "VLAN2" "LAN VLAN" "Set a LAN Vlan" "999"
prompt_router_mtu
vm_prompt_start_vm "yes"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
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 [ -z "$VMID" ]; then
VMID=$(get_valid_nextid)
fi
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
sleep 2
continue
fi
echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break
else
exit-script
fi
done
if vm_confirm_advanced_settings "Ready to create OpenWrt VM?"; then
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 openwrt --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $VM_NAME ]; then
HN="openwrt"
else
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
if [ "$HN" != "${VM_NAME,,}" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
fi
fi
echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}"
else
exit-script
fi
while true; do
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 1 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="1"; fi
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 1)." 8 58
else
exit-script
fi
done
while true; do
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 256 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="256"; fi
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 256)." 8 58
else
exit-script
fi
done
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
--inputbox "Set Disk Size in GiB (e.g., 1, 2, 4)" 8 58 "1" \
--title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
DISK_SIZE="${DISK_SIZE}G"
fi
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
else
exit-script
fi
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Bridge" 8 58 vmbr0 --title "WAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $BRG ]; then
BRG="vmbr0"
fi
echo -e "${DGN}Using WAN Bridge: ${BGN}$BRG${CL}"
else
exit-script
fi
if LAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Bridge" 8 58 vmbr0 --title "LAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $LAN_BRG ]; then
LAN_BRG="vmbr0"
fi
echo -e "${DGN}Using LAN Bridge: ${BGN}$LAN_BRG${CL}"
else
exit-script
fi
if LAN_IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a router IP" 8 58 $LAN_IP_ADDR --title "LAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $LAN_IP_ADDR ]; then
LAN_IP_ADDR="192.168.1.1"
fi
echo -e "${DGN}Using LAN IP ADDRESS: ${BGN}$LAN_IP_ADDR${CL}"
else
exit-script
fi
if LAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a router netmask" 8 58 $LAN_NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $LAN_NETMASK ]; then
LAN_NETMASK="255.255.255.0"
fi
echo -e "${DGN}Using LAN NETMASK: ${BGN}$LAN_NETMASK${CL}"
else
exit-script
fi
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC1 ]; then
MAC="$GEN_MAC"
else
MAC="$MAC1"
fi
echo -e "${DGN}Using WAN MAC Address: ${BGN}$MAC${CL}"
else
exit-script
fi
if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC_LAN --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC2 ]; then
LAN_MAC="$GEN_MAC_LAN"
else
LAN_MAC="$MAC2"
fi
echo -e "${DGN}Using LAN MAC Address: ${BGN}$LAN_MAC${CL}"
else
exit-script
fi
while true; do
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Vlan (leave blank for default)" 8 58 --title "WAN VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$VLAN1" ]; then
VLAN1="Default"
VLAN=""
echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}"
break
fi
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
VLAN=",tag=$VLAN1"
echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
else
exit-script
fi
done
while true; do
if VLAN2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Vlan" 8 58 999 --title "LAN VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$VLAN2" ]; then
VLAN2="Default"
LAN_VLAN=""
echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}"
break
fi
if [[ "$VLAN2" =~ ^[0-9]+$ ]] && [ "$VLAN2" -ge 1 ] && [ "$VLAN2" -le 4094 ]; then
LAN_VLAN=",tag=$VLAN2"
echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
else
exit-script
fi
done
while true; do
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
MTU1="Default"
MTU=""
echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
MTU=",mtu=$MTU1"
echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
else
exit-script
fi
done
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
START_VM="yes"
else
START_VM="no"
fi
echo -e "${DGN}Start VM when completed: ${BGN}$START_VM${CL}"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create OpenWrt VM?" --no-button Do-Over 10 58); then
echo -e "${RD}Creating a OpenWrt VM using the above advanced settings${CL}"
else
header_info
@@ -253,92 +506,174 @@ function advanced_settings() {
fi
}
vm_start_script "Use Default Settings?\n\nDefaults:\n• 1 CPU Core\n• 256 MB RAM\n• 1 GB Disk" 13 58
function start_script() {
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
header_info
echo -e "${BL}Using Default Settings${CL}"
default_settings
else
header_info
echo -e "${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
arch_check
pve_check
ssh_check
start_script
post_to_api_vm
vm_select_storage "$HN"
msg_info "Validating Storage"
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
ITEM=" Type: $TYPE Free: $FREE "
OFFSET=2
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
fi
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
echo -e "\n${RD}⚠ Unable to detect a valid storage location.${CL}"
echo -e "Exiting..."
exit
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
else
while [ -z "${STORAGE:+x}" ]; do
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
"Which storage pool would you like to use for the OpenWrt VM?\n\n" \
16 $(($MSG_MAX_LENGTH + 23)) 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
done
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Getting URL for OpenWrt Disk Image"
vm_latest_from_index "https://openwrt.org" 'Current stable release - OpenWrt \K[0-9]+\.[0-9]+\.[0-9]+' || exit 115
var_version="$VM_INDEX_LATEST"
URL="https://downloads.openwrt.org/releases/$var_version/targets/x86/64/openwrt-$var_version-x86-64-generic-ext4-combined.img.gz"
response=$(curl -fsSL https://openwrt.org)
stableversion=$(echo "$response" | sed -n 's/.*Current stable release - OpenWrt \([0-9.]\+\).*/\1/p' | head -n 1)
URL="https://downloads.openwrt.org/releases/$stableversion/targets/x86/64/openwrt-$stableversion-x86-64-generic-ext4-combined.img.gz"
msg_ok "${CL}${BL}${URL}${CL}"
# A mirror serving an error page returns 200, so size decides whether this
# is an image. Anything real here is far above 5 MB.
CACHE_FILE="$(vm_image_cache_path "$URL")"
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((5 * 1024 * 1024)) || exit 115
curl -f#SL -o "$(basename "$URL")" "$URL"
FILE=$(basename "$URL")
msg_ok "Downloaded ${CL}${BL}$FILE${CL}"
FILE="$TEMP_DIR/$(basename "${CACHE_FILE%.gz}")"
vm_extract_image "$CACHE_FILE" "$FILE" || exit 115
FILE="$VM_IMAGE_FILE"
gunzip -f "$FILE" >/dev/null 2>&1 || true
FILE="${FILE%.*}"
msg_ok "Extracted OpenWrt Disk Image ${CL}${BL}$FILE${CL}"
vm_claim_vmid
msg_info "Creating OpenWrt VM"
qm create $VMID -cores $CORE_COUNT -memory $RAM_SIZE -name $HN \
-onboot 1 -ostype l26 -scsihw virtio-scsi-pci --tablet 0 >/dev/null
vm_mark_created
vm_import_disk "$VMID" "$FILE" "$STORAGE"
-onboot 1 -ostype l26 -scsihw virtio-scsi-pci --tablet 0
if [[ "$(pvesm status | awk -v s=$STORAGE '$1==s {print $2}')" == "dir" ]]; then
qm set $VMID -efidisk0 ${STORAGE}:0,efitype=4m,size=4M
else
pvesm alloc $STORAGE $VMID vm-$VMID-disk-0 4M >/dev/null
qm set $VMID -efidisk0 ${STORAGE}:vm-$VMID-disk-0,efitype=4m,size=4M
fi
$STD qm set $VMID \
-scsi0 "${VM_IMPORTED_DISK}" \
IMPORT_OUT="$(qm importdisk $VMID $FILE $STORAGE --format raw 2>&1 || true)"
DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p")"
if [[ -z "$DISK_REF" ]]; then
DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$1 ~ ("vm-"id"-disk-") {print $1}' | sort | tail -n1)"
fi
if [[ -z "$DISK_REF" ]]; then
msg_error "Unable to determine imported disk reference."
echo "$IMPORT_OUT"
exit 226
fi
qm set $VMID \
-efidisk0 ${STORAGE}:0,efitype=4m,size=4M \
-scsi0 ${DISK_REF} \
-boot order=scsi0 \
-tags community-script
-tags community-script >/dev/null
msg_ok "Attached disk"
msg_info "Resizing disk to ${DISK_SIZE}"
qm disk resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null
msg_ok "Resized disk to ${DISK_SIZE}"
set_description
msg_ok "Created OpenWrt VM ${CL}${BL}(${HN})"
DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://community-scripts.org' 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>
msg_info "Booting OpenWrt to configure its network interfaces"
$STD qm start $VMID
<h2 style='font-size: 24px; margin: 20px 0;'>OpenWrt VM</h2>
<p style='margin: 16px 0;'>
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
<img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
</a>
</p>
<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 OpenWrt VM ${CL}${BL}(${HN})"
msg_info "OpenWrt is being started in order to configure the network interfaces."
qm start $VMID
sleep 15
VM_STATE=""
msg_info "Waiting for OpenWrt to boot..."
for i in {1..30}; do
if ! VM_STATE="$(qm status "$VMID" 2>&1)"; then
msg_error "VM $VMID no longer exists: ${VM_STATE}"
exit 226
if qm status "$VMID" | grep -q "running"; then
sleep 5
msg_ok "OpenWrt is running"
break
fi
[[ "$VM_STATE" == *running* ]] && break
sleep 1
done
if [[ "$VM_STATE" != *running* ]]; then
msg_error "VM $VMID did not reach running state: ${VM_STATE}"
msg_ok "Network interfaces are being configured as OpenWrt initiates."
if qm status "$VMID" | grep -q "running"; then
send_line_to_vm ""
send_line_to_vm "uci delete network.@device[0]"
send_line_to_vm "uci set network.wan=interface"
send_line_to_vm "uci set network.wan.device=eth1"
send_line_to_vm "uci set network.wan.proto=dhcp"
send_line_to_vm "uci delete network.lan"
send_line_to_vm "uci set network.lan=interface"
send_line_to_vm "uci set network.lan.device=eth0"
send_line_to_vm "uci set network.lan.proto=static"
send_line_to_vm "uci set network.lan.ipaddr=${LAN_IP_ADDR}"
send_line_to_vm "uci set network.lan.netmask=${LAN_NETMASK}"
send_line_to_vm "uci commit"
send_line_to_vm "poweroff"
msg_ok "Network interfaces configured in OpenWrt"
else
msg_error "VM is not running"
exit 226
fi
sleep 5
msg_ok "OpenWrt is running"
msg_info "Configuring network interfaces in OpenWrt"
$STD send_line_to_vm ""
$STD send_line_to_vm "uci delete network.@device[0]"
$STD send_line_to_vm "uci set network.wan=interface"
$STD send_line_to_vm "uci set network.wan.device=eth1"
$STD send_line_to_vm "uci set network.wan.proto=dhcp"
$STD send_line_to_vm "uci delete network.lan"
$STD send_line_to_vm "uci set network.lan=interface"
$STD send_line_to_vm "uci set network.lan.device=eth0"
$STD send_line_to_vm "uci set network.lan.proto=static"
$STD send_line_to_vm "uci set network.lan.ipaddr=${LAN_IP_ADDR}"
$STD send_line_to_vm "uci set network.lan.netmask=${LAN_NETMASK}"
$STD send_line_to_vm "uci commit"
$STD send_line_to_vm "poweroff"
msg_ok "Network interfaces configured in OpenWrt"
msg_info "Waiting for OpenWrt to shut down"
for i in {1..60}; do
VM_STATE="$(qm status "$VMID" 2>&1)" || break
[[ "$VM_STATE" == *stopped* ]] && break
msg_info "Waiting for OpenWrt to shut down..."
until qm status "$VMID" | grep -q "stopped"; do
sleep 2
done
if [[ "$VM_STATE" != *stopped* ]]; then
msg_error "OpenWrt did not shut down: ${VM_STATE}"
exit 226
fi
msg_ok "OpenWrt has shut down"
msg_info "Adding bridge interfaces on Proxmox side"
@@ -349,21 +684,13 @@ msg_ok "Bridge interfaces added"
if [ "$START_VM" = "yes" ]; then
msg_info "Starting OpenWrt VM"
$STD qm start $VMID
qm start $VMID
msg_ok "Started OpenWrt VM"
fi
VLAN_FINISH=""
if [ -z "$VLAN" ] && [ "${VLAN2:-999}" != "999" ]; then
if [ -z "$VLAN" ] && [ "$VLAN2" != "999" ]; then
VLAN_FINISH=" Please remember to adjust the VLAN tags to suit your network."
fi
vm_print_summary \
"LAN URL=http://${LAN_IP_ADDR}" \
"WAN Bridge=${BRG}" \
"LAN Bridge=${LAN_BRG}" \
"OpenWrt Version=${var_version}"
vm_next_steps \
"Open the LAN web UI at http://${LAN_IP_ADDR}." \
"Login as root with a blank initial password, then set a strong password immediately." \
"Keep WAN and LAN isolated appropriately; the defaults put both on vmbr0 until you adjust bridges/VLANs.${VLAN_FINISH}"
vm_finish
post_update_to_api "done" "none"
msg_ok "Completed Successfully!${VLAN_FINISH:+\n$VLAN_FINISH}"
+602 -254
View File
@@ -1,17 +1,27 @@
#!/usr/bin/env bash
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: michelroegl-brunner | MickLesk (CanbiZ)
# Author: michelroegl-brunner
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
function header_info {
clear
cat <<"EOF"
____ ____ _ __
/ __ \/ __ \/ | / /_______ ____ ________
/ / / / /_/ / |/ / ___/ _ \/ __ \/ ___/ _ \
/ /_/ / ____/ /| (__ ) __/ / / (__ ) __/
\____/_/ /_/ |_/____/\___/_/ /_/____/\___/
EOF
}
header_info
echo -e "Loading..."
#API VARIABLES
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
APP="OPNsense"
APP_TYPE="vm"
NSAPP="opnsense-vm"
var_os="opnsense"
var_version="26.7"
@@ -20,20 +30,70 @@ FREEBSD_MAJOR="15"
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
YW=$(echo "\033[33m")
BL=$(echo "\033[36m")
HA=$(echo "\033[1;34m")
THIN="discard=on,ssd=1,"
header_info
echo -e "Loading..."
RD=$(echo "\033[01;31m")
BGN=$(echo "\033[4;92m")
GN=$(echo "\033[1;92m")
DGN=$(echo "\033[32m")
CL=$(echo "\033[m")
BFR="\\r\\033[K"
HOLD="-"
CM="${GN}✓${CL}"
CROSS="${RD}✗${CL}"
set -Eeo pipefail
shopt -s inherit_errexit
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
post_update_to_api "failed" "$exit_code"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
cleanup_vmid
}
vm_require_arch amd64
function get_valid_nextid() {
local try_id
try_id=$(pvesh get /cluster/nextid)
while true; do
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
try_id=$((try_id + 1))
continue
fi
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
try_id=$((try_id + 1))
continue
fi
break
done
echo "$try_id"
}
function cleanup_vmid() {
if qm status $VMID &>/dev/null; then
qm stop $VMID &>/dev/null
qm destroy $VMID &>/dev/null
fi
}
function cleanup() {
local exit_code=$?
popd >/dev/null
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ $exit_code -eq 0 ]]; then
post_update_to_api "done" "none"
else
post_update_to_api "failed" "$exit_code"
fi
fi
rm -rf $TEMP_DIR
}
function check_disk_space() {
local path="$1"
@@ -46,16 +106,16 @@ function check_disk_space() {
return 0
}
TEMP_DIR=$(mktemp -d)
if ! check_disk_space "$TEMP_DIR" 20 && [ -d "/var/tmp" ] && check_disk_space "/var/tmp" 20; then
rm -rf "$TEMP_DIR"
# Use disk-backed temp directory to avoid tmpfs/RAM size limits in /tmp
if [ -d "/var/tmp" ] && check_disk_space "/var/tmp" 20; then
TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX)
elif [ -d "/tmp" ] && check_disk_space "/tmp" 20; then
TEMP_DIR=$(mktemp -d)
else
# Fallback: try /var/tmp anyway, disk space check will catch it later
TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX)
fi
pushd "$TEMP_DIR" >/dev/null
vm_preflight
vm_require_tools xz
pushd $TEMP_DIR >/dev/null
function send_line_to_vm() {
echo -e "${DGN}Sending line: ${YW}$1${CL}"
for ((i = 0; i < ${#1}; i++)); do
@@ -126,139 +186,95 @@ function send_line_to_vm() {
qm sendkey $VMID ret
}
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "OPNsense VM" --yesno "This will create a New OPNsense VM. Proceed?" 10 58); then
:
else
header_info && echo -e "⚠ User exited script \n" && exit
fi
function msg_info() {
local msg="$1"
echo -ne " ${HOLD} ${YW}${msg}..."
}
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}"
}
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.0–8.9
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 105
fi
return 0
fi
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 2)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
exit 105
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.2"
exit 105
}
function arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
echo -e "\n ${CROSS} This script will not work with PiMox! \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 "⚠ User exited script \n"
exit
}
function get_available_bridges() {
ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sort
}
function validate_ip_octets() {
local octet='(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])'
[[ "$1" =~ ^${octet}\.${octet}\.${octet}\.${octet}$ ]]
}
function prompt_router_input() {
local var_name="$1" title="$2" prompt="$3" default_value="$4" value
if vm_dialog inputbox "$title" "$prompt" 8 58 "$default_value" --cancel-button Exit-Script; then
value="$VM_DIALOG_RESULT"
printf -v "$var_name" '%s' "$value"
else
exit_script
fi
}
function prompt_optional_static_ip() {
local ip_var="$1" gw_var="$2" mask_var="$3" prefix="$4" ip_value gw_value mask_value
prompt_router_input "$ip_var" "${prefix} IP ADDRESS" "Set a ${prefix} IP" "${!ip_var:-}"
ip_value="${!ip_var}"
if [ -z "$ip_value" ]; then
echo -e "${DGN}Using DHCP AS ${prefix} IP ADDRESS${CL}"
return 0
fi
if ! validate_ip_octets "$ip_value"; then
msg_error "Invalid IP Address format for ${prefix} IP. Needs to be 0.0.0.0, was $ip_value"
exit 1
fi
echo -e "${DGN}Using ${prefix} IP ADDRESS: ${BGN}$ip_value${CL}"
prompt_router_input "$gw_var" "${prefix} GATEWAY IP ADDRESS" "Set a ${prefix} GATEWAY IP" "${!gw_var:-}"
gw_value="${!gw_var}"
if [ -z "$gw_value" ]; then
msg_error "${prefix} gateway is required for a static IP."
exit 1
fi
if ! validate_ip_octets "$gw_value"; then
msg_error "Invalid IP Address format for ${prefix} Gateway. Needs to be 0.0.0.0, was $gw_value"
exit 1
fi
echo -e "${DGN}Using ${prefix} GATEWAY ADDRESS: ${BGN}$gw_value${CL}"
prompt_router_input "$mask_var" "${prefix} NETMASK" "Set a ${prefix} netmask (24 for example)" "${!mask_var:-}"
mask_value="${!mask_var}"
if [ -z "$mask_value" ]; then
msg_error "${prefix} netmask is required for a static IP."
exit 1
fi
if [[ ! "$mask_value" =~ ^[0-9]+$ || "$mask_value" -lt 1 || "$mask_value" -gt 32 ]]; then
msg_error "Invalid ${prefix} NETMASK format. Needs to be 1-32, was $mask_value"
exit 1
fi
echo -e "${DGN}Using ${prefix} NETMASK: ${BGN}$mask_value${CL}"
}
function prompt_router_mac() {
local var_name="$1" title="$2" prompt="$3" default_value="$4" label="$5" value
prompt_router_input "$var_name" "$title" "$prompt" "$default_value"
value="${!var_name}"
[[ -n "$value" ]] || value="$default_value"
printf -v "$var_name" '%s' "$value"
if ! validate_mac_address "$value"; then
msg_error "Invalid ${label}: $value"
exit 1
fi
echo -e "${DGN}Using ${label}: ${BGN}$value${CL}"
}
function select_network_mode() {
local available_bridges bridge_count default_wan_brg
available_bridges=$(get_available_bridges)
bridge_count=$(echo "$available_bridges" | wc -l)
default_wan_brg=$(echo "$available_bridges" | grep -v "^${BRG}$" | head -n1 || true)
if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
WAN_BRG="${VM_WAN_BRIDGE:-}"
elif [ "$bridge_count" -ge 2 ]; then
if vm_dialog radiolist "NETWORK CONFIGURATION" --cancel-button Exit-Script \
"Choose network setup mode for OPNsense:\n" 14 70 2 \
"dual" "Dual Interface (Firewall/Router) - uses ${default_wan_brg}" ON \
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF; then
if [ "$VM_DIALOG_RESULT" = "dual" ]; then
WAN_BRG="$default_wan_brg"
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
else
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
WAN_BRG=""
fi
else
exit_script
fi
else
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
echo -e "${YW} (Only one bridge detected, dual interface requires a second bridge)${CL}"
WAN_BRG=""
fi
}
function prompt_wan_bridge() {
local wan_bridges wan_menu=() first=true brg
wan_bridges=$(get_available_bridges | grep -v "^${BRG}$" || true)
if [ -z "$wan_bridges" ]; then
WAN_BRG=""
msg_warn "Only one bridge is available; using single-interface mode."
return 0
fi
while IFS= read -r brg; do
if $first; then
wan_menu+=("$brg" "" "ON")
first=false
else
wan_menu+=("$brg" "" "OFF")
fi
done <<<"$wan_bridges"
if vm_dialog radiolist "WAN BRIDGE" "Select WAN Bridge" 14 58 6 "${wan_menu[@]}"; then
WAN_BRG="$VM_DIALOG_RESULT"
[[ -n "$WAN_BRG" ]] || WAN_BRG=$(echo "$wan_bridges" | head -n1)
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
else
exit_script
fi
}
function default_settings() {
vm_apply_machine_type "i440fx"
VMID=$(get_valid_nextid)
DISK_SIZE="20G"
FORMAT=",efitype=4m"
MACHINE=""
DISK_CACHE=""
@@ -281,6 +297,12 @@ function default_settings() {
START_VM="yes"
METHOD="default"
# Detect available bridges
local AVAILABLE_BRIDGES
AVAILABLE_BRIDGES=$(get_available_bridges)
local BRIDGE_COUNT
BRIDGE_COUNT=$(echo "$AVAILABLE_BRIDGES" | wc -l)
echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}"
echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
@@ -293,55 +315,289 @@ function default_settings() {
fi
echo -e "${DGN}Using LAN VLAN: ${BGN}Default${CL}"
echo -e "${DGN}Using LAN MAC Address: ${BGN}${MAC}${CL}"
select_network_mode
# Determine available network modes based on bridge count
local DEFAULT_WAN_BRG
DEFAULT_WAN_BRG=$(echo "$AVAILABLE_BRIDGES" | grep -v "^${BRG}$" | head -n1 || true)
if [ "$BRIDGE_COUNT" -ge 2 ]; then
# Multiple bridges available - offer dual or single mode
if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \
"Choose network setup mode for OPNsense:\n" 14 70 2 \
"dual" "Dual Interface (Firewall/Router) - uses ${DEFAULT_WAN_BRG}" ON \
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF \
3>&1 1>&2 2>&3); then
if [ "$NETWORK_MODE" = "dual" ]; then
WAN_BRG="$DEFAULT_WAN_BRG"
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
else
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
WAN_BRG=""
fi
else
exit-script
fi
else
# Only one bridge available - single interface mode only
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
echo -e "${YW} (Only one bridge detected, dual interface requires a second bridge)${CL}"
WAN_BRG=""
fi
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}"
echo -e "${BL}Creating a OPNsense VM using the above default settings${CL}"
}
function advanced_settings() {
local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
METHOD="advanced"
IP_ADDR=""
WAN_IP_ADDR=""
LAN_GW=""
WAN_GW=""
NETMASK=""
WAN_NETMASK=""
VLAN=""
MTU=""
vm_prompt_disk_size "20G"
vm_prompt_keyboard
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "i440fx"
vm_prompt_cpu_model "kvm64"
vm_prompt_disk_cache "none"
vm_prompt_hostname "opnsense"
vm_prompt_cpu_cores "4"
vm_prompt_ram "8192"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
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 [ -z "$VMID" ]; then
VMID=$(get_valid_nextid)
fi
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
sleep 2
continue
fi
echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break
else
exit-script
fi
done
prompt_router_input "BRG" "LAN BRIDGE" "Set a LAN Bridge" "vmbr0"
[[ -n "$BRG" ]] || BRG="vmbr0"
if ! ip link show "${BRG}" &>/dev/null; then
msg_error "Bridge '${BRG}' does not exist"
exit 1
fi
echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}"
prompt_optional_static_ip "IP_ADDR" "LAN_GW" "NETMASK" "LAN"
prompt_wan_bridge
if [[ -n "$WAN_BRG" ]]; then
prompt_optional_static_ip "WAN_IP_ADDR" "WAN_GW" "WAN_NETMASK" "WAN"
fi
prompt_router_mac "MAC" "LAN MAC ADDRESS" "Set a LAN MAC Address" "$GEN_MAC" "LAN MAC address"
if [[ -n "$WAN_BRG" ]]; then
prompt_router_mac "WAN_MAC" "WAN MAC ADDRESS" "Set a WAN MAC Address" "$GEN_MAC_LAN" "WAN MAC address"
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
"i440fx" "Machine i440fx" ON \
"q35" "Machine q35" OFF \
3>&1 1>&2 2>&3); then
if [ $MACH = q35 ]; then
echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}"
FORMAT=""
MACHINE=" -machine q35"
else
echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}"
FORMAT=",efitype=4m"
MACHINE=""
fi
else
WAN_MAC="$GEN_MAC_LAN"
exit-script
fi
if vm_confirm_advanced_settings "Ready to create OPNsense VM?"; then
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"0" "KVM64 (Default)" ON \
"1" "Host" OFF \
3>&1 1>&2 2>&3); then
if [ $CPU_TYPE1 = "1" ]; then
echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}"
CPU_TYPE=" -cpu host"
else
echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}"
CPU_TYPE=""
fi
else
exit-script
fi
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 \
"1" "Write Through" OFF \
3>&1 1>&2 2>&3); then
if [ $DISK_CACHE = "1" ]; then
echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}"
DISK_CACHE="cache=writethrough,"
else
echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}"
DISK_CACHE=""
fi
else
exit-script
fi
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 OPNsense --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$VM_NAME" ]; then
HN="OPNsense"
else
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
if [ "$HN" != "${VM_NAME,,}" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
fi
fi
echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}"
else
exit-script
fi
while true; do
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 4 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="4"; fi
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 4)." 8 58
else
exit-script
fi
done
while true; do
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 8192 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="8192"; fi
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"
break
fi
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 8192)." 8 58
else
exit-script
fi
done
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Bridge" 8 58 vmbr0 --title "LAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $BRG ]; then
BRG="vmbr0"
fi
if ! ip link show "${BRG}" &>/dev/null; then
msg_error "Bridge '${BRG}' does not exist"
exit
fi
echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}"
else
exit-script
fi
if IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN IP" 8 58 $IP_ADDR --title "LAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $IP_ADDR ]; then
echo -e "${DGN}Using DHCP AS LAN IP ADDRESS${CL}"
else
if [[ -n "$IP_ADDR" && ! "$IP_ADDR" =~ $ip_regex ]]; then
msg_error "Invalid IP Address format for LAN IP. Needs to be 0.0.0.0, was $IP_ADDR"
exit
fi
echo -e "${DGN}Using LAN IP ADDRESS: ${BGN}$IP_ADDR${CL}"
if LAN_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN GATEWAY IP" 8 58 $LAN_GW --title "LAN GATEWAY IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $LAN_GW ]; then
echo -e "${DGN}Gateway needs to be set if ip is not dhcp${CL}"
exit-script
fi
if [[ -n "$LAN_GW" && ! "$LAN_GW" =~ $ip_regex ]]; then
msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was $LAN_GW"
exit
fi
echo -e "${DGN}Using LAN GATEWAY ADDRESS: ${BGN}$LAN_GW${CL}"
fi
if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $NETMASK ]; then
echo -e "${DGN}Netmask needs to be set if ip is not dhcp${CL}"
fi
if [[ -n "$NETMASK" && ! ("$NETMASK" =~ ^[0-9]+$ && "$NETMASK" -ge 1 && "$NETMASK" -le 32) ]]; then
msg_error "Invalid LAN NETMASK format. Needs to be 1-32, was $NETMASK"
exit
fi
echo -e "${DGN}Using LAN NETMASK: ${BGN}$NETMASK${CL}"
else
exit-script
fi
fi
else
exit-script
fi
# Build WAN bridge selection from available bridges (excluding LAN bridge)
local WAN_BRIDGES
WAN_BRIDGES=$(get_available_bridges | grep -v "^${BRG}$" || true)
if [ -z "$WAN_BRIDGES" ]; then
msg_error "No additional bridge available for WAN. Only '${BRG}' exists."
msg_error "Create a second bridge (e.g. vmbr1) in Proxmox network config first."
exit
fi
local WAN_MENU=()
local first=true
while IFS= read -r brg; do
if $first; then
WAN_MENU+=("$brg" "" "ON")
first=false
else
WAN_MENU+=("$brg" "" "OFF")
fi
done <<<"$WAN_BRIDGES"
if WAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "WAN BRIDGE" --radiolist "Select WAN Bridge" 14 58 6 \
"${WAN_MENU[@]}" 3>&1 1>&2 2>&3); then
if [ -z "$WAN_BRG" ]; then
WAN_BRG=$(echo "$WAN_BRIDGES" | head -n1)
fi
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
else
exit-script
fi
if WAN_IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN IP" 8 58 $WAN_IP_ADDR --title "WAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $WAN_IP_ADDR ]; then
echo -e "${DGN}Using DHCP AS WAN IP ADDRESS${CL}"
else
if [[ -n "$WAN_IP_ADDR" && ! "$WAN_IP_ADDR" =~ $ip_regex ]]; then
msg_error "Invalid IP Address format for WAN IP. Needs to be 0.0.0.0, was $WAN_IP_ADDR"
exit
fi
echo -e "${DGN}Using WAN IP ADDRESS: ${BGN}$WAN_IP_ADDR${CL}"
if WAN_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN GATEWAY IP" 8 58 $WAN_GW --title "WAN GATEWAY IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $WAN_GW ]; then
echo -e "${DGN}Gateway needs to be set if ip is not dhcp${CL}"
exit-script
fi
if [[ -n "$WAN_GW" && ! "$WAN_GW" =~ $ip_regex ]]; then
msg_error "Invalid IP Address format for WAN Gateway. Needs to be 0.0.0.0, was $WAN_GW"
exit
fi
echo -e "${DGN}Using WAN GATEWAY ADDRESS: ${BGN}$WAN_GW${CL}"
else
exit-script
fi
if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $WAN_NETMASK ]; then
echo -e "${DGN}WAN Netmask needs to be set if ip is not dhcp${CL}"
fi
if [[ -n "$WAN_NETMASK" && ! ("$WAN_NETMASK" =~ ^[0-9]+$ && "$WAN_NETMASK" -ge 1 && "$WAN_NETMASK" -le 32) ]]; then
msg_error "Invalid WAN NETMASK format. Needs to be 1-32, was $WAN_NETMASK"
exit
fi
echo -e "${DGN}Using WAN NETMASK: ${BGN}$WAN_NETMASK${CL}"
else
exit-script
fi
fi
else
exit-script
fi
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC1 ]; then
MAC="$GEN_MAC"
else
MAC="$MAC1"
fi
echo -e "${DGN}Using LAN MAC Address: ${BGN}$MAC${CL}"
else
exit-script
fi
if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC_LAN --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC2 ]; then
WAN_MAC="$GEN_MAC_LAN"
else
WAN_MAC="$MAC2"
fi
echo -e "${DGN}Using WAN MAC Address: ${BGN}$WAN_MAC${CL}"
else
exit-script
fi
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create OPNsense VM?" --no-button Do-Over 10 58); then
echo -e "${RD}Creating a OPNsense VM using the above advanced settings${CL}"
else
header_info
@@ -350,18 +606,75 @@ function advanced_settings() {
fi
}
vm_start_script "Use Default Settings?\n\nDefaults:\n• 4 CPU Cores\n• 8 GB RAM\n• 20 GB Disk" 13 58
function start_script() {
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
header_info
echo -e "${BL}Using Default Settings${CL}"
default_settings
else
header_info
echo -e "${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
arch_check
pve_check
ssh_check
start_script
post_to_api_vm
vm_select_storage "$HN"
msg_info "Validating Storage"
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
ITEM=" Type: $TYPE Free: $FREE "
OFFSET=2
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
fi
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
msg_error "Unable to detect a valid storage location."
exit
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
else
while [ -z "${STORAGE:+x}" ]; do
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" \
16 $(($MSG_MAX_LENGTH + 23)) 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
done
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image"
vm_latest_from_index "https://download.freebsd.org/releases/VM-IMAGES/" "${FREEBSD_MAJOR}\.[0-9]+-RELEASE" \
--probe "https://download.freebsd.org/releases/VM-IMAGES/{}/amd64/Latest/FreeBSD-{}-amd64.qcow2.xz" \
--probe "https://download.freebsd.org/releases/VM-IMAGES/{}/amd64/Latest/FreeBSD-{}-amd64-ufs.qcow2.xz" \
--probe "https://download.freebsd.org/releases/VM-IMAGES/{}/amd64/Latest/FreeBSD-{}-amd64-zfs.qcow2.xz" || exit 115
FREEBSD_VER="$VM_INDEX_LATEST"
URL="$VM_INDEX_URL"
# Use latest stable FreeBSD amd64 qcow2 VM image matching FREEBSD_MAJOR
RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ |
grep -Eo "${FREEBSD_MAJOR}\.[0-9]+-RELEASE" |
sort -Vr |
uniq)"
URL=""
FREEBSD_VER=""
for ver in $RELEASE_LIST; do
# FreeBSD 15+ publishes separate -ufs/-zfs images instead of a generic one
for variant in "" "-ufs" "-zfs"; do
candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64${variant}.qcow2.xz"
if curl -fsI "$candidate" >/dev/null 2>&1; then
FREEBSD_VER="$ver"
URL="$candidate"
break 2
fi
done
done
if [ -z "$URL" ]; then
msg_error "Could not find a FreeBSD ${FREEBSD_MAJOR}.x amd64 qcow2 image."
exit 115
fi
msg_ok "Download URL: ${CL}${BL}${URL}${CL}"
# Check available disk space (require at least 20GB for safety)
@@ -374,12 +687,9 @@ if ! check_disk_space "$TEMP_DIR" 20; then
fi
msg_info "Downloading FreeBSD Image"
# A mirror serving an error page returns 200, so size decides whether this
# is an image. Anything real here is far above 5 MB.
CACHE_FILE="$(vm_image_cache_path "$URL")"
vm_fetch_image "$URL" "$CACHE_FILE" --cache --verify-xz --min-bytes $((5 * 1024 * 1024)) || exit 1
curl -f#SL -o "$(basename "$URL")" "$URL"
echo -en "\e[1A\e[0K"
msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}"
msg_ok "Downloaded ${CL}${BL}$(basename "$URL")${CL}"
# Check disk space again before decompression
if ! check_disk_space "$TEMP_DIR" 15; then
@@ -389,19 +699,49 @@ if ! check_disk_space "$TEMP_DIR" 15; then
exit 214
fi
FILE="$TEMP_DIR/FreeBSD.qcow2"
vm_extract_image "$CACHE_FILE" "$FILE" || exit 115
FILE="$VM_IMAGE_FILE"
msg_info "Decompressing FreeBSD Image (this may take a few minutes)"
FILE=FreeBSD.qcow2
if ! unxz -cv $(basename $URL) >${FILE}; then
msg_error "Failed to decompress FreeBSD image."
msg_error "This is usually caused by insufficient disk space."
df -h "$TEMP_DIR"
exit 115
fi
vm_define_disk_references 1
# Remove the compressed file to save space
rm -f "$(basename "$URL")"
msg_ok "Decompressed ${CL}${BL}${FILE}${CL}"
STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in
nfs | dir)
DISK_EXT=".qcow2"
DISK_REF="$VMID/"
DISK_IMPORT="-format qcow2"
THIN=""
;;
btrfs)
DISK_EXT=".raw"
DISK_REF="$VMID/"
DISK_IMPORT="-format raw"
FORMAT=",efitype=4m"
THIN=""
;;
*)
DISK_EXT=""
DISK_REF=""
DISK_IMPORT="-format raw"
;;
esac
for i in {0,1}; do
disk="DISK$i"
eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-}
eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk}
done
vm_claim_vmid
msg_info "Creating a OPNsense VM"
# A firewall VM filters itself: Proxmox's per-NIC firewall off, and virtio
# multiqueue matched to the cores so routing scales past one vCPU.
qm create $VMID -agent 1${MACHINE} -tablet 0 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE -balloon 0 \
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC,firewall=0,queues=$CORE_COUNT$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
vm_mark_created
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
# Retry pvesm alloc on transient zfs_request "got timeout" errors (#14127)
alloc_attempt=1
@@ -420,34 +760,66 @@ while :; do
echo -e "$alloc_err" >&2
exit 220
done
vm_import_disk "$VMID" "$FILE" "$STORAGE"
qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} &>/dev/null
qm set $VMID \
-efidisk0 ${DISK0_REF}${FORMAT} \
-scsi0 "${VM_IMPORTED_DISK}",${DISK_CACHE}${THIN}size=2G \
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=2G \
-boot order=scsi0 \
-serial0 socket \
-tags community-script >/dev/null
vm_resize_disk
set_description
qm resize $VMID scsi0 20G >/dev/null
DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://community-scripts.org' 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>
<h2 style='font-size: 24px; margin: 20px 0;'>OPNsense VM</h2>
<p style='margin: 16px 0;'>
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
<img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
</a>
</p>
<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_info "Bridge interfaces are being added."
qm set $VMID \
-net0 virtio,bridge=${BRG},macaddr=${MAC},firewall=0,queues=${CORE_COUNT}${VLAN}${MTU} 2>/dev/null
if [ -n "$WAN_BRG" ]; then
qm set $VMID \
-net1 virtio,bridge=${WAN_BRG},macaddr=${WAN_MAC},firewall=0,queues=${CORE_COUNT} 2>/dev/null
fi
-net0 virtio,bridge=${BRG},macaddr=${MAC}${VLAN}${MTU} 2>/dev/null
msg_ok "Bridge interfaces have been successfully added."
msg_ok "Created a OPNsense VM ${CL}${BL}(${HN})"
msg_ok "Starting OPNsense VM (the bootstrap takes 10-30 minutes)"
$STD qm start $VMID
msg_ok "Starting OPNsense VM (Patience this takes 20-30 minutes)"
qm start $VMID
sleep 90
send_line_to_vm "root"
sleep 2
send_line_to_vm ""
send_line_to_vm "for i in \$(seq 1 60); do fetch -q https://raw.githubusercontent.com/opnsense/update/master/src/bootstrap/opnsense-bootstrap.sh.in && break; sleep 5; done"
sleep 5
send_line_to_vm "fetch https://raw.githubusercontent.com/opnsense/update/master/src/bootstrap/opnsense-bootstrap.sh.in"
if [ -n "$WAN_BRG" ]; then
msg_info "Adding WAN interface"
qm set $VMID \
-net1 virtio,bridge=${WAN_BRG},macaddr=${WAN_MAC} &>/dev/null
msg_ok "WAN interface added"
sleep 5 # Brief pause after adding network interface
fi
# FreeBSD 15+ VM images ship the base system as pkgbase packages; the bootstrap's
# "delete all packages" step would remove the running base system (/bin/rm etc.)
# and brick the VM. Deregister them from the pkg db first - the files stay in
@@ -505,22 +877,13 @@ while [ $build_stable -lt 6 ] && [ $build_elapsed -lt 2400 ]; do
fi
# No working screendump after several attempts: fixed wait instead
if [ $screen_ok -eq 0 ] && [ $build_elapsed -ge 480 ]; then
msg_warn "Console screendump not available on this system - falling back to a fixed wait (12 minutes)."
msg_error "Console screendump not available on this system - falling back to a fixed wait (12 minutes)."
sleep 720
build_elapsed=$((build_elapsed + 720))
break
fi
done
if [ $build_stable -ge 6 ]; then
msg_ok "OPNsense console settled after $((build_elapsed / 60)) minutes"
else
msg_warn "The console did not settle within $((build_elapsed / 60)) minutes; continuing, verify the OPNsense console afterwards"
fi
# The answers below are matched to OPNsense's console dialog (setaddr.php).
# One prompt ("via WAN tracking?") only appears when WAN has DHCP6, so a spare
# "n" is sent for it; where it is absent, OPNsense re-asks the IPv6 address and
# the following ENTER lands there. Invalid answers are re-asked, empty ones take
# the default, which keeps the sequence safe in both dialog variants.
msg_ok "OPNsense build finished after $((build_elapsed / 60)) minutes"
send_line_to_vm "root"
send_line_to_vm "opnsense"
send_line_to_vm "2"
@@ -570,28 +933,13 @@ if [ -n "$WAN_BRG" ] && [ "$WAN_IP_ADDR" != "" ]; then
fi
sleep 10
send_line_to_vm "0"
if [[ "$START_VM" == "no" ]]; then
msg_info "Shutting down OPNsense as requested"
$STD qm shutdown "$VMID" --timeout 120
msg_ok "OPNsense VM shut down"
else
msg_ok "OPNsense VM is running"
fi
msg_ok "Started OPNsense VM"
msg_ok "Completed successfully!\n"
if [ "$IP_ADDR" != "" ]; then
LAN_URL="http://${IP_ADDR}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP_ADDR}${CL}"
else
LAN_URL="DHCP - check the OPNsense console or your leases"
echo -e "${INFO}${YW} LAN IP was DHCP.${CL}"
echo -e "${INFO}${BGN}To find the IP login to the VM shell${CL}"
fi
vm_print_summary \
"LAN URL=${LAN_URL}" \
"LAN Bridge=${BRG}" \
"WAN Bridge=${WAN_BRG:-single-interface mode}" \
"OPNsense Version=${var_version}" \
"FreeBSD Base=${FREEBSD_VER}"
vm_next_steps \
"Verify the guest bootstrap and network configuration in the OPNsense console." \
"Login as root with password opnsense after successful bootstrap, then change the password immediately." \
"Keep WAN and LAN isolated appropriately; single-interface mode is intended for proxy, VPN, or IDS use cases."
vm_finish "VM creation completed; verify the guest bootstrap and network configuration in the console."
+3 -3
View File
@@ -149,8 +149,8 @@ fi
FULL_URL="https://download.truenas.com/${SELECTED_ISO#/}"
ISO_NAME=$(basename "$FULL_URL")
vm_select_iso_storage "$ISO_NAME" "$HN"
CACHE_FILE="$ISO_PATH"
CACHE_DIR="/var/lib/vz/template/iso"
CACHE_FILE="$CACHE_DIR/$ISO_NAME"
msg_info "Retrieving the ISO for the TrueNAS Disk Image"
MIN_ISO_BYTES=$((500 * 1024 * 1024))
@@ -162,7 +162,7 @@ qm create "$VMID"${MACHINE} -bios ovmf -agent enabled=1 -tablet 0 -localtime 1${
-cores "$CORE_COUNT" -memory "$RAM_SIZE" -balloon 0 -name "$HN" -tags community-script \
-net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 \
-efidisk0 $STORAGE:1,efitype=4m,pre-enrolled-keys=0 -sata0 ${STORAGE}:${DISK_SIZE%G},ssd=1 \
-scsihw virtio-scsi-single -cdrom "$ISO_VOLUME" -boot order='sata0;ide2' -vga virtio >/dev/null
-scsihw virtio-scsi-single -cdrom local:iso/$ISO_NAME -boot order='sata0;ide2' -vga virtio >/dev/null
msg_ok "Created VM shell"
if [ "$IMPORT_DISKS" == "yes" ]; then
+63 -66
View File
@@ -1,9 +1,10 @@
#!/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
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
@@ -18,51 +19,55 @@ var_version="26.04"
THIN="discard=on,ssd=1,"
USE_CLOUD_INIT="no"
set -Eeo pipefail
shopt -s inherit_errexit
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
vm_preflight
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
vm_preflight
vm_require_tools virt-customize
if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
var_version="${VM_OS_VERSION:-$var_version}"
elif vm_dialog radiolist "UBUNTU VERSION" "Choose the Ubuntu release to install" --cancel-button Exit-Script 13 60 4 \
"26.04" "Ubuntu 26.04 LTS (Resolute)" ON \
"25.04" "Ubuntu 25.04 (Plucky)" OFF \
"24.04" "Ubuntu 24.04 LTS (Noble)" OFF \
"22.04" "Ubuntu 22.04 LTS (Jammy)" OFF; then
var_version="$VM_DIALOG_RESULT"
else
exit_script
fi
function select_os() {
if [[ -n "${1:-}" ]]; then
var_version="$1"
elif [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
var_version="${VM_OS_VERSION:-$var_version}"
elif vm_dialog radiolist "UBUNTU VERSION" "Choose the Ubuntu release to install" --cancel-button Exit-Script 13 60 4 \
"26.04" "Ubuntu 26.04 LTS (Resolute)" ON \
"24.04" "Ubuntu 24.04 LTS (Noble)" OFF \
"22.04" "Ubuntu 22.04 LTS (Jammy)" OFF; then
var_version="$VM_DIALOG_RESULT"
else
exit_script
fi
case "$var_version" in
26.04) UBUNTU_CODENAME="resolute" ;;
24.04) UBUNTU_CODENAME="noble" ;;
22.04) UBUNTU_CODENAME="jammy" ;;
*)
msg_error "Unsupported Ubuntu version '${var_version}'"
exit 1
;;
esac
APP="Ubuntu ${var_version}"
}
case "$var_version" in
26.04) UBUNTU_CODENAME="resolute" ;;
25.04) UBUNTU_CODENAME="plucky" ;;
24.04) UBUNTU_CODENAME="noble" ;;
22.04) UBUNTU_CODENAME="jammy" ;;
*)
msg_error "Unsupported Ubuntu version '${var_version}'"
exit 1
;;
esac
APP="Ubuntu ${var_version} VM"
header_info
echo -e "\n Loading..."
# Ubuntu cloud images configure netplan from cloud-init only. Without it the
# guest boots with an interface that never gets an address.
VM_CLOUD_INIT="${VM_CLOUD_INIT:-yes}"
vm_prompt_cloud_init "ubuntu"
if [ "$USE_CLOUD_INIT" != "yes" ]; then
msg_warn "Without Cloud-Init this Ubuntu image gets no network configuration - configure it in the guest yourself."
fi
function default_settings() {
select_os "${VM_OS_VERSION:-}"
VMID=$(get_valid_nextid)
vm_apply_machine_type "q35"
DISK_SIZE="7G"
@@ -97,7 +102,6 @@ function default_settings() {
function advanced_settings() {
METHOD="advanced"
select_os
echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "q35"
@@ -111,7 +115,6 @@ function advanced_settings() {
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
vm_prompt_keyboard
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
@@ -125,14 +128,11 @@ function advanced_settings() {
}
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 7 GB Disk" 13 58
VM_CLOUD_INIT="${VM_CLOUD_INIT:-yes}"
vm_prompt_cloud_init "ubuntu"
if [ "$USE_CLOUD_INIT" != "yes" ]; then
msg_warn "Without Cloud-Init this Ubuntu image gets no network configuration or credentials; configure them in the guest yourself."
fi
post_to_api_vm
vm_select_storage "$HN"
vm_define_disk_references 2
DISK_IMPORT="-format ${DISK_IMPORT_FORMAT}"
msg_info "Retrieving the URL for the ${APP} Disk Image"
UBUNTU_ARCH="$(vm_arch_resolve amd64 arm64)"
@@ -147,31 +147,33 @@ FILE="$(basename "$CACHE_FILE")"
cp -f "$CACHE_FILE" "$FILE"
vm_prepare_cloud_image "$FILE" "$HN" || true
if [[ "$USE_CLOUD_INIT" != "yes" ]]; then
vm_expand_image "$FILE" "$DISK_SIZE"
fi
vm_claim_vmid
msg_info "Creating a ${APP}"
qm create $VMID -agent 1${MACHINE} -tablet 0 -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
vm_mark_created
vm_alloc_efi_disk "$DISK0"
vm_import_disk "$VMID" "$FILE" "$STORAGE" "$DISK_IMPORT_FORMAT"
qm importdisk $VMID $FILE $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
qm set $VMID \
-efidisk0 ${DISK0_REF}${FORMAT} \
-scsi0 ${VM_IMPORTED_DISK},${DISK_CACHE}${THIN}size=${DISK_SIZE} \
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \
-boot order=scsi0 \
-serial0 socket >/dev/null
set_description
vm_resize_disk
vm_provision "$VMID"
if [[ "$USE_CLOUD_INIT" == "yes" && -n "${CLOUDINIT_SSH_KEYS:-}" ]]; then
$STD qm set "$VMID" --sshkeys "$CLOUDINIT_SSH_KEYS"
fi
if [ "$USE_CLOUD_INIT" = "yes" ]; then
if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1; then
setup_cloud_init \
"$VMID" \
"$STORAGE" \
"$HN" \
"yes" \
"${CLOUDINIT_USER:-ubuntu}" \
"${CLOUDINIT_NETWORK_MODE:-dhcp}" \
"${CLOUDINIT_IP:-}" \
"${CLOUDINIT_GW:-}" \
"${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}"
if [[ "${CLOUDINIT_NETWORK_MODE:-dhcp}" == "static" ]]; then
setup_cloud_init_network_no_rename \
"$VMID" \
@@ -184,21 +186,16 @@ if [ "$USE_CLOUD_INIT" = "yes" ]; then
fi
msg_ok "Created a ${APP} ${CL}${BL}(${HN})"
vm_start_vm "$APP"
vm_wait_for_ip 120 || true
if [ "$START_VM" = "yes" ]; then
msg_info "Starting ${APP}"
$STD qm start $VMID
msg_ok "Started ${APP}"
fi
post_update_to_api "done" "none"
msg_ok "Completed successfully!\n"
if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f display_cloud_init_info >/dev/null 2>&1; then
display_cloud_init_info "$VMID" "$HN"
else
echo -e "Cloud-Init is disabled. The VM disk was resized on the Proxmox side only.\nIf the guest does not auto-expand its root filesystem after first boot, expand it manually inside the VM.\n\nMore info at https://github.com/community-scripts/ProxmoxVED/discussions/272 \n"
fi
vm_print_summary \
"Ubuntu Release=${var_version} (${UBUNTU_CODENAME})" \
"Cloud-Init=${USE_CLOUD_INIT}"
if [ "$USE_CLOUD_INIT" != "yes" ]; then
vm_next_steps \
"Configure guest networking and credentials manually from the VM console." \
"The root filesystem was expanded offline where host tools allowed it."
fi
vm_finish
+4 -3
View File
@@ -96,8 +96,9 @@ URL="https://download.umbrel.com/release/${UMBREL_RELEASE}/umbrelos-amd64-usb-in
# The upstream file name is the same for every release, so the version goes into
# the cached name -- otherwise the cache serves 1.7.4 to someone asking for 2.0.
ISO_NAME="umbrelos-${UMBREL_RELEASE}-amd64-usb-installer.iso"
vm_select_iso_storage "$ISO_NAME" "$HN"
CACHE_FILE="$ISO_PATH"
CACHE_DIR="/var/lib/vz/template/iso"
CACHE_FILE="${CACHE_DIR}/${ISO_NAME}"
mkdir -p "$CACHE_DIR"
msg_ok "${CL}${BL}${URL}${CL}"
# download.umbrel.com answers 307 for any name at all, so a redirect proves
@@ -113,7 +114,7 @@ qm create "$VMID"${MACHINE} -bios ovmf -agent enabled=1 -tablet 0 -localtime 1 $
-net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \
-efidisk0 "${STORAGE}:1,efitype=4m,pre-enrolled-keys=0" \
-scsi0 "${STORAGE}:${DISK_SIZE%G},${DISK_CACHE:-}${THIN%,}" \
-cdrom "$ISO_VOLUME" -boot order='scsi0;ide2' >/dev/null
-cdrom "local:iso/${ISO_NAME}" -boot order='scsi0;ide2' >/dev/null
set_description
msg_ok "Created a Umbrel OS VM ${CL}${BL}(${HN})"
-436
View File
@@ -1,436 +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
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
APP="UniFi OS Server"
APP_TYPE="vm"
NSAPP="unifi-os-server-vm"
var_os="debian"
var_version="13"
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
CLOUDINIT_REQUIRED=1
OS_TYPE=""
OS_VERSION=""
OS_CODENAME=""
OS_DISPLAY=""
THIN="discard=on,ssd=1,"
header_info
echo -e "\n Loading..."
set -Eeuo pipefail
shopt -s inherit_errexit
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
vm_require_arch amd64
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
vm_preflight
vm_require_tools curl jq virt-customize
function select_os() {
if [[ -n "${1:-}" ]]; then
OS_CHOICE="$1"
elif [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
OS_CHOICE="${VM_OS_VERSION:-debian13}"
elif vm_dialog radiolist "SELECT OS" "Choose Operating System for UniFi OS VM" 12 68 2 \
"debian13" "Debian 13 (Trixie) - Latest" ON \
"ubuntu2404" "Ubuntu 24.04 LTS (Noble)" OFF; then
OS_CHOICE="$VM_DIALOG_RESULT"
else
exit_script
fi
case $OS_CHOICE in
debian13)
OS_TYPE="debian"
OS_VERSION="13"
OS_CODENAME="trixie"
OS_DISPLAY="Debian 13 (Trixie)"
var_os="debian"
var_version="13"
;;
ubuntu2404)
OS_TYPE="ubuntu"
OS_VERSION="24.04"
OS_CODENAME="noble"
OS_DISPLAY="Ubuntu 24.04 LTS"
var_os="ubuntu"
var_version="24.04"
;;
*)
msg_error "Unsupported OS '${OS_CHOICE}' (expected debian13 or ubuntu2404)"
exit 1
;;
esac
}
function get_image_url() {
local arch
arch=$(vm_arch_resolve amd64 arm64)
case $OS_TYPE in
debian)
# Always use Cloud-Init variant for UniFi OS
echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-generic-${arch}.qcow2"
;;
ubuntu)
# Ubuntu only has cloudimg variant (always with Cloud-Init support)
echo "https://cloud-images.ubuntu.com/${OS_CODENAME}/current/${OS_CODENAME}-server-cloudimg-${arch}.img"
;;
esac
}
function default_settings() {
vm_apply_machine_type "q35"
select_os "${VM_OS_VERSION:-}"
# Set defaults for other settings
VMID=$(get_valid_nextid)
DISK_CACHE=""
DISK_SIZE="32G"
HN="unifi-server-os"
CPU_TYPE=" -cpu host"
CORE_COUNT="2"
RAM_SIZE="6144"
BRG="vmbr0"
MAC="$GEN_MAC"
VLAN=""
MTU=""
START_VM="yes"
METHOD="default"
vm_echo_default_settings
}
function advanced_settings() {
METHOD="advanced"
select_os
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "q35"
vm_prompt_disk_size "32G"
vm_prompt_disk_cache "none"
vm_prompt_hostname "unifi-server-os"
vm_prompt_cpu_model "host"
vm_prompt_cpu_cores "2"
vm_prompt_ram "6144"
vm_prompt_bridge "vmbr0"
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
vm_prompt_keyboard
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
if vm_confirm_advanced_settings "Ready to create a UniFi OS Server VM?"; then
echo -e "${CREATING}${BOLD}${DGN}Creating a UniFi OS Server VM using the above advanced settings${CL}"
else
header_info
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 6 GB RAM\n• 32 GB Disk\n• Cloud-Init enabled" 14 58
if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
CLOUDINIT_PASSWORD="${CLOUDINIT_PASSWORD:-${VM_ROOT_PASSWORD:-}}"
fi
vm_prompt_cloud_init "root"
if [[ "${USE_CLOUD_INIT:-no}" != "yes" ]]; then
msg_error "UniFi OS Server requires Cloud-Init"
exit 1
fi
if ! declare -f setup_cloud_init >/dev/null; then
msg_error "Required Cloud-Init helpers are unavailable"
exit 1
fi
if [[ "${VM_UNATTENDED:-0}" == "1" && -n "${VM_SSH_KEYS:-}" && -z "${CLOUDINIT_SSH_KEYS:-}" ]]; then
if [[ -f "$VM_SSH_KEYS" ]]; then
cp "$VM_SSH_KEYS" "$TEMP_DIR/ssh-keys-input"
else
printf '%s\n' "$VM_SSH_KEYS" >"$TEMP_DIR/ssh-keys-input"
fi
CLOUDINIT_SSH_KEYS="$TEMP_DIR/ssh-keys.pub"
_ci_ssh_extract_keys_from_file "$TEMP_DIR/ssh-keys-input" >"$CLOUDINIT_SSH_KEYS"
if [[ ! -s "$CLOUDINIT_SSH_KEYS" ]] || ! ssh-keygen -lf "$CLOUDINIT_SSH_KEYS" >/dev/null; then
msg_error "VM_SSH_KEYS must contain valid SSH public keys or name a public-key file"
exit 1
fi
fi
post_to_api_vm
msg_info "Checking system resources"
SYSTEM_RAM_GB=$(grep MemTotal /proc/meminfo | awk '{printf "%.0f", $2 / 1024 / 1024}')
SYSTEM_SWAP_GB=$(grep SwapTotal /proc/meminfo | awk '{printf "%.0f", $2 / 1024 / 1024}')
SYSTEM_FREE_DISK_GB=$(df -BG / | awk 'NR==2 {print $4}' | sed 's/G//')
if [[ ${SYSTEM_RAM_GB} -lt 4 ]]; then
msg_error "Warning: Less than 4GB RAM detected (${SYSTEM_RAM_GB}GB). Install may be slow."
sleep 3
fi
if [[ ${SYSTEM_FREE_DISK_GB} -lt 10 ]]; then
msg_error "Warning: Less than 10GB free disk detected. Install may fail."
sleep 3
fi
msg_ok "System resources: ${SYSTEM_RAM_GB}GB RAM, ${SYSTEM_FREE_DISK_GB}GB free disk"
if command -v ufw &>/dev/null; then
if ufw status verbose | grep -q "Status: active"; then
msg_info "Setting up firewall rules for UniFi OS Server ports"
ufw allow 11443/tcp 2>/dev/null
ufw allow 8080/tcp 2>/dev/null
ufw allow 3478/tcp 2>/dev/null
ufw allow 3478/udp 2>/dev/null
msg_ok "Firewall rules configured"
fi
fi
vm_select_storage "$HN"
# Fetch latest UniFi OS Server version and download URL
msg_info "Fetching latest UniFi OS Server version"
# Download firmware list from Ubiquiti API
API_URL="https://fw-update.ui.com/api/firmware-latest"
TEMP_JSON=$(mktemp)
if ! curl -fsSL "$API_URL" -o "$TEMP_JSON"; then
rm -f "$TEMP_JSON"
msg_error "Failed to fetch data from Ubiquiti API"
exit 1
fi
# Parse JSON to find latest unifi-os-server linux-x64 version
LATEST=$(jq -r '
._embedded.firmware
| map(select(.product == "unifi-os-server"))
| map(select(.platform == "linux-x64"))
| sort_by(.version_major, .version_minor, .version_patch)
| last
' "$TEMP_JSON")
UOS_VERSION=$(echo "$LATEST" | jq -r '.version' | sed 's/^v//')
UOS_URL=$(echo "$LATEST" | jq -r '._links.data.href')
# Cleanup temp file
rm -f "$TEMP_JSON"
if [[ -z "$UOS_URL" || "$UOS_URL" == "null" || -z "$UOS_VERSION" || "$UOS_VERSION" == "null" ]]; then
msg_error "Failed to parse UniFi OS Server version or download URL"
exit 1
fi
UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin"
msg_ok "Found UniFi OS Server ${UOS_VERSION}"
# --- Download Cloud Image ---
msg_info "Downloading ${OS_DISPLAY} Cloud Image"
URL=$(get_image_url)
sleep 2
msg_ok "${CL}${BL}${URL}${CL}"
CACHE_FILE="$(vm_image_cache_path "$URL")"
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
FILE="$(basename "$CACHE_FILE")"
# Work on a copy: image preparation rewrites the image, which would poison the cache for every later VM.
cp -f "$CACHE_FILE" "$FILE"
# Resize the imported disk with vm_resize_disk; Cloud-Init grows the actual root
# filesystem before the first-boot installer runs, without another offline copy.
# --- Download UniFi OS installer on the host ---
msg_info "Downloading UniFi OS Server ${UOS_VERSION} installer"
curl -fsSL "${UOS_URL}" -o "unifi-os-server.bin"
chmod +x "unifi-os-server.bin"
msg_ok "Downloaded UniFi OS Server installer"
# --- Pre-install packages and setup first-boot installer ---
msg_info "Customizing disk image (installing packages, staging installer)"
# Create the first-boot installer script
FIRSTBOOT_SCRIPT="$TEMP_DIR/unifi-os-firstboot.sh"
cat >"$FIRSTBOOT_SCRIPT" <<'FBEOF'
#!/usr/bin/env bash
set -Eeuo pipefail
LOG="/var/log/unifi-os-install.log"
exec > >(tee -a "$LOG") 2>&1
echo "[$(date)] Starting UniFi OS Server first-boot setup..."
trap 'echo "[$(date)] ERROR: First-boot setup failed at line $LINENO"' ERR
if ! systemctl start qemu-guest-agent; then
echo "[$(date)] WARNING: Preinstalled guest agent could not start; retrying after package installation"
fi
# Sync clock before apt (fresh VMs have clock skew that breaks GPG signature validation)
echo "[$(date)] Syncing system clock..."
if ! timedatectl set-ntp true; then
echo "[$(date)] WARNING: Could not enable NTP; checking existing clock synchronization"
fi
# Try NTP first
for attempt in {1..6}; do
if timedatectl show -p NTPSynchronized --value 2>/dev/null | grep -q "yes"; then
echo "[$(date)] Clock synchronized via NTP"
break
fi
sleep 5
done
# Fallback: sync from HTTP header if NTP didn't work
if ! timedatectl show -p NTPSynchronized --value 2>/dev/null | grep -q "yes"; then
if HTTP_DATE=$(curl -fsSI --max-time 10 https://deb.debian.org | sed -n 's/^[Dd]ate: //p' | tr -d '\r') &&
[ -n "$HTTP_DATE" ] && date -s "$HTTP_DATE" >/dev/null; then
echo "[$(date)] Clock synchronized via HTTP"
else
echo "[$(date)] WARNING: Clock synchronization unavailable"
fi
fi
# Install required packages
export DEBIAN_FRONTEND=noninteractive
echo "[$(date)] Installing packages..."
for attempt in {1..3}; do
if apt-get update -qq 2>&1; then
break
fi
if [ "$attempt" -eq 3 ]; then
echo "[$(date)] apt-get update failed after 3 attempts"
exit 1
fi
echo "[$(date)] apt-get update failed (attempt $attempt/3), retrying in 10s..."
sleep 10
done
for attempt in {1..3}; do
if apt-get install -y -qq qemu-guest-agent podman uidmap slirp4netns curl wget; then
break
fi
if [ "$attempt" -eq 3 ]; then
echo "[$(date)] apt-get install failed after 3 attempts"
exit 1
fi
echo "[$(date)] apt-get install failed (attempt $attempt/3), retrying in 10s..."
sleep 10
done
systemctl enable --now qemu-guest-agent
echo "[$(date)] Packages installed"
# Setup swap (2GB)
if [ ! -f /swapfile ]; then
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
echo "[$(date)] Swap file created"
fi
# Run UniFi OS installer
if [ -f /opt/unifi-os-server.bin ]; then
cd /opt
./unifi-os-server.bin <<<'y'
rm -f /opt/unifi-os-server.bin
echo "[$(date)] UniFi OS Server installed successfully"
else
echo "[$(date)] ERROR: /opt/unifi-os-server.bin not found"
exit 1
fi
echo "[$(date)] First-boot setup complete"
FBEOF
vm_prepare_cloud_image "$FILE" "$HN"
vm_customize "UniFi OS installer" "$FILE" \
--upload "unifi-os-server.bin:/opt/unifi-os-server.bin" \
--chmod 0755:/opt/unifi-os-server.bin \
--run-command "systemctl enable ssh" || exit 1
vm_firstboot_unit "$FILE" "unifi-os-firstboot" "$FIRSTBOOT_SCRIPT" \
--description "UniFi OS Server First Boot Installer" \
--after qemu-guest-agent.service \
--requires-path /opt/unifi-os-server.bin \
--cloud-init yes || exit 1
rm -f "$FIRSTBOOT_SCRIPT" "unifi-os-server.bin"
msg_ok "Disk image customized (UniFi OS ${UOS_VERSION} staged for first-boot install)"
vm_claim_vmid
msg_info "Creating UniFi OS VM"
qm create "$VMID" -agent 1${MACHINE} -tablet 0 -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
vm_mark_created
vm_import_disk "$VMID" "$FILE" "$STORAGE" "$DISK_IMPORT_FORMAT"
qm set "$VMID" \
-efidisk0 "${STORAGE}:0,efitype=4m" \
-scsi0 "${VM_IMPORTED_DISK},${DISK_CACHE}size=${DISK_SIZE}" \
-boot order=scsi0 -serial0 socket >/dev/null
vm_resize_disk
qm set "$VMID" --agent enabled=1 >/dev/null
vm_provision "$VMID"
# Core currently suppresses SSH-key write errors; keep them fatal for this VM.
if [[ -n "${CLOUDINIT_SSH_KEYS:-}" ]]; then
qm set "$VMID" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null
fi
set_description
msg_ok "Created a UniFi OS VM ${CL}${BL}(${HN})"
VM_IP=""
UNIFI_READY=""
FIRSTBOOT_DONE=""
if [ "$START_VM" == "yes" ]; then
vm_start_vm "UniFi OS VM"
vm_wait_for_ip 360 || true
if [[ -n "${VM_FIRSTBOOT_MARKER:-}" ]] && vm_guest_exec "$VMID" 10 test -f "$VM_FIRSTBOOT_MARKER" >/dev/null; then
FIRSTBOOT_DONE="yes"
else
msg_warn "UniFi OS first-boot installation has not completed yet"
fi
if [[ -n "${VM_IP:-}" ]] && vm_wait_http "https://${VM_IP}:11443" 300 --insecure; then
UNIFI_READY="yes"
msg_ok "UniFi OS is up at https://${VM_IP}:11443"
elif [[ -n "${VM_IP:-}" ]]; then
msg_warn "UniFi OS is not ready; first-boot installation may still be running or may have failed"
fi
else
msg_info "Start VM ${VMID} to run the UniFi OS first-boot installation"
fi
vm_print_summary \
"Operating System=${OS_DISPLAY}" \
"UniFi OS Version=${UOS_VERSION}" \
"Web Interface=https://${VM_IP:-<VM-IP>}:11443" \
"Console Login=${CLOUDINIT_USER:-root}"
if [[ "$UNIFI_READY" == "yes" ]]; then
vm_next_steps \
"Open https://${VM_IP}:11443 and complete UniFi OS setup."
FINISH_MESSAGE="UniFi OS Server VM is ready."
else
vm_next_steps \
"Wait for first-boot installation to complete in the VM: journalctl -u unifi-os-firstboot.service" \
"Follow progress in /var/log/unifi-os-install.log inside the VM." \
"Open https://${VM_IP:-<VM-IP>}:11443 after the installer finishes."
if [[ "$FIRSTBOOT_DONE" == "yes" ]]; then
FINISH_MESSAGE="VM provisioning completed; UniFi OS is installed, but web readiness was not confirmed yet."
else
FINISH_MESSAGE="VM provisioning completed; UniFi OS installation continues in the VM on first boot."
fi
fi
vm_finish "$FINISH_MESSAGE"
-126
View File
@@ -1,126 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/IceWhaleTech/ZimaOS
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
load_functions
APP="ZimaOS"
APP_TYPE="vm"
NSAPP="zimaos-vm"
var_os="zimaos"
var_version=" "
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
THIN="discard=on,ssd=1,"
header_info
echo -e "\n Loading..."
set -Eeo pipefail
shopt -s inherit_errexit
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
vm_require_arch amd64
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
vm_preflight
function default_settings() {
VMID=$(get_valid_nextid)
vm_apply_machine_type "q35"
DISK_SIZE="64G"
DISK_CACHE=""
HN="zimaos"
CPU_TYPE=" -cpu host"
CORE_COUNT="4"
RAM_SIZE="4096"
BRG="vmbr0"
MAC="$GEN_MAC"
VLAN=""
MTU=""
START_VM="yes"
METHOD="default"
vm_echo_default_settings
}
function advanced_settings() {
METHOD="advanced"
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
vm_prompt_machine_type "q35"
vm_prompt_disk_size "64G" "Set Disk Size in GiB (Recommended: 64+ for apps)"
vm_prompt_disk_cache "none"
vm_prompt_hostname "zimaos"
vm_prompt_cpu_model "host"
vm_prompt_cpu_cores "4"
vm_prompt_ram "4096"
vm_prompt_bridge "vmbr0"
vm_prompt_mac "$GEN_MAC"
vm_prompt_vlan
vm_prompt_mtu
vm_prompt_keyboard
vm_prompt_verbose "no"
vm_prompt_start_vm "yes"
if vm_confirm_advanced_settings "Ready to create a ZimaOS VM?"; then
echo -e "${CREATING}${BOLD}${DGN}Creating a ZimaOS VM using the above advanced settings${CL}"
else
header_info
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
advanced_settings
fi
}
vm_start_script "Use Default Settings?\n\nDefaults:\n• 4 CPU Cores (Host model)\n• 4 GB RAM\n• 64 GB Disk\n• Q35 Machine Type" 14 58
post_to_api_vm
vm_select_storage "$HN"
msg_info "Retrieving the URL for the ZimaOS installer"
if ! vm_release_asset github IceWhaleTech/ZimaOS 'zimaos-x86_64-.*_installer\.iso$'; then
exit 1
fi
URL="$VM_RELEASE_URL"
FILENAME="$VM_RELEASE_ASSET"
ZIMAOS_VERSION="$VM_RELEASE_VERSION"
var_version="$ZIMAOS_VERSION"
vm_select_iso_storage "$FILENAME" "$HN"
CACHE_FILE="$ISO_PATH"
msg_ok "ZimaOS ${CL}${BL}${ZIMAOS_VERSION}${CL}"
[[ -s "$CACHE_FILE" ]] || msg_warn "Downloading the ZimaOS installer (approximately 2 GB, this may take a while)"
# Official installers such as 1.8.0-beta2 are smaller than 2 GiB.
MIN_ISO_BYTES=$((1024 * 1024 * 1024))
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes "$MIN_ISO_BYTES" --sha256 "$VM_RELEASE_SHA256" || exit 115
vm_claim_vmid
msg_info "Creating a ZimaOS VM"
$STD qm create $VMID -agent 1${MACHINE} -tablet 0 -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-single \
-efidisk0 ${STORAGE}:1,efitype=4m,pre-enrolled-keys=0 -scsi0 ${STORAGE}:${DISK_SIZE%G},${DISK_CACHE}${THIN%,} \
-cdrom "$ISO_VOLUME" -boot order='scsi0;ide2' -vga std -serial0 socket
vm_mark_created
set_description
msg_ok "Created a ZimaOS VM ${CL}${BL}(${HN})"
vm_start_vm "ZimaOS VM"
vm_print_summary "Version=${ZIMAOS_VERSION}" "ISO=${FILENAME}" "Discovery=https://find.zimaspace.com"
vm_next_steps \
"Open the VM Console in Proxmox." \
"Follow the installer and select scsi0 as the target disk." \
"When prompted to remove the disk and reboot, reboot; the boot order prefers the disk." \
"Detach the ISO afterwards (Hardware -> CD/DVD -> Remove)." \
"Read the IP from the Proxmox summary once the guest agent is up, or use https://find.zimaspace.com." \
"For NAS use, add a second disk in Proxmox and let ZimaOS manage it."
vm_finish "VM created; complete the ZimaOS installation in the Proxmox console."