Compare commits

..

27 Commits

Author SHA1 Message Date
GitHub Actions
e437d804ca Update .app files 2026-02-05 13:16:52 +00:00
push-app-to-main[bot]
6ed9afb40b OpenCloud (#11538)
* Add opencloud (ct)

* Update opencloud-install.sh

* Add OpenCloud installation to script

Fetch and deploy OpenCloud version 5.0.1.

* Apply suggestion from @vhsdream

* Apply suggestion from @vhsdream

* Apply suggestion from @vhsdream

* Apply suggestion from @vhsdream

* Apply suggestion from @vhsdream

* Apply suggestion from @vhsdream

* Apply suggestion from @vhsdream

* Update install/opencloud-install.sh

* Update install/opencloud-install.sh

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com>
Co-authored-by: Chris <punk.sand7393@fastmail.com>
2026-02-05 14:16:36 +01:00
community-scripts-pr-app[bot]
fbf04bee6a Update .app files (#11575)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-05 14:11:51 +01:00
community-scripts-pr-app[bot]
c9bb2788be Update CHANGELOG.md (#11576)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 13:00:17 +00:00
push-app-to-main[bot]
93e4eb5b22 Nginx-UI (#11573)
* Add nginx-ui (ct)

* Update date_created and default credentials in JSON

added json information (password)

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-02-05 13:59:50 +01:00
community-scripts-pr-app[bot]
f4d4a00180 chore: update github-versions.json (#11574)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 12:12:28 +00:00
community-scripts-pr-app[bot]
101f882022 Update CHANGELOG.md (#11572)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 11:37:51 +00:00
ls-root
8f97908896 fix(frontend): theme respective syntax highlighting (#11565)
Now if the user is in light mode the syntax highlighting
switches to light mode too.
2026-02-05 12:37:22 +01:00
community-scripts-pr-app[bot]
833a6a248b Update .app files (#11570)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-05 12:02:06 +01:00
community-scripts-pr-app[bot]
9a65218900 Update CHANGELOG.md (#11571)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 10:56:17 +00:00
community-scripts-pr-app[bot]
b61abacb76 Update CHANGELOG.md (#11569)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 10:55:58 +00:00
community-scripts-pr-app[bot]
273c5c75ee Update date in json (#11568)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-05 10:55:52 +00:00
CanbiZ (MickLesk)
06d9199d89 feat(sqlserver): add SQL Server 2025 with Ubuntu 24.04 support & refactor 2022 (#11546)
- Add new SQL Server 2025 scripts with Ubuntu 24.04 support
- Refactor both versions to use setup_deb822_repo
- Fix debuginfod profile causing update errors (#11522)
- Use apt instead of apt-get in CT scripts
- Consistent messaging and formatting
2026-02-05 11:55:32 +01:00
community-scripts-pr-app[bot]
5d5c273fb6 Update CHANGELOG.md (#11562)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 07:11:37 +00:00
CanbiZ (MickLesk)
92506cdc34 feat(cloud-init): add interactive SSH key discovery and selection (#11547)
- Add SSH key discovery from standard paths (/root/.ssh, /etc/ssh)
- Add whiptail-based interactive key selection dialog
- Extract key fingerprints and comments for better identification
- Support multiple key selection with checkboxes
- Auto-skip private keys and known_hosts files
- Restore shell state after library load
2026-02-05 08:11:13 +01:00
community-scripts-pr-app[bot]
5db509eb16 Update CHANGELOG.md (#11561)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 07:03:31 +00:00
CanbiZ (MickLesk)
129f2f6329 Refactor: Docker-VM (Multi-OS / Cloud-Init / Stabilization) (#9047)
* Upgrade to Debian 13 and clean up dependencies

Updated the script to use Debian 13 Qcow2 Disk Image and removed redundant installation of libguestfs-tools.

* Update URL for Debian Qcow2 Disk Image

* Update docker-vm.sh

* Update fmt.Println message from 'Hello' to 'Goodbye'

* add cloudinit function
2026-02-05 08:03:04 +01:00
community-scripts-pr-app[bot]
be9d26885a chore: update github-versions.json (#11560)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 06:22:30 +00:00
community-scripts-pr-app[bot]
ddd130dbe3 Update CHANGELOG.md (#11558)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 00:22:45 +00:00
community-scripts-pr-app[bot]
93c52abebd chore: update github-versions.json (#11557)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 00:22:22 +00:00
community-scripts-pr-app[bot]
a74f6ee3c3 Update CHANGELOG.md (#11555)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 22:00:07 +00:00
Alessandro Del Pex
b36051375b Add log directory and permissions for koillection (#11553)
Create log directory and set ownership for www-data
2026-02-04 22:59:43 +01:00
community-scripts-pr-app[bot]
7b8a3a111d Update .app files (#11550)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-04 21:06:58 +01:00
community-scripts-pr-app[bot]
be994ce84f Update CHANGELOG.md (#11551)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 19:58:23 +00:00
Chris
4bd5c7b54b [FIX] Scanopy: ensure Scanopy Daemon update (#11541)
- It wasn't updating due to it having the same name as the server
- Changed it to 'Scanopy Daemon'
- Other small fixes
2026-02-04 20:57:52 +01:00
community-scripts-pr-app[bot]
868446a4b6 Update CHANGELOG.md (#11545)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 19:21:53 +00:00
Chris
5f31954d91 [FIX] Immich Public Proxy docs link (#11543) 2026-02-04 20:21:30 +01:00
21 changed files with 1351 additions and 389 deletions

View File

@@ -398,6 +398,31 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-02-05
### 🆕 New Scripts
- Nginx-UI ([#11573](https://github.com/community-scripts/ProxmoxVE/pull/11573))
- New: SQL-Server 2025 | Refactor SQL-Server 2022 [@MickLesk](https://github.com/MickLesk) ([#11546](https://github.com/community-scripts/ProxmoxVE/pull/11546))
### 🚀 Updated Scripts
- #### ✨ New Features
- Refactor: Docker-VM (Multi-OS / Cloud-Init / Stabilization) [@MickLesk](https://github.com/MickLesk) ([#9047](https://github.com/community-scripts/ProxmoxVE/pull/9047))
### 💾 Core
- #### ✨ New Features
- cloud-init: add interactive SSH key discovery and selection [@MickLesk](https://github.com/MickLesk) ([#11547](https://github.com/community-scripts/ProxmoxVE/pull/11547))
### 🌐 Website
- #### 🐞 Bug Fixes
- fix(frontend): theme respective syntax highlighting [@ls-root](https://github.com/ls-root) ([#11565](https://github.com/community-scripts/ProxmoxVE/pull/11565))
## 2026-02-04 ## 2026-02-04
### 🆕 New Scripts ### 🆕 New Scripts
@@ -407,8 +432,11 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
### 🚀 Updated Scripts ### 🚀 Updated Scripts
- Add log directory and permissions for koillection [@shineangelic](https://github.com/shineangelic) ([#11553](https://github.com/community-scripts/ProxmoxVE/pull/11553))
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- [FIX] Scanopy: ensure Scanopy Daemon update [@vhsdream](https://github.com/vhsdream) ([#11541](https://github.com/community-scripts/ProxmoxVE/pull/11541))
- Immich: pin version to 2.5.3 [@vhsdream](https://github.com/vhsdream) ([#11515](https://github.com/community-scripts/ProxmoxVE/pull/11515)) - Immich: pin version to 2.5.3 [@vhsdream](https://github.com/vhsdream) ([#11515](https://github.com/community-scripts/ProxmoxVE/pull/11515))
### 💾 Core ### 💾 Core
@@ -429,6 +457,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534)) - fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534))
### ❔ Uncategorized
- [FIX] Immich Public Proxy docs link [@vhsdream](https://github.com/vhsdream) ([#11543](https://github.com/community-scripts/ProxmoxVE/pull/11543))
## 2026-02-03 ## 2026-02-03
### 🆕 New Scripts ### 🆕 New Scripts

6
ct/headers/nginx-ui Normal file
View File

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

6
ct/headers/opencloud Normal file
View File

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

6
ct/headers/sqlserver2025 Normal file
View File

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

View File

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

68
ct/nginx-ui.sh Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://nginxui.com
APP="Nginx-UI"
var_tags="${var_tags:-webserver;nginx;proxy}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /usr/local/bin/nginx-ui ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "nginx-ui" "0xJacky/nginx-ui"; then
msg_info "Stopping Service"
systemctl stop nginx-ui
msg_ok "Stopped Service"
msg_info "Backing up Configuration"
cp /usr/local/etc/nginx-ui/app.ini /tmp/nginx-ui-app.ini.bak
msg_ok "Backed up Configuration"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginx-ui" "0xJacky/nginx-ui" "prebuild" "latest" "/opt/nginx-ui" "nginx-ui-linux-64.tar.gz"
msg_info "Updating Binary"
cp /opt/nginx-ui/nginx-ui /usr/local/bin/nginx-ui
chmod +x /usr/local/bin/nginx-ui
rm -rf /opt/nginx-ui
msg_ok "Updated Binary"
msg_info "Restoring Configuration"
mv /tmp/nginx-ui-app.ini.bak /usr/local/etc/nginx-ui/app.ini
msg_ok "Restored Configuration"
msg_info "Starting Service"
systemctl start nginx-ui
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}"

60
ct/opencloud.sh Normal file
View File

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

View File

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

View File

@@ -1,5 +1,5 @@
{ {
"generated": "2026-02-04T18:17:33Z", "generated": "2026-02-05T12:12:20Z",
"versions": [ "versions": [
{ {
"slug": "2fauth", "slug": "2fauth",
@@ -186,9 +186,9 @@
{ {
"slug": "comfyui", "slug": "comfyui",
"repo": "comfyanonymous/ComfyUI", "repo": "comfyanonymous/ComfyUI",
"version": "v0.12.2", "version": "v0.12.3",
"pinned": false, "pinned": false,
"date": "2026-02-04T06:09:31Z" "date": "2026-02-05T07:04:07Z"
}, },
{ {
"slug": "commafeed", "slug": "commafeed",
@@ -221,9 +221,9 @@
{ {
"slug": "cronicle", "slug": "cronicle",
"repo": "jhuckaby/Cronicle", "repo": "jhuckaby/Cronicle",
"version": "v0.9.102", "version": "v0.9.103",
"pinned": false, "pinned": false,
"date": "2025-12-19T03:45:13Z" "date": "2026-02-05T03:09:04Z"
}, },
{ {
"slug": "cryptpad", "slug": "cryptpad",
@@ -445,9 +445,9 @@
{ {
"slug": "headscale", "slug": "headscale",
"repo": "juanfont/headscale", "repo": "juanfont/headscale",
"version": "v0.27.1", "version": "v0.28.0",
"pinned": false, "pinned": false,
"date": "2025-11-11T19:32:29Z" "date": "2026-02-04T20:40:23Z"
}, },
{ {
"slug": "healthchecks", "slug": "healthchecks",
@@ -494,9 +494,9 @@
{ {
"slug": "homepage", "slug": "homepage",
"repo": "gethomepage/homepage", "repo": "gethomepage/homepage",
"version": "v1.9.0", "version": "v1.10.0",
"pinned": false, "pinned": false,
"date": "2026-01-19T05:46:09Z" "date": "2026-02-05T06:17:32Z"
}, },
{ {
"slug": "homer", "slug": "homer",
@@ -515,9 +515,9 @@
{ {
"slug": "huntarr", "slug": "huntarr",
"repo": "plexguide/Huntarr.io", "repo": "plexguide/Huntarr.io",
"version": "9.1.12", "version": "9.2.0",
"pinned": false, "pinned": false,
"date": "2026-02-04T14:28:36Z" "date": "2026-02-05T04:18:08Z"
}, },
{ {
"slug": "inspircd", "slug": "inspircd",
@@ -536,16 +536,16 @@
{ {
"slug": "invoiceninja", "slug": "invoiceninja",
"repo": "invoiceninja/invoiceninja", "repo": "invoiceninja/invoiceninja",
"version": "v5.12.53", "version": "v5.12.55",
"pinned": false, "pinned": false,
"date": "2026-02-04T00:52:01Z" "date": "2026-02-05T01:06:15Z"
}, },
{ {
"slug": "jackett", "slug": "jackett",
"repo": "Jackett/Jackett", "repo": "Jackett/Jackett",
"version": "v0.24.1027", "version": "v0.24.1032",
"pinned": false, "pinned": false,
"date": "2026-02-04T05:56:22Z" "date": "2026-02-05T05:55:27Z"
}, },
{ {
"slug": "joplin-server", "slug": "joplin-server",
@@ -746,9 +746,9 @@
{ {
"slug": "mealie", "slug": "mealie",
"repo": "mealie-recipes/mealie", "repo": "mealie-recipes/mealie",
"version": "v3.10.1", "version": "v3.10.2",
"pinned": false, "pinned": false,
"date": "2026-02-03T01:04:38Z" "date": "2026-02-04T23:32:32Z"
}, },
{ {
"slug": "mediamanager", "slug": "mediamanager",
@@ -781,9 +781,9 @@
{ {
"slug": "metube", "slug": "metube",
"repo": "alexta69/metube", "repo": "alexta69/metube",
"version": "2026.02.03", "version": "2026.02.04",
"pinned": false, "pinned": false,
"date": "2026-02-03T21:49:49Z" "date": "2026-02-04T20:01:18Z"
}, },
{ {
"slug": "miniflux", "slug": "miniflux",
@@ -1096,9 +1096,9 @@
{ {
"slug": "pulse", "slug": "pulse",
"repo": "rcourtman/Pulse", "repo": "rcourtman/Pulse",
"version": "v5.1.0", "version": "v5.1.2",
"pinned": false, "pinned": false,
"date": "2026-02-04T17:43:59Z" "date": "2026-02-05T00:18:57Z"
}, },
{ {
"slug": "pve-scripts-local", "slug": "pve-scripts-local",
@@ -1271,9 +1271,9 @@
{ {
"slug": "speedtest-tracker", "slug": "speedtest-tracker",
"repo": "alexjustesen/speedtest-tracker", "repo": "alexjustesen/speedtest-tracker",
"version": "v1.13.7", "version": "v1.13.8",
"pinned": false, "pinned": false,
"date": "2026-02-04T16:47:42Z" "date": "2026-02-04T19:24:23Z"
}, },
{ {
"slug": "spoolman", "slug": "spoolman",
@@ -1292,9 +1292,9 @@
{ {
"slug": "stirling-pdf", "slug": "stirling-pdf",
"repo": "Stirling-Tools/Stirling-PDF", "repo": "Stirling-Tools/Stirling-PDF",
"version": "v2.4.3", "version": "v2.4.4",
"pinned": false, "pinned": false,
"date": "2026-01-31T22:19:14Z" "date": "2026-02-05T00:15:53Z"
}, },
{ {
"slug": "streamlink-webui", "slug": "streamlink-webui",

View File

@@ -9,7 +9,7 @@
"updateable": true, "updateable": true,
"privileged": false, "privileged": false,
"interface_port": 3000, "interface_port": 3000,
"documentation": "https://github.com/alangrainger/immich-public-proxy/docs", "documentation": "https://github.com/alangrainger/immich-public-proxy/tree/main/docs",
"website": "https://github.com/alangrainger/immich-public-proxy", "website": "https://github.com/alangrainger/immich-public-proxy",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/immich-public-proxy.webp", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/immich-public-proxy.webp",
"config_path": "/opt/immich-proxy/app/.env", "config_path": "/opt/immich-proxy/app/.env",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

6
vm/headers/docker-vm Normal file
View File

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