Compare commits

...

23 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
a1fc5567de Update date_created and default credentials in JSON
added json information (password)
2026-02-05 13:13:24 +01:00
push-app-to-main[bot]
d7476d0dda Add nginx-ui (ct) 2026-02-05 11:41:52 +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
20 changed files with 1140 additions and 351 deletions

View File

@@ -398,6 +398,30 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-02-05
### 🆕 New Scripts
- 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
### 🆕 New Scripts
@@ -407,8 +431,11 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
### 🚀 Updated Scripts
- Add log directory and permissions for koillection [@shineangelic](https://github.com/shineangelic) ([#11553](https://github.com/community-scripts/ProxmoxVE/pull/11553))
- #### 🐞 Bug Fixes
- [FIX] Scanopy: ensure Scanopy Daemon update [@vhsdream](https://github.com/vhsdream) ([#11541](https://github.com/community-scripts/ProxmoxVE/pull/11541))
- Immich: pin version to 2.5.3 [@vhsdream](https://github.com/vhsdream) ([#11515](https://github.com/community-scripts/ProxmoxVE/pull/11515))
### 💾 Core
@@ -429,6 +456,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))
### ❔ Uncategorized
- [FIX] Immich Public Proxy docs link [@vhsdream](https://github.com/vhsdream) ([#11543](https://github.com/community-scripts/ProxmoxVE/pull/11543))
## 2026-02-03
### 🆕 New Scripts

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 build
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
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}"

View File

@@ -29,7 +29,7 @@ function update_script() {
exit
fi
if check_for_gh_release "scanopy" "scanopy/scanopy"; then
if check_for_gh_release "Scanopy" "scanopy/scanopy"; then
msg_info "Stopping services"
systemctl stop scanopy-server
[[ -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
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
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
@@ -61,19 +61,22 @@ function update_script() {
$STD npm run build
msg_ok "Created frontend UI"
msg_info "Building scanopy-server (patience)"
msg_info "Building Scanopy Server (patience)"
cd /opt/scanopy/backend
$STD cargo build --release --bin 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 ]] &&
fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "singlefile" "latest" "/usr/local/bin" "scanopy-daemon-linux-amd64" &&
rm -f /usr/bin/scanopy-daemon ~/configure_daemon.sh &&
if [[ -f /etc/systemd/system/scanopy-daemon.service ]]; then
fetch_and_deploy_gh_release "Scanopy Daemon" "scanopy/scanopy" "singlefile" "latest" "/usr/local/bin" "scanopy-daemon-linux-amd64"
mv "/usr/local/bin/Scanopy Daemon" /usr/local/bin/scanopy-daemon
rm -f /usr/bin/scanopy-daemon ~/configure_daemon.sh
sed -i -e 's|usr/bin|usr/local/bin|' \
-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
msg_ok "Updated Scanopy Daemon"
fi
msg_info "Starting services"
systemctl start scanopy-server

View File

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

45
ct/sqlserver2025.sh Normal file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.microsoft.com/en-us/sql-server/sql-server-2025
APP="SQL Server 2025"
var_tags="${var_tags:-sql;database}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-10}"
var_os="${var_os:-ubuntu}"
var_version="${var_version:-24.04}"
var_unprivileged="${var_unprivileged:-0}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/mssql ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating SQL Server 2025"
rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh
$STD apt update
$STD apt -y upgrade
msg_ok "Updated successfully!"
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following IP:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}${IP}:1433${CL}"

View File

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

View File

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

View File

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

@@ -23,7 +23,7 @@ msg_ok "Installed Dependencies"
PG_VERSION=17 setup_postgresql
NODE_VERSION="24" setup_nodejs
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}')"
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
@@ -35,11 +35,11 @@ $STD npm ci --no-fund --no-audit
$STD npm run build
msg_ok "Created frontend UI"
msg_info "Building scanopy-server (patience)"
msg_info "Building Scanopy Server (patience)"
cd /opt/scanopy/backend
$STD cargo build --release --bin 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"
cat <<EOF >/opt/scanopy/.env

View File

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

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.microsoft.com/en-us/sql-server/sql-server-2025
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y coreutils
msg_ok "Installed Dependencies"
msg_info "Setting up SQL Server 2025 Repository"
setup_deb822_repo \
"mssql-server-2025" \
"https://packages.microsoft.com/keys/microsoft.asc" \
"https://packages.microsoft.com/ubuntu/24.04/mssql-server-2025" \
"./" \
""
msg_ok "Repository configured"
msg_info "Installing SQL Server 2025"
$STD apt install -y mssql-server
msg_ok "Installed SQL Server 2025"
msg_info "Installing SQL Server Tools"
export DEBIAN_FRONTEND=noninteractive
export ACCEPT_EULA=Y
setup_deb822_repo \
"mssql-release" \
"https://packages.microsoft.com/keys/microsoft.asc" \
"https://packages.microsoft.com/ubuntu/24.04/prod" \
"noble" \
"main"
$STD apt-get install -y \
mssql-tools18 \
unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >>~/.bash_profile
source ~/.bash_profile
msg_ok "Installed SQL Server Tools"
read -r -p "${TAB3}Do you want to run the SQL Server setup now? (Later is also possible) <y/N>" prompt
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
/opt/mssql/bin/mssql-conf setup
else
msg_ok "Skipping SQL Server setup. You can run it later with '/opt/mssql/bin/mssql-conf setup'."
fi
msg_info "Starting SQL Server Service"
systemctl enable -q --now mssql-server
msg_ok "Service started"
msg_info "Cleaning up"
rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh
msg_ok "Cleaned up"
motd_ssh
customize
cleanup_lxc

View File

@@ -28,13 +28,210 @@
# ==============================================================================
# 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_DNS_SERVERS="${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}"
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)
qm set "$vmid" --cipassword "$cipassword" >/dev/null
# Add SSH keys if available
if [ -f "$CLOUDINIT_SSH_KEYS" ]; then
# Add SSH keys only if explicitly provided (not auto-imported from host)
if [ -n "${CLOUDINIT_SSH_KEYS:-}" ] && [ -f "$CLOUDINIT_SSH_KEYS" ]; then
qm set "$vmid" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null 2>&1 || true
_ci_msg_info "SSH keys imported from: $CLOUDINIT_SSH_KEYS"
fi
# 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 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
# ==============================================================================

View File

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

View File

@@ -1,71 +1,45 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: thost96 (thost96) | Co-Author: michelroegl-brunner
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Author: thost96 (thost96) | michelroegl-brunner | MickLesk
# 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() {
clear
cat <<"EOF"
____ __ _ ____ ___
/ __ \____ _____/ /_____ _____ | | / / |/ /
/ / / / __ \/ ___/ //_/ _ \/ ___/ | | / / /|_/ /
/ /_/ / /_/ / /__/ ,< / __/ / | |/ / / / /
/_____/\____/\___/_/|_|\___/_/ |___/_/ /_/
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/api.func) 2>/dev/null
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/vm-core.func) 2>/dev/null
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/.$//')
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
NSAPP="docker-vm"
var_os="debian"
var_version="12"
DISK_SIZE="10G"
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}"
USE_CLOUD_INIT="no"
OS_TYPE=""
OS_VERSION=""
THIN="discard=on,ssd=1,"
# ==============================================================================
# ERROR HANDLING & CLEANUP
# ==============================================================================
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
function error_handler() {
local exit_code="$?"
local line_number="$1"
@@ -76,140 +50,96 @@ function error_handler() {
cleanup_vmid
}
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
# ==============================================================================
# OS SELECTION FUNCTIONS
# ==============================================================================
function select_os() {
if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT OS" --radiolist \
"Choose Operating System for Docker VM" 14 68 4 \
"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
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() {
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
;;
ubuntu)
echo "https://cloud-images.ubuntu.com/${OS_CODENAME}/current/${OS_CODENAME}-server-cloudimg-${arch}.img"
;;
esac
}
# ==============================================================================
# SETTINGS FUNCTIONS
# ==============================================================================
function default_settings() {
select_os
select_cloud_init
VMID=$(get_valid_nextid)
FORMAT=",efitype=4m"
MACHINE=""
FORMAT=""
MACHINE=" -machine q35"
DISK_CACHE=""
DISK_SIZE="10G"
HN="docker"
CPU_TYPE=""
CPU_TYPE=" -cpu host"
CORE_COUNT="2"
RAM_SIZE="4096"
BRG="vmbr0"
@@ -218,12 +148,13 @@ function default_settings() {
MTU=""
START_VM="yes"
METHOD="default"
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 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 "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${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}"
@@ -231,12 +162,22 @@ function default_settings() {
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 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() {
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"
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
# VM ID
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
@@ -250,27 +191,29 @@ function advanced_settings() {
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
break
else
exit-script
exit_script
fi
done
# Machine Type
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 \
"q35" "Q35 (Modern, PCIe)" ON \
"i440fx" "i440fx (Legacy, PCI)" OFF \
3>&1 1>&2 2>&3); 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=""
MACHINE=" -machine q35"
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"
MACHINE=""
fi
else
exit-script
exit_script
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
DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ')
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
@@ -280,12 +223,13 @@ function advanced_settings() {
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
exit_script
fi
else
exit-script
exit_script
fi
# Disk Cache
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 \
@@ -298,24 +242,25 @@ function advanced_settings() {
DISK_CACHE=""
fi
else
exit-script
exit_script
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 [ -z $VM_NAME ]; then
HN="docker"
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else
HN=$(echo ${VM_NAME,,} | tr -d ' ')
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else
exit-script
exit_script
fi
# CPU Model
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 \
"1" "Host (Recommended)" ON \
"0" "KVM64" OFF \
3>&1 1>&2 2>&3); then
if [ $CPU_TYPE1 = "1" ]; then
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
@@ -325,80 +270,78 @@ function advanced_settings() {
CPU_TYPE=""
fi
else
exit-script
exit_script
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 [ -z $CORE_COUNT ]; then
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
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
else
exit-script
exit_script
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
RAM_SIZE="2048"
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
else
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
RAM_SIZE="4096"
fi
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
else
exit-script
exit_script
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 [ -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
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
else
exit-script
exit_script
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 [ -z $MAC1 ]; then
MAC="$GEN_MAC"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
else
MAC="$MAC1"
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
fi
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
else
exit-script
exit_script
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
VLAN1="Default"
VLAN=""
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
else
VLAN=",tag=$VLAN1"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
fi
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
else
exit-script
exit_script
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 [ -z $MTU1 ]; then
MTU1="Default"
MTU=""
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
else
MTU=",mtu=$MTU1"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
fi
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
else
exit-script
exit_script
fi
# Start VM
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"
@@ -407,6 +350,7 @@ function advanced_settings() {
START_VM="no"
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
echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}"
else
@@ -427,13 +371,28 @@ function start_script() {
advanced_settings
fi
}
# ==============================================================================
# MAIN EXECUTION
# ==============================================================================
header_info
check_root
arch_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
post_to_api_vm
# ==============================================================================
# STORAGE SELECTION
# ==============================================================================
msg_info "Validating Storage"
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
@@ -446,6 +405,7 @@ while read -r line; do
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."
@@ -453,6 +413,8 @@ if [ -z "$VALID" ]; then
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
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
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" \
@@ -462,112 +424,288 @@ else
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 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}')
case $STORAGE_TYPE in
nfs | dir)
DISK_EXT=".qcow2"
DISK_REF="$VMID/"
DISK_IMPORT="-format qcow2"
DISK_IMPORT="--format qcow2"
THIN=""
;;
btrfs)
DISK_EXT=".raw"
DISK_REF="$VMID/"
DISK_IMPORT="-format raw"
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}
# ==============================================================================
# IMAGE CUSTOMIZATION WITH DOCKER
# ==============================================================================
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
if ! command -v virt-customize &>/dev/null; then
msg_info "Installing Pre-Requisite libguestfs-tools onto Host"
apt-get -qq update >/dev/null
apt-get -qq install libguestfs-tools lsb-release -y >/dev/null
# Workaround for Proxmox VE 9.0 libguestfs issue
apt-get -qq install dhcpcd-base -y >/dev/null 2>&1 || true
msg_ok "Installed libguestfs-tools successfully"
apt-get update
apt-get install -y qemu-guest-agent curl ca-certificates
curl -fsSL https://get.docker.com | sh
systemctl enable docker
systemctl start docker
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
msg_info "Adding Docker and Docker Compose Plugin to Debian 12 Qcow2 Disk Image"
virt-customize -q -a "${FILE}" --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,software-properties-common,lsb-release >/dev/null &&
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 &&
virt-customize -q -a "${FILE}" --run-command "echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable' > /etc/apt/sources.list.d/docker.list" >/dev/null &&
virt-customize -q -a "${FILE}" --run-command "apt-get update -qq && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" >/dev/null &&
virt-customize -q -a "${FILE}" --run-command "systemctl enable docker" >/dev/null &&
virt-customize -q -a "${FILE}" --hostname "${HN}" >/dev/null &&
virt-customize -q -a "${FILE}" --run-command "echo -n > /etc/machine-id" >/dev/null
msg_ok "Added Docker and Docker Compose Plugin to Debian 12 Qcow2 Disk Image successfully"
# Resize disk to target size
msg_info "Resizing disk image to ${DISK_SIZE}"
qemu-img resize "$WORK_FILE" "${DISK_SIZE}" >/dev/null 2>&1
msg_ok "Resized disk image"
msg_info "Expanding root partition to use full disk space"
qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1
virt-resize --expand /dev/sda1 ${FILE} expanded.qcow2 >/dev/null 2>&1
mv expanded.qcow2 ${FILE} >/dev/null 2>&1
msg_ok "Expanded image to full size"
# ==============================================================================
# VM CREATION
# ==============================================================================
msg_info "Creating Docker VM shell"
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 \
-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
qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
qm set $VMID \
-efidisk0 ${DISK0_REF}${FORMAT} \
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \
-boot order=scsi0 \
-serial0 socket >/dev/null
qm resize $VMID scsi0 8G >/dev/null
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null
msg_ok "Created VM shell"
# ==============================================================================
# DISK IMPORT
# ==============================================================================
msg_info "Importing disk into storage ($STORAGE)"
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
DESCRIPTION=$(
cat <<EOF
<div align='center'>
<a href='https://Helper-Scripts.com' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a>
msg_ok "Attached EFI and root disk"
<h2 style='font-size: 24px; margin: 20px 0;'>Docker VM</h2>
# Set VM description
set_description
<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>
# Cloud-Init configuration
if [ "$USE_CLOUD_INIT" = "yes" ]; then
msg_info "Configuring Cloud-Init"
setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes"
msg_ok "Cloud-Init configured"
fi
<span style='margin: 0 10px;'>
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
</span>
<span style='margin: 0 10px;'>
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
</span>
<span style='margin: 0 10px;'>
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
</span>
</div>
EOF
)
qm set $VMID -description "$DESCRIPTION" >/dev/null
msg_ok "Created a Docker VM ${CL}${BL}(${HN})"
# Start VM
if [ "$START_VM" == "yes" ]; then
msg_info "Starting Docker VM"
qm start $VMID
qm start $VMID >/dev/null 2>&1
msg_ok "Started Docker VM"
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"
msg_ok "Completed successfully!\n"

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

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