Unattended deployments
Create containers without the menu, which prompts remain, and how to answer each one in advance.
A container script opens its install menu unless mode is set. Everything
else that can ask a question has a variable or a file that answers it. This
page covers container scripts on a Proxmox VE host; the behaviour described is
in ui/menu.func,
ui/defaults.func,
core/core.func and
pve/backend.func.
One container
mode=generated var_unattended=yes \
var_os=debian var_ram=1024 var_disk=4 \
var_hostname=adguard var_net=192.168.1.53/24 var_gateway=192.168.1.1 \
var_container_storage=local-lvm var_template_storage=local \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/adguard.sh)"What each part does:
| Part | Why it is there |
|---|---|
mode=generated | Skips the menu and takes every setting from the script and the command line. No .vars file is read or written. |
var_unattended=yes | Makes the engine's prompt helpers take their default instead of waiting. |
var_os=debian | ct/adguard.sh offers Debian or Alpine and asks which; this answers it. |
var_container_storage, var_template_storage | Preselect the storages so the Storage Pools dialog stays closed. Use names from pvesm status. |
| the rest | Ordinary settings from the configuration reference. |
The generator builds this kind of command from a form, also with
mode=generated.
Which mode to use
mode= | Use it when | Storage |
|---|---|---|
generated | Everything is on the command line | From var_container_storage and var_template_storage. Without them: taken silently if only one storage fits, otherwise the dialog opens. |
default | Same, on a host with a single storage | As generated, but first default.vars is checked: if it lacks either storage line and several storages fit, the dialog opens, even with both variables set, and the answer is saved there. Later runs do not load the saved lines, so without the two variables the dialog opens again. |
mydefaults | Host-wide settings live in default.vars | Read from default.vars. If the file does not exist yet it is created and the storages are asked once. |
appdefaults | A defaults/<app>.vars exists for the app | Read from the app's file; asked once and saved there if missing. |
mode=advanced always opens the wizard. Without mode the menu opens, and a
run that only sets var_* waits there.
The files and their precedence are explained in Defaults and install modes.
What still prompts, and the answer
| Prompt | Appears when | Answer in advance |
|---|---|---|
Choose the container OS (10 second timeout, first entry wins) | The script supports two operating systems, e.g. ct/adguard.sh, ct/valkey.sh, ct/docker.sh | var_os=debian or var_os=alpine |
DIAGNOSTICS: Send Diagnostics of LXC Installation? | The file diagnostics does not exist yet, i.e. the first script ever run on the host | Create the file, see below |
Storage Pools | More than one storage holds the content and none is preselected | var_container_storage and var_template_storage, per the table above |
ARM64 SUPPORT message box | Every run on an arm64 Proxmox VE host | None in the code; it needs Enter |
Continue anyway? (y/N) on arm64 | The script does not declare var_arm64=yes | var_arm64=yes. Without a terminal the run exits 106 instead of asking. |
Raise the kernel key limits now? | The host's key quota for unprivileged containers is nearly used | Under var_unattended=yes, mode=default, mode=mydefaults or mode=appdefaults the default, yes, is applied at once |
OS VERSION NOT AVAILABLE | The requested release is not in the host's template catalog | PHS_SILENT=1, or no terminal on stdin: the nearest release is used and a warning printed |
STORAGE ALMOST FULL | The storage is 95% full or more | In an unattended run it is not asked: the install continues below 98% and exits 214 from 98% |
| GPU selection (60 second timeout, default all) | var_gpu=yes and the host has more than one GPU type | Wait for the timeout, or var_gpu=no |
| Third-party installer confirmation | The script or its installer calls confirm_external_installer, e.g. install/casaos-install.sh | var_external_installer=yes |
| The app's own questions | The install script asks with a plain read | The app's own var_*, see below |
Recovery menu after a failed install (default: 1, auto-remove in 60s) | The install inside the container failed | None; after 60 seconds the container is removed |
A container ID that is already taken does not prompt: the next free ID is used and a warning names it.
Diagnostics
diagnostics_check asks once and stores the answer. To decide beforehand,
write the file the dialog would write:
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=no" >/usr/local/community-scripts/diagnosticsUse DIAGNOSTICS=yes to send them. What is sent is described under
Telemetry.
var_unattended
is_unattended decides whether helpers such as prompt_confirm ask or apply
their default. It returns true for mode=default and mode=appdefaults, when
PHS_SILENT=1, when var_unattended is yes, true or 1, or when stdin
is not a terminal. mode=generated is not in that list, and mode=mydefaults
stops counting once the container is being built, so add var_unattended=yes
to both. Put it on the command line, as in the examples, so that it is in the
environment the installer inside the container inherits.
It does not answer whiptail dialogs (storage, diagnostics) or a script's plain
read; those have their own rows in the table.
The app's own questions
Some installers ask about options of the app. They read a variable first and
ask only if it is empty. ct/docker.sh exports three of them:
mode=generated var_unattended=yes var_os=debian \
var_docker_socket=n var_docker_compose=y \
var_container_storage=local-lvm var_template_storage=local \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/docker.sh)"The variables a script has are listed on its page and in the generator. A script whose installer asks without such a variable cannot run unattended; report it as a bug.
An installer that hands over to an upstream script needs consent:
mode=generated var_unattended=yes var_external_installer=yes \
var_container_storage=local-lvm var_template_storage=local \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/casaos.sh)"A batch
for app in adguard valkey debian; do
mode=generated var_unattended=yes var_os=debian \
var_container_storage=local-lvm var_template_storage=local \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" ||
echo "${app} failed with exit code $?"
doneEach run takes the next free container ID and the app's name as hostname, so
the loop needs no per-app values. Do not set var_ctid, var_hostname,
var_mac or a single static var_net for the whole loop. To hand out static
addresses, give var_net a range; each run takes the first address in it that
does not answer a ping:
for app in adguard valkey debian; do
mode=generated var_unattended=yes var_os=debian \
var_net=192.168.1.100/24-192.168.1.120/24 var_gateway=192.168.1.1 \
var_container_storage=local-lvm var_template_storage=local \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" ||
echo "${app} failed with exit code $?"
doneThe same with files
Put what every container shares into default.vars once:
mkdir -p /usr/local/community-scripts
cat <<'EOF' >>/usr/local/community-scripts/default.vars
var_container_storage=local-lvm
var_template_storage=local
var_ssh=yes
var_timezone=Europe/Berlin
EOFThen each run needs only the mode:
mode=mydefaults var_unattended=yes var_os=debian \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/valkey.sh)"A var_* on that command line still wins over the file.
Secrets
var_pw, var_github_token and an app's passwords on the command line end up
in the shell history. Keep them in a root-only file and export them for the
run:
set -a
. /root/.deploy.env # contains lines such as var_pw=...
set +a
mode=generated var_unattended=yes \
var_container_storage=local-lvm var_template_storage=local \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)"default.vars and app files are plain text as well; see the file modes in
Defaults and install modes.
Virtual machines
VM scripts load none of the above. With VM_UNATTENDED=1 they take the
script's defaults, overridden by VM_* variables from the environment; the
generator emits those for a VM script. See
Virtual machines.
Check before you automate
Run the command once by hand on a disposable host. A prompt that appears there will also appear in the batch. If a run fails, start from the exit code it prints: Exit codes and Troubleshooting.