├── README.md ├── bootstrap.sh ├── docker-zfs-legacy.md ├── download-cloud-image.sh ├── download-cloud-template.sh ├── new-ct.sh ├── new-vm-windows.sh ├── new-vm.sh ├── remove-nag-subscription.sh ├── setup-pbs.sh └── setup-pve.sh /README.md: -------------------------------------------------------------------------------- 1 | # Proxmox VE automation scripts 2 | 3 | Collection of scripts to manage Proxmox environments. 4 | 5 | Please read section [Using Docker on LXC](#using-docker-on-lxc) if you are planning to use Docker with Linux Containers (LXC). 6 | 7 | To migrate an existing Windows VM from Hyper-V to Proxmox see [this other project](https://github.com/fdcastel/Hyper-V-Automation#windows-prepare-a-vhdx-for-qemu-migration) (for Windows) first. 8 | 9 | 10 | 11 | # How to install 12 | 13 | To download all scripts into a temporary folder: 14 | 15 | ```bash 16 | source <(curl -Ls https://bit.ly/p-v-a) 17 | ``` 18 | 19 | This will download and execute [bootstrap.sh](bootstrap.sh). It will also install `unzip` apt package. 20 | 21 | 22 | 23 | # Scripts 24 | 25 | ## Summary 26 | - [download-cloud-image](#download-cloud-image) 27 | - [new-ct](#new-ct) 28 | - [new-vm](#new-vm) 29 | - [new-vm-windows](#new-vm-windows) 30 | - [remove-nag-subscription](#remove-nag-subscription) 31 | - [setup-pbs](#setup-pbs) 32 | - [setup-pve](#setup-pve) 33 | 34 | 35 | 36 | ## download-cloud-image 37 | 38 | ``` 39 | Usage: ./download-cloud-image.sh [OPTIONS] 40 | Url of image to download. 41 | --no-clobber, -nc Doesn't overwrite an existing image. 42 | --help, -h Display this help message. 43 | ``` 44 | 45 | Downloads an image from given `url` into `/var/lib/vz/template/iso/` folder. 46 | 47 | If the image already exists it will not be downloaded again. 48 | 49 | If the file is compressed with `gz`, `xz` or `zip` it will also uncompress it. 50 | 51 | Returns the full path of downloaded image. 52 | 53 | ### Example 54 | 55 | ```bash 56 | # Download Debian 12 image 57 | DEBIAN_URL='https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2' 58 | ./download-cloud-image.sh $DEBIAN_URL 59 | ``` 60 | 61 | 62 | 63 | ## download-cloud-template 64 | 65 | ``` 66 | Usage: ./download-cloud-template.sh [OPTIONS] 67 | Url of template to download. 68 | --filename, -f Renames the downloaded file. 69 | --no-clobber, -nc Doesn't overwrite an existing template. 70 | --help, -h Display this help message. 71 | ``` 72 | 73 | Downloads a LXC template from given `url` into `/var/lib/vz/template/cache/` folder. 74 | 75 | You can use `--filename` to rename the resulting file. 76 | 77 | If the template already exists it will not be downloaded again. 78 | 79 | Please note that this script DOES NOT uncompress the resulting file. LXCs templates can be used in compressed format. 80 | 81 | Returns the full path of downloaded template. 82 | 83 | ### Example 84 | 85 | ```bash 86 | # Download OpenWRT template 87 | OPENWRT_URL='https://images.linuxcontainers.org/images/openwrt/23.05/amd64/default/20241109_11:57/rootfs.tar.xz' 88 | OPENWRT_TEMPLATE_NAME='openwrt-23.05-amd64-default-20241109.tar.xz' 89 | ./download-cloud-template.sh $OPENWRT_URL --filename $OPENWRT_TEMPLATE_NAME 90 | ``` 91 | 92 | 93 | 94 | ## new-ct 95 | 96 | ``` 97 | Usage: ./new-ct.sh --ostemplate --hostname --password [OPTIONS] 98 | Proxmox unique ID of the CT. 99 | --ostemplate The OS template or backup file. 100 | --hostname Set a host name for the container. 101 | --password Sets root password inside container. 102 | --sshkey[s] Setup public SSH keys (one key per line, OpenSSH format). 103 | 104 | Additional options: 105 | --ostype OS type (default = ubuntu). 106 | --cores Number of cores per socket (default = unlimited). 107 | --memory Amount of RAM for the VM in MB (default = 2048). 108 | --rootfs Use volume as container root (default = local-zfs:120). 109 | --privileged Makes the container run as privileged user (default = unprivileged). 110 | --bridge Use bridge for container networking (default = vmbr0). 111 | --hwaddr MAC address for eth0 interface. 112 | --install-docker Install docker and docker-compose. 113 | --no-start Do not start the container after creation. 114 | --help, -h Display this help message. 115 | ``` 116 | 117 | Creates a LXC container (CT). 118 | 119 | Additionally, you can use `--install-docker` to also install `docker` into container (currently implemented only for Ubuntu, Debian and Alpine). In this case, please see section [Using Docker on LXC](#using-docker-on-lxc) for more information. 120 | 121 | Any additional arguments are passed to `pct create` command. Please see [`pct` command documentation](https://pve.proxmox.com/pve-docs/pct.1.html) for more information about the options. 122 | 123 | ### Examples 124 | 125 | #### Ubuntu 126 | 127 | ```bash 128 | # Download Ubuntu 24.04 LTS image 129 | UBUNTU_IMAGE='ubuntu-24.04-standard_24.04-2_amd64.tar.zst' 130 | UBUNTU_TEMPLATE="local:vztmpl/$UBUNTU_IMAGE" 131 | pveam download local $UBUNTU_IMAGE 132 | 133 | # Creates an Ubuntu LXC container with a 120G storage, "id_rsa.pub" ssh key and Docker installed. 134 | CT_ID=310 135 | CT_NAME='ct-ubuntu' 136 | ./new-ct.sh $CT_ID \ 137 | --memory 1024 \ 138 | --ostemplate $UBUNTU_TEMPLATE \ 139 | --hostname $CT_NAME \ 140 | --sshkey ~/.ssh/id_rsa.pub \ 141 | --rootfs local-zfs:120 \ 142 | --install-docker 143 | ``` 144 | 145 | #### OpenWRT 146 | 147 | ```bash 148 | # Download OpenWRT image 149 | OPENWRT_URL='https://images.linuxcontainers.org/images/openwrt/23.05/amd64/default/20241109_11:57/rootfs.tar.xz' 150 | OPENWRT_TEMPLATE_NAME='openwrt-23.05-amd64-default-20241109.tar.xz' 151 | OPENWRT_TEMPLATE=$(./download-cloud-template.sh $OPENWRT_URL --filename $OPENWRT_TEMPLATE_NAME) 152 | 153 | # Creates an OpenWRT privileged LXC container with a 8G storage, two named network interfaces and sets root password. 154 | CT_ID=311 155 | CT_NAME='ct-openwrt' 156 | CT_PASSWORD='uns@f3' 157 | CT_LAN_IFNAME='lan' 158 | CT_LAN_BRIDGE='vmbrloc0' # Do NOT use your LAN here! (will start a DHCP server on it). 159 | CT_WAN_IFNAME='wan' 160 | CT_WAN_BRIDGE='vmbr0' 161 | ./new-ct.sh $CT_ID \ 162 | --ostype unmanaged \ 163 | --arch amd64 \ 164 | --memory 1024 \ 165 | --ostemplate $OPENWRT_TEMPLATE \ 166 | --hostname $CT_NAME \ 167 | --password $CT_PASSWORD \ 168 | --rootfs local-zfs:8 \ 169 | --privileged \ 170 | --no-start \ 171 | --net0 name=$CT_LAN_IFNAME,bridge=$CT_LAN_BRIDGE \ 172 | --net1 name=$CT_WAN_IFNAME,bridge=$CT_WAN_BRIDGE,ip=dhcp,ip6=auto 173 | 174 | # Load initial OpenWRT configuration 175 | CT_ROOT_MOUNTPOINT="/rpool/data/subvol-$CT_ID-disk-0" 176 | cat > "$CT_ROOT_MOUNTPOINT/etc/uci-defaults/80-init" << OUTER_EOF 177 | #!/bin/sh 178 | 179 | # System 180 | uci batch << EOF 181 | set system.@system[0].hostname='{{ openwrt.hostname }}' 182 | commit system 183 | EOF 184 | 185 | # Network interfaces 186 | uci batch << EOF 187 | set network.lan=interface 188 | set network.lan.ifname='$CT_LAN_IFNAME' 189 | set network.lan.proto='static' 190 | set network.lan.ipaddr='192.168.1.1' 191 | set network.lan.netmask='255.255.255.0' 192 | 193 | set network.wan=interface 194 | set network.wan.ifname='$CT_WAN_IFNAME' 195 | set network.wan.proto='dhcp' 196 | set network.wan.zone='wan' 197 | commit network 198 | EOF 199 | OUTER_EOF 200 | 201 | # Set passwd for LUCI 202 | cat > "$CT_ROOT_MOUNTPOINT/etc/uci-defaults/81-passwd" << OUTER_EOF 203 | #!/bin/sh 204 | 205 | passwd << EOF 206 | $CT_PASSWORD 207 | $CT_PASSWORD 208 | EOF 209 | OUTER_EOF 210 | 211 | # Start the LXC Container 212 | pct start $CT_ID 213 | ``` 214 | 215 | OpenWRT offers extensive configuration options through the [UCI system](https://openwrt.org/docs/guide-user/base-system/uci). 216 | 217 | More information about the `uci-defaults` folder can be found [here](https://openwrt.org/docs/guide-developer/uci-defaults). 218 | 219 | 220 | 221 | ## new-vm 222 | 223 | ``` 224 | Usage: ./new-vm.sh --image --name [--cipassword ] | [--sshkey[s] ] [OPTIONS] 225 | Proxmox unique ID of the VM. 226 | --image Path to image file. 227 | --name A name for the VM. 228 | --cipassword Password to assign the user. Using this is generally not recommended. Use ssh keys instead. 229 | --sshkey[s] Setup public SSH keys (one key per line, OpenSSH format). 230 | 231 | Additional options: 232 | --ostype Guest OS type (default = l26). 233 | --cores Number of cores per socket (default = 2). 234 | --memory Amount of RAM for the VM in MB (default = 2048). 235 | --disksize Size of VM main disk (default = 120G). 236 | --balloon Amount of target RAM for the VM in MB. Using zero (default) disables the ballon driver. 237 | --install-docker Install docker and docker-compose. 238 | --help, -h Display this help message. 239 | ``` 240 | 241 | Creates a VM from a cloud image. 242 | 243 | You can use any image containing `cloud-init` and `qemu-guest-agent` installed. 244 | 245 | Additionally, you can use `--install-docker` to also install `docker` into virtual machine (currently implemented only for Ubuntu). 246 | 247 | Any additional arguments are passed to `qm create` command. Please see [`qm` command documentation](https://pve.proxmox.com/pve-docs/qm.1.html) for more information about the options. 248 | 249 | ### Examples 250 | 251 | ```bash 252 | # Download Ubuntu 24.04 LTS image 253 | UBUNTU_URL='https://cloud-images.ubuntu.com/releases/noble/release/ubuntu-24.04-server-cloudimg-amd64.img' 254 | UBUNTU_IMAGE_FILE=$(./download-cloud-image.sh $UBUNTU_URL --no-clobber) 255 | 256 | # Creates an Ubuntu VM with "id_rsa.pub" ssh key and Docker installed. 257 | VM_ID=401 258 | ./new-vm.sh $VM_ID --image $UBUNTU_IMAGE_FILE --name 'vm-ubuntu' --sshkey ~/.ssh/id_rsa.pub --install-docker 259 | ``` 260 | 261 | ```bash 262 | # Download Debian 12 image 263 | DEBIAN_URL='https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2' 264 | DEBIAN_IMAGE_FILE=$(./download-cloud-image.sh $DEBIAN_URL --no-clobber) 265 | 266 | # Creates a Debian VM with "id_rsa.pub" ssh key and Docker installed. 267 | VM_ID=402 268 | ./new-vm.sh $VM_ID --image $DEBIAN_IMAGE_FILE --name 'vm-debian' --sshkey '~/.ssh/id_rsa.pub' --install-docker 269 | ``` 270 | 271 | 272 | 273 | ## new-vm-windows 274 | 275 | ``` 276 | Usage: ./new-vm-windows.sh --image --name [OPTIONS] 277 | Proxmox unique ID of the VM. 278 | --image Source image to import (.vhdx | .qcow2). 279 | --name A name for the VM. 280 | 281 | Additional options: 282 | --ostype Guest OS type (default = win11). 283 | --cores Number of cores per socket (default = 2). 284 | --memory Amount of RAM for the VM in MB (default = 2048). 285 | --no-start Do not start the VM after creation. 286 | --no-guest Do not wait for QEMU Guest Agent after start. 287 | --help, -h Display this help message. 288 | ``` 289 | 290 | Creates a VM from a `vhdx` image. For _Generation 2_ (UEFI) types only. 291 | 292 | The image will be _imported_ as a `raw` image format. The original `vhdx` file remains unaltered. 293 | 294 | Any additional arguments are passed to `qm create` command. Please see [`qm` command documentation](https://pve.proxmox.com/pve-docs/qm.1.html) for more information about the options. 295 | 296 | After creation, the script will start the VM and wait for the QEMU Guest Agent to be responsive. These actions can be skipped using the `--no-start` and `--no-guest` options, respectively. 297 | 298 | It's recommended that the `vhdx` includes the following: 299 | 300 | - [Windows VirtIO Drivers](https://pve.proxmox.com/wiki/Windows_VirtIO_Drivers) (recommended) 301 | - [QEMU Guest Agent](https://pve.proxmox.com/wiki/Qemu-guest-agent) (recommended) 302 | - [Cloudbase-Init](https://cloudbase.it/cloudbase-init/) (optional) 303 | 304 | Please refer to [Hyper-V Automation](https://github.com/fdcastel/Hyper-V-Automation#examples) project for more information. 305 | 306 | 307 | 308 | ### Examples 309 | 310 | Creates a Windows VM from a [`vhdx` template](https://github.com/fdcastel/Hyper-V-Automation#create-a-windows-vhdx-template-for-qemu) previously created with [`New-VHDXFromWindowsImage.ps1`](https://github.com/fdcastel/Hyper-V-Automation#new-vhdxfromwindowsimage-) and initializes the Administrator password via CloudBase-Init. 311 | 312 | ```bash 313 | VM_ID=103 314 | ./new-vm-windows.sh $VM_ID \ 315 | --image '/tmp/Server2025Standard-template.vhdx' \ 316 | --name 'tst-win2025' \ 317 | --ide2 local-zfs:cloudinit \ 318 | --cipassword 'Unsaf3@AnySp33d!' 319 | 320 | # You can run any commands on VM with "qm guest exec": 321 | qm guest exec $VM_ID -- powershell -c $(cat << 'EOF' 322 | <# Enables ICMP Echo Request (ping) for IPv4 and IPv6 #> 323 | Get-NetFirewallRule -Name 'FPS-ICMP*' | Set-NetFirewallRule -Enabled:True ; 324 | 325 | <# Enables Remote Desktop (more secure) #> 326 | $tsSettings = Get-WmiObject -Class 'Win32_TerminalServiceSetting' -Namespace root\cimv2\terminalservices ; 327 | $tsSettings.SetAllowTsConnections(1, 1) ; 328 | Enable-NetFirewallRule -DisplayGroup "Remote Desktop" ; 329 | 330 | <# Enables SSH (Server 2025 only) #> 331 | Start-Service sshd ; 332 | Set-Service -Name sshd -StartupType 'Automatic' ; 333 | Set-NetFirewallRule OpenSSH-Server-In-TCP -Profile Any 334 | EOF 335 | ) 336 | ``` 337 | 338 | Creates a Windows VM from a [previously prepared `vhdx`](https://github.com/fdcastel/Hyper-V-Automation#prepare-a-vhdx-for-qemu-migration) of an existing Hyper-V VM. 339 | 340 | ```bash 341 | VM_ID=104 342 | ./new-vm-windows.sh $VM_ID --image '/tmp/TstWindows.vhdx' --name 'TstWindows' 343 | 344 | # Query ipv4 addresses 345 | qm guest cmd $VM_ID network-get-interfaces | \ 346 | jq -r '.[] | .["ip-addresses"][] | select(.["ip-address-type"]=="ipv4") | .["ip-address"]' 347 | ``` 348 | 349 | 350 | 351 | ## remove-nag-subscription 352 | 353 | ``` 354 | Usage: ./remove-nag-subscription.sh 355 | ``` 356 | 357 | Removes Proxmox VE / Proxmox Backup Server nag dialog from web UI. 358 | 359 | 360 | 361 | ## setup-pbs 362 | 363 | ``` 364 | Usage: ./setup-pbs.sh 365 | ``` 366 | 367 | First-time setup for Proxmox Backup Server. 368 | 369 | Remove `enterprise` (subscription-only) sources and adds `pbs-no-subscription` repository provided by [proxmox.com](https://proxmox.com). NOT recommended for production use. 370 | 371 | This script must be run only once. 372 | 373 | 374 | 375 | ## setup-pve 376 | 377 | ``` 378 | Usage: ./setup-pve.sh 379 | ``` 380 | 381 | First-time setup for Proxmox VE. 382 | 383 | Remove `enterprise` (subscription-only) sources and adds `pve-no-subscription` repository provided by [proxmox.com](https://proxmox.com). NOT recommended for production use. 384 | 385 | This script must be run only once. 386 | 387 | 388 | 389 | # Using Docker on LXC 390 | 391 | The following is a compilation about the subject I found around the net. Please read if you wish to follow this path. 392 | 393 | 394 | 395 | ## Overview 396 | 397 | **Using Docker on LXC is not recommended by Proxmox team.** However, certain features of LXC like reduced memory usage and bind mount points between containers and host may be an incentive to go against this recommendation. 398 | 399 | Two discussions about the pros and cons of each alternative may be found [here](https://forum.proxmox.com/threads/proxmox-7-1-and-docker-lxc-vs-vm.105140) and [here](https://www.reddit.com/r/Proxmox/comments/xno101/using_multiple_lxc_vs_multiple_lxcdocker_vs/). 400 | 401 | 402 | 403 | ## Backups 404 | 405 | Backups (both to local storage and to Proxmox Backup Server) work fine. 406 | 407 | However, please note that **the contents of `/var/lib/docker` will be included in backups** by default. This is probably NOT what you want. 408 | 409 | > This folder often grows in size very quickly. And its contents (except for Docker volumes, see below) may easily be downloaded or rebuilt. 410 | 411 | To avoid this, you may use a [`.pxarexclude` file](https://pbs.proxmox.com/docs/backup-client.html#excluding-files-directories-from-a-backup) to exclude its contents from the backup archive. 412 | 413 | ```bash 414 | cat > /.pxarexclude < 34 | Storage Driver: vfs 35 | <...> 36 | ``` 37 | 38 | To avoid this, you have 3 options: 39 | 40 | - use [`fuse-overlayfs`](https://github.com/containers/fuse-overlayfs) 41 | - use [`zfs` storage driver](https://docs.docker.com/storage/storagedriver/zfs-driver/) 42 | - use `overlay2` storage driver 43 | 44 | To make a long story short, I did not find any successful report of the two first options. 45 | 46 | `overlay2` is a [stable and recommended driver](https://docs.docker.com/storage/storagedriver/select-storage-driver/) but only works with `xfs` and `ext4` filesystems. 47 | 48 | This did not not stop [u/volopasse](https://www.reddit.com/r/Proxmox/comments/lsrt28/comment/goubt7u/?utm_source=reddit&utm_medium=web2x&context=3) some years ago to find a workaround: to create a sparse zfs volume formatted as `ext4` and use it as a bind mount point for `/var/lib/docker`. This will make Docker use `overlay2` without any changes needed in Docker configuration. 49 | 50 | It may seem a hack (which it is) but it [reportedly works better than the very own Docker ZFS driver](https://github.com/moby/moby/issues/31247#issuecomment-611976248). 51 | 52 | All these steps are wrapped into [`new-ct-docker-volume.sh`](new-ct-docker-volume.sh) script, which is also used by [`new-ct.sh`](new-ct.sh) script when invoked with `--install-docker` option. 53 | 54 | From my personal experience: I am using this solution for more than a year now in my home servers with zero problems of performance nor stability. That said I do not use nor recommend this solution in any production capacity. Also, see [Caveats](#caveats) section below. 55 | 56 | 57 | 58 | ## Caveats when using Docker on LXC 59 | 60 | ### Backups 61 | 62 | Backups (both to local storage and to Proxmox Backup Server) works fine. 63 | 64 | However, please note that **the contents of `/var/lib/docker` will not be included in backups**. 65 | 66 | Because of this you should not use [Docker volumes](https://docs.docker.com/storage/volumes/) to store any persistent data which is important since they will be kept at this location (and, again, will **not** be included in backups). 67 | 68 | Instead you should use [Docker bind mounts](https://docs.docker.com/storage/bind-mounts/) which mounts a file or directory from the Docker host (LXC, in our case) into a Docker container. All files from LXC filesystem will be included into backups. 69 | 70 | Lastly, since we are talking about backups, please remember again that all this is very new and _not recommended by Proxmox team_. I put these scripts initially for my personal usage and they have yet a long way to run until be considered stable and battle-tested. Thus, caution is advised. 71 | 72 | 73 | 74 | ### Restoring backups 75 | 76 | To restore an existing backup you _must_ use [restore-ct](#restore-ct) script with the `--restore-docker` option. This will rebuild the `zfs` volume for `/var/lib/docker` and mount it correctly. 77 | 78 | Simply using the _Restore_ command from Proxmox Web UI will fail since it doesn't know what to do with `/var/lib/docker/` bind mount point. 79 | 80 | 81 | 82 | ### Migrations & Snapshots 83 | 84 | Originally this method caused migrations and snapshot to fail. 85 | 86 | [iGadget](https://github.com/alexpdp7/ansible-create-proxmox-host/issues/1#issue-1492924235) found that naming the ZFS volume in a very specific way solves the snapshot and migration problems. This naming scheme is already adopted by these scripts so, for now, migrations and snapshots are working. 87 | 88 | However, please be aware that this takes advantage of a very specific way Proxmox was implemented and it may break in future Proxmox versions. 89 | -------------------------------------------------------------------------------- /download-cloud-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit when any command fails 4 | 5 | 6 | # 7 | # Constants 8 | # 9 | 10 | ISO_FOLDER='/var/lib/vz/template/iso/' 11 | 12 | 13 | # 14 | # Functions 15 | # 16 | 17 | function echo_err() { 18 | >&2 echo "$@" 19 | } 20 | 21 | function show_usage() { 22 | if [ -n "$1" ]; then 23 | tput setaf 1 24 | echo_err "Error: $1"; 25 | tput sgr0 26 | fi 27 | echo_err 28 | echo_err "Usage: $0 [OPTIONS]" 29 | echo_err ' Url of image to download.' 30 | echo_err " --no-clobber, -nc Doesn't overwrite an existing image." 31 | echo_err " --help, -h Display this help message." 32 | echo_err 33 | exit 1 34 | } 35 | 36 | 37 | # 38 | # Main 39 | # 40 | 41 | NO_CLOBBER=0 42 | 43 | # Parse arguments -- https://stackoverflow.com/a/14203146/33244 44 | POSITIONAL_ARGS=() 45 | while [[ "$#" -gt 0 ]]; do case $1 in 46 | -nc|--no-clobber) NO_CLOBBER=1; shift;; 47 | 48 | -h|--help) show_usage;; 49 | -*|--*) show_usage "Unknown option: $1";; 50 | *) POSITIONAL_ARGS+=("$1"); shift;; 51 | esac; done 52 | set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters 53 | 54 | URL="$1" 55 | if [ -z "$URL" ]; then show_usage "You must inform an url."; fi; 56 | 57 | # Extract the base file name from a URL -- https://unix.stackexchange.com/a/64435 58 | URL_FILE="${URL##*/}" 59 | 60 | LOCAL_FILE="$ISO_FOLDER/$URL_FILE" 61 | 62 | UNCOMPRESSED_LOCAL_FILE="$LOCAL_FILE" 63 | case $LOCAL_FILE in 64 | *"gz"|*"xz"|*"zip") UNCOMPRESSED_LOCAL_FILE=${LOCAL_FILE%.*};; 65 | esac 66 | 67 | if [ $NO_CLOBBER -eq 1 ] && [ -f "$UNCOMPRESSED_LOCAL_FILE" ]; then 68 | echo $UNCOMPRESSED_LOCAL_FILE 69 | exit 0 70 | fi 71 | 72 | wget -O $LOCAL_FILE $URL --show-progress 73 | if [ $? -ne 0 ]; then 74 | echo_err "Error downloading '$URL'." 75 | exit 1 76 | fi 77 | 78 | # Uncompress file (if needed) 79 | case $LOCAL_FILE in 80 | *"gz") echo_err "Extracting (.gz)..."; gunzip -d $LOCAL_FILE -f -v;; 81 | *"xz") echo_err "Extracting (.xz)..."; xz -d $LOCAL_FILE -f -v;; 82 | *"zip") echo_err "Extracting (.zip)..."; unzip -o $LOCAL_FILE -d $ISO_FOLDER 1>&2 && rm $LOCAL_FILE;; # unzip outputs info to stdout 83 | esac 84 | 85 | # Return the full path of downloaded file 86 | echo $UNCOMPRESSED_LOCAL_FILE 87 | -------------------------------------------------------------------------------- /download-cloud-template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit when any command fails 4 | 5 | 6 | # 7 | # Constants 8 | # 9 | 10 | ISO_FOLDER='/var/lib/vz/template/cache/' 11 | 12 | 13 | # 14 | # Functions 15 | # 16 | 17 | function echo_err() { 18 | >&2 echo "$@" 19 | } 20 | 21 | function show_usage() { 22 | if [ -n "$1" ]; then 23 | tput setaf 1 24 | echo_err "Error: $1"; 25 | tput sgr0 26 | fi 27 | echo_err 28 | echo_err "Usage: $0 [OPTIONS]" 29 | echo_err ' Url of template to download.' 30 | echo_err " --filename, -f Renames the downloaded file." 31 | echo_err " --no-clobber, -nc Doesn't overwrite an existing template." 32 | echo_err " --help, -h Display this help message." 33 | echo_err 34 | exit 1 35 | } 36 | 37 | 38 | # 39 | # Main 40 | # 41 | 42 | NO_CLOBBER=0 43 | FILENAME= 44 | 45 | # Parse arguments -- https://stackoverflow.com/a/14203146/33244 46 | POSITIONAL_ARGS=() 47 | while [[ "$#" -gt 0 ]]; do case $1 in 48 | -nc|--no-clobber) NO_CLOBBER=1; shift;; 49 | -f|--filename) FILENAME="$2"; shift; shift;; 50 | 51 | -h|--help) show_usage;; 52 | -*|--*) show_usage "Unknown option: $1";; 53 | *) POSITIONAL_ARGS+=("$1"); shift;; 54 | esac; done 55 | set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters 56 | 57 | URL="$1" 58 | if [ -z "$URL" ]; then show_usage "You must inform an url."; fi; 59 | 60 | if [ -n "$FILENAME" ]; then 61 | LOCAL_FILE="$ISO_FOLDER/$FILENAME" 62 | else 63 | # Extract the base file name from a URL -- https://unix.stackexchange.com/a/64435 64 | URL_FILE="${URL##*/}" 65 | LOCAL_FILE="$ISO_FOLDER/$URL_FILE" 66 | fi 67 | 68 | if [ $NO_CLOBBER -eq 1 ] && [ -f "$LOCAL_FILE" ]; then 69 | echo $LOCAL_FILE 70 | exit 0 71 | fi 72 | 73 | wget -O $LOCAL_FILE $URL --show-progress 74 | if [ $? -ne 0 ]; then 75 | echo_err "Error downloading '$URL'." 76 | exit 1 77 | fi 78 | 79 | # Return the full path of downloaded file 80 | echo $LOCAL_FILE 81 | -------------------------------------------------------------------------------- /new-ct.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit when any command fails 4 | 5 | 6 | # 7 | # Constants 8 | # 9 | 10 | DEFAULT_OSTYPE='ubuntu' 11 | DEFAULT_MEMORY=2048 12 | DEFAULT_ROOTFS='local-zfs:120' 13 | DEFAULT_BRIDGE='vmbr0' 14 | 15 | # 16 | # Functions 17 | # 18 | 19 | function echo_err() { 20 | >&2 echo "$@" 21 | } 22 | 23 | function show_usage() { 24 | if [ -n "$1" ]; then 25 | tput setaf 1 26 | echo "Error: $1"; 27 | tput sgr0 28 | fi 29 | echo_err 30 | echo_err "Usage: $0 --ostemplate --hostname --password [OPTIONS]" 31 | echo_err ' Proxmox unique ID of the CT.' 32 | echo_err ' --ostemplate The OS template or backup file.' 33 | echo_err ' --hostname Set a host name for the container.' 34 | echo_err ' --password Sets root password inside container.' 35 | echo_err ' --sshkey[s] Setup public SSH keys (one key per line, OpenSSH format).' 36 | echo_err 37 | echo_err 'Additional options:' 38 | echo_err " --ostype OS type (default = $DEFAULT_OSTYPE)." 39 | echo_err " --cores Number of cores per socket (default = unlimited)." 40 | echo_err " --memory Amount of RAM for the VM in MB (default = $DEFAULT_MEMORY)." 41 | echo_err " --rootfs Use volume as container root (default = $DEFAULT_ROOTFS)." 42 | echo_err ' --privileged Makes the container run as privileged user (default = unprivileged).' 43 | echo_err " --bridge Use bridge for container networking (default = $DEFAULT_BRIDGE)." 44 | echo_err " --hwaddr MAC address for eth0 interface." 45 | echo_err ' --install-docker Install docker and docker-compose.' 46 | echo_err ' --no-start Do not start the container after creation.' 47 | echo_err " --help, -h Display this help message." 48 | echo_err 49 | echo_err "Any additional arguments are passed to 'pct create' command." 50 | echo_err 51 | exit 1 52 | } 53 | 54 | 55 | # 56 | # Main 57 | # 58 | 59 | CT_OSTEMPLATE= 60 | CT_HOSTNAME= 61 | CT_PASSWORD= 62 | CT_SSHKEYS= 63 | CT_OSTYPE=$DEFAULT_OSTYPE 64 | CT_CORES= 65 | CT_MEMORY=$DEFAULT_MEMORY 66 | CT_ROOTFS=$DEFAULT_ROOTFS 67 | CT_UNPRIVILEGED=1 68 | CT_BRIDGE=$DEFAULT_BRIDGE 69 | CT_HWADDR= 70 | CT_INSTALL_DOCKER=0 71 | CT_NO_START=0 72 | 73 | # Parse arguments -- https://stackoverflow.com/a/14203146/33244 74 | POSITIONAL_ARGS=() 75 | while [[ "$#" -gt 0 ]]; do case $1 in 76 | --ostemplate) CT_OSTEMPLATE="$2"; shift; shift;; 77 | --hostname) CT_HOSTNAME="$2"; shift; shift;; 78 | --password) CT_PASSWORD="$2"; shift; shift;; 79 | --sshkey|--sshkeys) CT_SSHKEYS="$2"; shift; shift;; 80 | 81 | --ostype) CT_OSTYPE="$2"; shift; shift;; 82 | --cores) CT_CORES="$2"; shift; shift;; 83 | --memory) CT_MEMORY="$2"; shift; shift;; 84 | --rootfs) CT_ROOTFS="$2"; shift; shift;; 85 | --bridge) CT_BRIDGE="$2"; shift; shift;; 86 | --hwaddr) CT_HWADDR="$2"; shift; shift;; 87 | --privileged) CT_UNPRIVILEGED=0; shift;; 88 | 89 | --install-docker) CT_INSTALL_DOCKER=1; shift;; 90 | --no-start) CT_NO_START=1; shift;; 91 | 92 | -h|--help) show_usage;; 93 | *) POSITIONAL_ARGS+=("$1"); shift;; 94 | esac; done 95 | set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters 96 | 97 | CT_ID="$1"; shift 98 | if [ -z "$CT_ID" ]; then show_usage "You must inform a CT id."; fi; 99 | if [ -z "$CT_OSTEMPLATE" ]; then show_usage "You must inform an OS template (--ostemplate)."; fi; 100 | if [ -z "$CT_HOSTNAME" ]; then show_usage "You must inform a host name (--hostname)."; fi; 101 | if [ -z "$CT_PASSWORD" ] && [ -z "$CT_SSHKEYS" ]; then show_usage "You must inform either a password (--password) or a public ssh key file (--sshkeys)."; fi; 102 | if [ $CT_INSTALL_DOCKER -eq 1 ] && [ $CT_NO_START -eq 1 ]; then show_usage "Options --install-docker and --no-start are mutually exclusive. Docker installation requires the container to be running."; fi; 103 | 104 | PASSWORD_ARGS= 105 | if [ -n "$CT_PASSWORD" ]; then 106 | PASSWORD_ARGS="--password $CT_PASSWORD" 107 | fi; 108 | 109 | SSH_KEYS_ARGS= 110 | if [ -n "$CT_SSHKEYS" ]; then 111 | SSH_KEYS_ARGS="--ssh-public-keys $CT_SSHKEYS" 112 | fi; 113 | 114 | DOCKER_ARGS= 115 | if [ $CT_INSTALL_DOCKER -eq 1 ]; then 116 | if [ "$CT_OSTYPE" != 'ubuntu' ] && [ "$CT_OSTYPE" != 'debian' ] && [ "$CT_OSTYPE" != 'alpine' ]; then 117 | show_usage "Don't know how to install docker on '$OSTYPE'."; 118 | fi 119 | 120 | # Extra arguments required for Docker 121 | DOCKER_ARGS="--features keyctl=1,nesting=1" 122 | fi; 123 | 124 | CORES_ARGS= 125 | if [ -n "$CT_CORES" ]; then 126 | CORES_ARGS="--cores $CT_CORES" 127 | fi; 128 | 129 | CT_INTERFACE_NAME='eth0' 130 | 131 | NET0_EXTRA_ARGS= 132 | if [ -n "$CT_HWADDR" ]; then 133 | NET0_EXTRA_ARGS=",hwaddr=$CT_HWADDR" 134 | fi; 135 | 136 | # Create CT 137 | pct create $CT_ID $CT_OSTEMPLATE \ 138 | --ostype $CT_OSTYPE \ 139 | --cmode shell \ 140 | --hostname $CT_HOSTNAME \ 141 | --rootfs $CT_ROOTFS \ 142 | --net0 name=$CT_INTERFACE_NAME,bridge=$CT_BRIDGE,ip=dhcp$NET0_EXTRA_ARGS \ 143 | $CORES_ARGS \ 144 | --memory $CT_MEMORY \ 145 | --onboot 1 \ 146 | --unprivileged $CT_UNPRIVILEGED \ 147 | $DOCKER_ARGS \ 148 | $PASSWORD_ARGS \ 149 | $SSH_KEYS_ARGS \ 150 | "$@" # pass remaining arguments -- https://stackoverflow.com/a/4824637/33244 151 | 152 | # Cannot set timezone in 'pct create' (Bug?) 153 | # Causes "Insecure dependency in symlink while running with -T switch at /usr/share/perl5/PVE/LXC/Setup/Base.pm" 154 | # pveversion: pve-manager/7.0-11/63d82f4e (running kernel: 5.11.22-4-pve) 155 | pct set $CT_ID --timezone host 156 | 157 | # Start container 158 | if [ $CT_NO_START -eq 1 ]; then exit 0; fi; 159 | pct start $CT_ID 160 | 161 | # Install docker 162 | if [ $CT_INSTALL_DOCKER -eq 0 ]; then exit 0; fi; 163 | 164 | # Wait for network -- Source: https://stackoverflow.com/a/24963234 165 | pct exec $CT_ID -- sh < /dev/null 2>&1); do 168 | echo "Waiting for \$WAIT_FOR_HOST - network interface might be down..." 169 | sleep 1 170 | done 171 | EOF 172 | 173 | if [ "$CT_OSTYPE" == 'ubuntu' ] || [ "$CT_OSTYPE" == 'debian' ]; then 174 | # Install docker 175 | # https://docs.docker.com/engine/install/ubuntu/ 176 | # https://docs.docker.com/engine/install/debian/ 177 | pct exec $CT_ID -- sh <<'EOF' 178 | apt-get update 179 | apt-get install -y ca-certificates curl 180 | install -m 0755 -d /etc/apt/keyrings 181 | . /etc/os-release && curl -fsSL https://download.docker.com/linux/$ID/gpg -o /etc/apt/keyrings/docker.asc 182 | chmod a+r /etc/apt/keyrings/docker.asc 183 | . /etc/os-release && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$ID $VERSION_CODENAME stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null 184 | apt-get update -y 185 | apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 186 | EOF 187 | fi 188 | 189 | if [ "$CT_OSTYPE" == 'alpine' ]; then 190 | # Install docker 191 | # https://wiki.alpinelinux.org/wiki/Docker 192 | pct exec $CT_ID -- sh < /dev/null 202 | if [ $? -ne 0 ]; then 203 | tput setaf 1 204 | echo_err 'WARNING: Docker storage driver is not "overlay2".' 205 | tput sgr0 206 | fi 207 | -------------------------------------------------------------------------------- /new-vm-windows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit when any command fails 4 | 5 | 6 | # 7 | # Constants 8 | # 9 | 10 | DEFAULT_OSTYPE='win11' 11 | DEFAULT_CORES=2 12 | DEFAULT_MEMORY=2048 13 | 14 | 15 | # 16 | # Functions 17 | # 18 | 19 | function echo_err() { 20 | >&2 echo "$@" 21 | } 22 | 23 | function show_usage() { 24 | if [ -n "$1" ]; then 25 | tput setaf 1 26 | echo "Error: $1"; 27 | tput sgr0 28 | fi 29 | echo_err 30 | echo_err "Usage: $0 --image --name [OPTIONS]" 31 | echo_err ' Proxmox unique ID of the VM.' 32 | echo_err ' --image Source image to import (.qcow2 | .vhdx).' 33 | echo_err ' --name A name for the VM.' 34 | echo_err 35 | echo_err 'Additional options:' 36 | echo_err " --ostype Guest OS type (default = $DEFAULT_OSTYPE)." 37 | echo_err " --cores Number of cores per socket (default = $DEFAULT_CORES)." 38 | echo_err " --memory Amount of RAM for the VM in MB (default = $DEFAULT_MEMORY)." 39 | echo_err " --no-start Do not start the VM after creation." 40 | echo_err " --no-guest Do not wait for QEMU Guest Agent after start." 41 | echo_err " --help, -h Display this help message." 42 | echo_err 43 | echo_err "Any additional arguments are passed to 'qm create' command." 44 | echo_err 45 | exit 1 46 | } 47 | 48 | 49 | # 50 | # Main 51 | # 52 | 53 | VM_OSTYPE=$DEFAULT_OSTYPE 54 | VM_CORES=$DEFAULT_CORES 55 | VM_MEMORY=$DEFAULT_MEMORY 56 | VM_NO_START=0 57 | VM_NO_GUEST=0 58 | 59 | # Parse arguments -- https://stackoverflow.com/a/14203146/33244 60 | POSITIONAL_ARGS=() 61 | while [[ "$#" -gt 0 ]]; do case $1 in 62 | --image) VM_IMAGE="$2"; shift; shift;; 63 | --name) VM_NAME="$2"; shift; shift;; 64 | 65 | --ostype) VM_OSTYPE="$2"; shift; shift;; 66 | --cores) VM_CORES="$2"; shift; shift;; 67 | --memory) VM_MEMORY="$2"; shift; shift;; 68 | 69 | --no-start) VM_NO_START=1; shift;; 70 | --no-guest) VM_NO_GUEST=1; shift;; 71 | 72 | -h|--help) show_usage;; 73 | *) POSITIONAL_ARGS+=("$1"); shift;; 74 | esac; done 75 | set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters 76 | 77 | VM_ID="$1"; shift 78 | if [ -z "$VM_ID" ]; then show_usage "You must inform a VM id."; fi; 79 | if [ -z "$VM_IMAGE" ]; then show_usage "You must inform a image file (--image)."; fi; 80 | if [ -z "$VM_NAME" ]; then show_usage "You must inform a VM name (--name)."; fi; 81 | 82 | 83 | 84 | # Create VM 85 | # Disables balloon driver due poor performance on Windows 86 | # Source: https://pve.proxmox.com/wiki/Performance_Tweaks#Do_not_use_the_Virtio_Balloon_Driver 87 | VM_BALLOON=0 88 | 89 | qm create $VM_ID --name $VM_NAME \ 90 | --cpu host \ 91 | --ostype $VM_OSTYPE \ 92 | --scsihw virtio-scsi-single \ 93 | --agent 1 \ 94 | --bios ovmf \ 95 | --machine q35 \ 96 | --net0 virtio,bridge=vmbr0 \ 97 | --cores $VM_CORES \ 98 | --numa 1 \ 99 | --memory $VM_MEMORY \ 100 | --balloon $VM_BALLOON \ 101 | --vga type=virtio \ 102 | --onboot 1 \ 103 | "$@" # pass remaining arguments -- https://stackoverflow.com/a/4824637/33244 104 | 105 | # Disk 0: EFI 106 | pvesm alloc local-zfs $VM_ID vm-$VM_ID-efi 1M 107 | qm set $VM_ID --efidisk0 local-zfs:vm-$VM_ID-efi 108 | 109 | # Disk 1: Main disk. 110 | qm importdisk $VM_ID $VM_IMAGE local-zfs --format 'raw' 111 | qm set $VM_ID --scsi1 local-zfs:vm-$VM_ID-disk-0,discard=on,iothread=1,ssd=1 \ 112 | --boot c \ 113 | --bootdisk scsi1 114 | 115 | 116 | 117 | # Start VM 118 | if [ $VM_NO_START -eq 1 ]; then exit 0; fi; 119 | qm start $VM_ID 120 | 121 | # Wait for qemu-guest-agent 122 | if [ $VM_NO_GUEST -eq 1 ]; then exit 0; fi; 123 | echo "Waiting for VM $VM_ID..." 124 | until qm agent $VM_ID ping 125 | do 126 | sleep 2 127 | done 128 | -------------------------------------------------------------------------------- /new-vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit when any command fails 4 | 5 | 6 | # 7 | # Constants 8 | # 9 | 10 | DEFAULT_OSTYPE='l26' 11 | DEFAULT_CORES=2 12 | DEFAULT_MEMORY=2048 13 | DEFAULT_DISKSIZE='120G' 14 | 15 | 16 | # 17 | # Functions 18 | # 19 | 20 | function echo_err() { 21 | >&2 echo "$@" 22 | } 23 | 24 | function show_usage() { 25 | if [ -n "$1" ]; then 26 | tput setaf 1 27 | echo "Error: $1"; 28 | tput sgr0 29 | fi 30 | echo_err 31 | echo_err "Usage: $0 --image --name [--cipassword ] | [--sshkey[s] ] [OPTIONS]" 32 | echo_err ' Proxmox unique ID of the VM.' 33 | echo_err ' --image Path to image file.' 34 | echo_err ' --name A name for the VM.' 35 | echo_err ' --cipassword Password to assign the user. Using this is generally not recommended. Use ssh keys instead.' 36 | echo_err ' --sshkey[s] Setup public SSH keys (one key per line, OpenSSH format).' 37 | echo_err 38 | echo_err 'Additional options:' 39 | echo_err " --ostype Guest OS type (default = $DEFAULT_OSTYPE)." 40 | echo_err " --cores Number of cores per socket (default = $DEFAULT_CORES)." 41 | echo_err " --memory Amount of RAM for the VM in MB (default = $DEFAULT_MEMORY)." 42 | echo_err " --disksize Size of VM main disk (default = $DEFAULT_DISKSIZE)." 43 | echo_err ' --balloon Amount of target RAM for the VM in MB. Using zero (default) disables the ballon driver.' 44 | echo_err ' --install-docker Install docker and docker-compose.' 45 | echo_err " --no-start Do not start the VM after creation." 46 | echo_err " --no-guest Do not wait for QEMU Guest Agent after start." 47 | echo_err " --help, -h Display this help message." 48 | echo_err 49 | echo_err "Any additional arguments are passed to 'qm create' command." 50 | echo_err 51 | exit 1 52 | } 53 | 54 | 55 | # 56 | # Main 57 | # 58 | 59 | VM_IMAGE= 60 | VM_NAME= 61 | VM_CIPASSWORD= 62 | VM_SSHKEYS= 63 | VM_OSTYPE=$DEFAULT_OSTYPE 64 | VM_CORES=$DEFAULT_CORES 65 | VM_MEMORY=$DEFAULT_MEMORY 66 | VM_DISKSIZE=$DEFAULT_DISKSIZE 67 | VM_BALLOON=0 68 | VM_INSTALL_DOCKER=0 69 | VM_NO_START=0 70 | VM_NO_GUEST=0 71 | 72 | # Parse arguments -- https://stackoverflow.com/a/14203146/33244 73 | POSITIONAL_ARGS=() 74 | while [[ "$#" -gt 0 ]]; do case $1 in 75 | --image) VM_IMAGE="$2"; shift; shift;; 76 | --name) VM_NAME="$2"; shift; shift;; 77 | --cipassword) VM_CIPASSWORD="$2"; shift; shift;; 78 | --sshkey|--sshkeys) VM_SSHKEYS="$2"; shift; shift;; 79 | 80 | --ostype) VM_OSTYPE="$2"; shift; shift;; 81 | --cores) VM_CORES="$2"; shift; shift;; 82 | --memory) VM_MEMORY="$2"; shift; shift;; 83 | --disksize) VM_DISKSIZE="$2"; shift; shift;; 84 | --balloon) VM_BALLOON="$2"; shift; shift;; 85 | --install-docker) VM_INSTALL_DOCKER=1; shift;; 86 | 87 | --no-start) VM_NO_START=1; shift;; 88 | --no-guest) VM_NO_GUEST=1; shift;; 89 | 90 | -h|--help) show_usage;; 91 | *) POSITIONAL_ARGS+=("$1"); shift;; 92 | esac; done 93 | set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters 94 | 95 | VM_ID="$1"; shift 96 | if [ -z "$VM_ID" ]; then show_usage "You must inform a VM id."; fi; 97 | if [ -z "$VM_IMAGE" ]; then show_usage "You must inform an image file (--image)."; fi; 98 | if [ -z "$VM_NAME" ]; then show_usage "You must inform a VM name (--name)."; fi; 99 | if [ -z "$VM_CIPASSWORD" ] && [ -z "$VM_SSHKEYS" ]; then show_usage "You must inform either a password (--cipassword) or a public ssh key file (--sshkeys)."; fi; 100 | 101 | 102 | 103 | # Create VM 104 | qm create $VM_ID --name $VM_NAME \ 105 | --cpu host \ 106 | --ostype $VM_OSTYPE \ 107 | --scsihw virtio-scsi-single \ 108 | --agent 1 \ 109 | --bios ovmf \ 110 | --machine q35 \ 111 | --net0 virtio,bridge=vmbr0 \ 112 | --cores $VM_CORES \ 113 | --numa 1 \ 114 | --memory $VM_MEMORY \ 115 | --balloon $VM_BALLOON \ 116 | --vga type=virtio \ 117 | --onboot 1 \ 118 | "$@" # pass remaining arguments -- https://stackoverflow.com/a/4824637/33244 119 | 120 | # Disk 0: EFI 121 | pvesm alloc local-zfs $VM_ID vm-$VM_ID-efi 1M 122 | qm set $VM_ID --efidisk0 local-zfs:vm-$VM_ID-efi 123 | 124 | # Disk 1: Main disk 125 | qm importdisk $VM_ID $VM_IMAGE local-zfs 126 | qm set $VM_ID --scsi1 local-zfs:vm-$VM_ID-disk-0,discard=on,iothread=1,ssd=1 \ 127 | --boot c \ 128 | --bootdisk scsi1 129 | qm resize $VM_ID scsi1 $VM_DISKSIZE 130 | 131 | # Disk 2: cloud-init 132 | # Do not use --ide or Debian 'genericcloud' image will not work. 133 | qm set $VM_ID --scsi2 local-zfs:cloudinit 134 | 135 | 136 | 137 | # Initialize VM via cloud-init 138 | mkdir -p /var/lib/vz/snippets/ 139 | CI_USER_FILE="vm-$VM_ID-cloud-init-user.yml" 140 | CI_USER_FILE_FULL="/var/lib/vz/snippets/$CI_USER_FILE" 141 | 142 | qm set $VM_ID --serial0 socket \ 143 | --ipconfig0 ip=dhcp \ 144 | --cicustom "user=local:snippets/$CI_USER_FILE" 145 | 146 | if [ -n "$VM_CIPASSWORD" ]; then 147 | qm set $VM_ID --cipassword $VM_CIPASSWORD 148 | fi; 149 | 150 | if [ -n "$VM_SSHKEYS" ]; then 151 | qm set $VM_ID --sshkey $VM_SSHKEYS 152 | fi; 153 | 154 | qm cloudinit dump $VM_ID user > $CI_USER_FILE_FULL 155 | 156 | INTERFACE_NAME='eth0' 157 | cat >> $CI_USER_FILE_FULL <> $CI_USER_FILE_FULL <<'EOF' 182 | - 'apt-get update' 183 | - 'apt-get install -y ca-certificates curl' 184 | - 'install -m 0755 -d /etc/apt/keyrings' 185 | - '. /etc/os-release && curl -fsSL https://download.docker.com/linux/$ID/gpg -o /etc/apt/keyrings/docker.asc' 186 | - 'chmod a+r /etc/apt/keyrings/docker.asc' 187 | - '. /etc/os-release && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$ID $VERSION_CODENAME stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null' 188 | - 'apt-get update -y' 189 | - 'apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin' 190 | EOF 191 | fi 192 | 193 | 194 | 195 | # Start VM 196 | if [ $VM_NO_START -eq 0 ]; then 197 | qm start $VM_ID 198 | fi; 199 | 200 | # Wait for qemu-guest-agent 201 | if [ $VM_NO_GUEST -eq 0 ]; then 202 | echo "Waiting for VM $VM_ID..." 203 | until qm agent $VM_ID ping 204 | do 205 | sleep 2 206 | done 207 | fi; 208 | 209 | # Remove custom cloud-init configuration 210 | qm set $VM_ID --delete cicustom 211 | rm $CI_USER_FILE_FULL 212 | -------------------------------------------------------------------------------- /remove-nag-subscription.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Disable nag subscription dialog (working with v7.x) 5 | # 6 | PROXMOXLIB_FILE=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 7 | cp $PROXMOXLIB_FILE $PROXMOXLIB_FILE.bak 8 | SEARCH_REGEX="Ext\.Msg\.show\(\{\s*title: gettext\('No valid subscription'\)," 9 | REPLACE_TEXT="void\(\{\n title: gettext\('No valid subscription'\)," 10 | perl -0777 -i -p -e "s/\b$SEARCH_REGEX/$REPLACE_TEXT/igs" $PROXMOXLIB_FILE 11 | systemctl restart pveproxy.service 12 | -------------------------------------------------------------------------------- /setup-pbs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PBS_ENTERPRISE_SOURCES_FILE='/etc/apt/sources.list.d/pbs-enterprise.list' 4 | 5 | # 6 | # Run-only-once check 7 | # 8 | FIRST_LINE=$(head -1 $PBS_ENTERPRISE_SOURCES_FILE) 9 | if [ "$FIRST_LINE" == '# Disable pbs-enterprise' ]; then 10 | echo 'This script must be run only once.' 11 | exit 1 12 | fi 13 | 14 | # 15 | # Remove enterprise (subscription-only) sources 16 | # 17 | cat > $PBS_ENTERPRISE_SOURCES_FILE <> /etc/apt/sources.list < $PVE_ENTERPRISE_SOURCES_FILE <> /etc/apt/sources.list <