Interactive and Unattended Node Setup
This guide covers Incus, LXD, Docker, Podman and Containerd installers, interactive batch creation entry points and uninstallers, as well as the ECS evaluator's environment preparation entry point. A successful installation still requires storage, networking and container connectivity checks. Supported host operating systems and available container images are separate lists.
Unified variables for live acceptance
Disposable-node acceptance uses the OCV_ prefix consistently across panel and script drivers: OCV_LIVE_HOST, OCV_LIVE_PASSWORD, OCV_LIVE_RUNTIME=incus|lxd, OCV_LIVE_MODE=interactive|noninteractive, and OCV_LIVE_NETWORK_TYPE=nat_ipv4|ipv6_only|nat_ipv4_ipv6. With OCV_LIVE_IPV6=yes, guest egress, an independent public HTTP probe, and an actual SSH login must each pass; IPv4, ULA, host, or loopback results do not count. Strict IPv6 WebSSH checks use OCV_WEBSSH_SOURCE_IPV6, the public source address actually used by the service's SSH connection, never the web endpoint's IPv4. Supply passwords only through secure input or environment variables, never in scripts or reports.
Common Mode Flag
Use the lowercase noninteractive variable. Prefer setting it for one command so later interactive operations are unaffected:
noninteractive=true bash ./installer.sh </dev/nullAccepted true values are true, yes, y and 1, case insensitive. The legacy NONINTERACTIVE flag remains supported; Incus also supports INCUS_NONINTERACTIVE. Precedence is the first nonempty value of noninteractive, NONINTERACTIVE, then the Incus alias. An explicit noninteractive=false overrides a true legacy flag. The normalized flag is exported to child scripts.
For interactive installation, use a terminal and clear inherited automation settings:
unset noninteractive NONINTERACTIVE INCUS_NONINTERACTIVE
unset INCUS_STORAGE_PATH INCUS_DISK_SIZE DISK_NUMS STORAGE_PATH
bash ./installer.sh2
3
Installers still use noninteractive package-manager options to avoid unrelated package dialogs; their own storage and other configuration prompts remain interactive. Do not use yes | bash ... as a substitute for unattended mode, or count a command with closed stdin as an interactive test.
LXD preserves its existing shortcut: when no mode is supplied, DISK_NUMS or STORAGE_PATH selects unattended resource configuration. Use noninteractive=false to explicitly select interactive mode.
The Go ECS evaluator goecs.sh uses the same switch. It normalizes noninteractive to true or false at startup and exports the value to child processes; an explicit lowercase false overrides NONINTERACTIVE=true. This controls optional prompts in the evaluator and does not replace package-manager non-interactive options.
Entry Points and Common Options
Download the script using the relevant installation page first, then run the entry point below. Verify the download and working directory.
| Runtime | Installer | Common optional variables | Uninstaller |
|---|---|---|---|
| Incus | scripts/incus_install.sh | INCUS_STORAGE_PATH, INCUS_DISK_SIZE, INCUS_STORAGE_BACKEND | scripts/uninstall_incus.sh |
| LXD | scripts/lxdinstall.sh | STORAGE_PATH, DISK_NUMS | scripts/lxduninstall.sh |
| Docker | scripts/dockerinstall.sh | NEED_DISK_LIMIT, DOCKER_INSTALL_PATH, DOCKER_POOL_SIZE, DOCKER_LOOP_FILE | dockeruninstall.sh |
| Podman | podmaninstall.sh | NEED_DISK_LIMIT, PODMAN_INSTALL_PATH, PODMAN_POOL_SIZE, PODMAN_LOOP_FILE | podmanuninstall.sh |
| Containerd | containerdinstall.sh | NEED_DISK_LIMIT, CONTAINERD_INSTALL_PATH, CONTAINERD_POOL_SIZE, CONTAINERD_LOOP_FILE | containerduninstall.sh |
For example, after downloading the Incus installer:
noninteractive=true INCUS_DISK_SIZE=10 bash ./incus_install.sh </dev/nullOmitted options use each installer's defaults. Storage paths, backends and disk quotas remain subject to host capabilities. Optional IPv6 discovery may fall back to IPv4; that does not mean dedicated IPv6 is ready. Incus unattended installation does not automatically reboot. Follow the separate reboot messages for interactive Incus installation and other runtimes.
Uninstallers remove containers, images and runtime configuration. Run an unattended uninstaller only when the entire runtime can be removed; noninteractive=true skips its confirmation. Dedicated legacy options such as LXD's REMOVE_STORAGE, Incus's INCUS_FORCE_UNINSTALL and Podman's FORCE_UNINSTALL retain their script-specific meaning and are not common mode flags.
Connecting the Panel
SSH mode requires the panel to reach the node's SSH endpoint. Agent mode requires running the Agent installation command generated by the panel, and the Agent must reach the panel's WebSocket endpoint. Installing a virtualization runtime alone does not establish this reverse connection. Complete runtime initialization before creating containers in either mode.
Web operations use the signed-in account. Automation uses an API token in the Authorization: Bearer <token> header. This token does not replace node SSH credentials or the Agent secret. Creation and deletion normally queue tasks: wait for their terminal state, then verify the runtime instance and networking.
Panel Firewall Persistence
For the panel's shared firewall manager, owned nft tables are saved under /etc/nftables.d/. The main configuration and compatibility snapshots follow the node's distribution:
| Distribution family | nft main configuration | IPv4 snapshot | IPv6 snapshot |
|---|---|---|---|
| Debian / Ubuntu | /etc/nftables.conf | /etc/iptables/rules.v4 | /etc/iptables/rules.v6 |
| RHEL / CentOS / Fedora and derivatives | /etc/sysconfig/nftables.conf | /etc/sysconfig/iptables | /etc/sysconfig/ip6tables |
| Arch / Manjaro | /etc/nftables.conf | /etc/iptables/iptables.rules | /etc/iptables/ip6tables.rules |
| Alpine | /etc/nftables.nft | /etc/iptables/rules-save | /etc/iptables/rules6-save |
The panel reads all snapshots before replacing individual files atomically, preserving existing permissions, ownership and symbolic links; new snapshots use mode 600. It does not perform a second global save or start/reload the whole firewall. Missing optional restoration services remain warnings without undoing live rules; install the appropriate service and test an actual reboot. Unknown distributions retain the historical default paths. Custom service overrides or rules_file settings require manual verification. A saved file does not establish successful boot restoration.
Initialization state belongs only to the current firewall manager. Reinitialization checks the required nft chains and recreates missing tables or chains. Failures are not cached as success, and an old object's memory address is not evidence that a new connection is ready. Administrators or external services can still change rules after the check; verify actual port connectivity.
Network Modes
| Goal | Required checks |
|---|---|
| NAT IPv4 only | Container IPv4 egress and host port mapping; an extra public IPv6 address must not be counted as IPv4-only |
| Dedicated IPv6 only | A routable public container IPv6 address with direct service access, without depending on IPv4 NAT |
| NAT IPv4 + IPv6 | Incus/LXD expose the guest ULA on matching host IPv4/IPv6 mapped ports; backends that require routed addressing must prove the guest's own public IPv6 |
A script's “attach independent IPv6” option generally adds IPv6 to an existing IPv4 network; it does not mean IPv6-only. Interpret the panel's networkType together with the provider type: Incus/LXD nat_ipv4_ipv6 maps the host public IPv6 to the guest ULA and does not read a /64 pool; backends requiring routed addressing use their pool/tunnel configuration. An IPv6 NAT address is not dedicated IPv6.
Dedicated IPv6 requires an assignable prefix, upstream routing or NDP, host forwarding and working container routes. After installation, check addresses, default routes and DNS inside the container, then test SSH externally over each address family. Delete and recreate a container using the same host port to detect stale mappings.
For Agent traffic monitoring, generate traffic and verify counter growth, restart recovery and deletion cleanup. An online Agent alone does not prove traffic accounting works.
Checking and Restoring IPv6 Routes
A prefix assigned in the provider portal does not mean a reinstalled OS has configured its address and default route. Inspect the actual state first:
ip -6 -o addr show scope global
ip -6 route show default
curl --noproxy '*' -6 -fsS --connect-timeout 10 --max-time 25 https://ipv6.ip.sb2
3
If configuration is missing, establish the assigned prefix and gateway first. Test an evidence-backed candidate temporarily; do not make it a default for every node. Check duplicate address detection (DAD), then verify the public source returned by the request. On failure, remove only this attempt's added address and route. Persist a successful configuration through the OS's actual network manager and test an actual reboot. Preserve IPv4, other interfaces and existing routes; do not globally flush addresses or routes.
A link-local neighbor answering ping is not necessarily a usable gateway. A host /128 does not establish ownership of a /64; only confirmed delegated prefixes may be used as container pools. Host egress must be followed by separate container egress, independent external SSH authentication and public HTTP response checks. Ping, a TCP 22 handshake or a host service alone does not prove container connectivity.
The Incus/LXD panel's optional IPv6 keepalive uses the dedicated /etc/cron.d/oneclickvirt-ipv6-keepalive file without replacing root's existing crontab. Identical jobs are reused; custom files are preserved with a warning. Missing optional cron.d, crontab or flock support does not roll back an otherwise created container. Keepalive requests cannot repair an incorrect upstream gateway and do not replace connectivity acceptance.
