Cube Sandbox One-Click
September 24, 2026 · View on GitHub
This directory is used to build and deliver the single-machine one-click release package for cube-sandbox.
Directory Overview
build-release-bundle-builder.sh: Recommended entry point. Compiles the components needed by one-click inside a builder image, then continues the release package assembly on the host machine.build-vm-assets.sh: Buildscontainerd-shim-cube-rs,cube-runtime, guest image (withcube-initas/sbin/init), and independentcube-agent.ext4; collects the guest kernel.build-guest-image.sh: Builds guest OS image with lightweightcube-initonly (no baked-in agent).build-agent-ext4.sh: Builds independentcube-agent/cube-agent.ext4(+version) for virtio-pmem1.build-release-bundle.sh: Low-level packaging entry point. Consumes either the source tree orONE_CLICK_*_BINpre-built artifacts, assemblessandbox-package, and produces the final release package.config-cube.toml: Default one-click runtime configuration template.support/:docker composetemplates for MySQL/Redis/MinIO, installed to/usr/local/services/cubetoolbox/support/on the target machine;support/bin/mkcertis the bundled mkcert binary.cubeproxy/: Compose template,global.conftemplate, and CoreDNS template forcube proxy.webui/: Nginx runtime files for the dashboard, installed to/usr/local/services/cubetoolbox/webui/on the target machine.install.sh: Entry point for installing and starting the control node on the target machine (defaults to all-in-one mode).install-compute.sh: Entry point for installing a compute node on the target machine.down.sh: Stops the services and dependencies installed by one-click.smoke.sh: Runs basic health checks.env.example: Target-machine environment variable template.build.env.example: Build-machine environment variable template for assembling the release bundle.lib/common.sh: Common shell utility functions.scripts/one-click/: Validation and maintenance helpers used by the systemd-managed deployment after installation.terraform/tencentcloud/: Terraform deployer for a clustered CubeSandbox on Tencent Cloud (TKE control plane + CVM compute nodes).create.shis the entry point;destroy.shtears everything down. These files are shipped both at the release-bundle top level and insidesandbox-package(see "Tencent Cloud Cluster Deployment").
Root Makefile Targets (agent-independent pmem)
From the repository root, these targets build via the unified builder image (make builder-image first if needed):
make cube-init # guest PID1 binary → _output/bin/cube-init (alias: guest-init)
make agent-ext4 # independent plane file → _output/cube-agent/{cube-agent.ext4,version}
# (alias: cube-agent-ext4)
make pmem-assets # cube-init + agent-ext4
make help # list all root targets
For the full runtime layout (shim + guest image + agent.ext4 + kernel), use ./build-vm-assets.sh or the release-bundle entry points below.
Supported Operating Systems
- Build / deployment host: Linux is recommended. macOS is supported for the Tencent Cloud Terraform deployer (
terraform/tencentcloud/create.shanddestroy.sh), including the default macOS Bash 3.2 environment. - Windows: native
cmd.exe/ PowerShell execution is not supported. Use WSL2 (Ubuntu or another Linux distribution) when running the shell scripts from Windows. - Target machines: the one-click runtime expects Linux with systemd and Docker/containerd support. The Tencent Cloud Terraform deployer creates Linux CVMs/TKE resources and configures them through SSH.
Build Inputs
The required fixed kernel artifact is the ordinary guest kernel vmlinux. A PVM guest kernel can also be packaged as vmlinux-pvm:
vmlinuxvmlinux-pvm(optional)
By default they are placed under assets/kernel-artifacts/, but can be overridden via environment variables:
export ONE_CLICK_CUBE_KERNEL_VMLINUX=/abs/path/to/vmlinux
export ONE_CLICK_CUBE_KERNEL_PVM_VMLINUX=/abs/path/to/vmlinux-pvm
Kernel multi-version inventory (content-addressed):
- On install,
vmlinux-bm/vmlinux-pvmare each inventoried undercomponent_versions/cube-kernel-scf/sha256-<12 hex>/ - Each inventory dir contains:
vmlinux-bm|pvm,vmlinuxsymlink,variant(bm/pvm), andversion(sha256:<64 hex>for shim) KERNEL_TAG/PVM_KERNEL_TAGare not inventory directory names;release-manifestkernel.version/pvm_versionalso record content short hashes- Ensure maps the digest from Master identity onto that short key
The installed runtime still uses cube-kernel-scf/vmlinux as the active guest kernel path. The package stores vmlinux-bm and keeps vmlinux as a symlink: by default it points to vmlinux-bm; if the target machine sets CUBE_PVM_ENABLE=1 during installation, the installer points it to vmlinux-pvm. CUBE_PVM_ENABLE is an installer toggle: on upgrades, CUBE_PVM_ENABLE=0|1 ./install.sh or the key appearing in the bundle .env always wins (even for 0, the default), while a full cp env.example .env resets it to 0.
The guest image no longer depends on a local zip file. By default it is generated locally from deploy/guest-image/Dockerfile during the one-click release package build. Common override parameters:
export ONE_CLICK_GUEST_IMAGE_DOCKERFILE=/abs/path/to/cube-sandbox/deploy/guest-image/Dockerfile
# Optional; defaults to the directory containing the Dockerfile
export ONE_CLICK_GUEST_IMAGE_CONTEXT_DIR=/abs/path/to/cube-sandbox/deploy/guest-image
# Optional; defaults to cube-sandbox-guest-image:one-click
export ONE_CLICK_GUEST_IMAGE_REF=cube-sandbox-guest-image:one-click
# Optional; defaults to the current repository revision
export ONE_CLICK_GUEST_IMAGE_VERSION=custom-guest-image-version
# Optional; reuse a prebuilt cube-guest-image-*.tar.gz (same layout as the
# Release / docker asset). When set, local docker/mkfs rebuild is skipped.
export ONE_CLICK_GUEST_IMAGE_TAR=/abs/path/to/cube-guest-image-amd64.tar.gz
Building the Release Package
It is recommended to copy the build environment template first:
cd deploy/one-click
cp build.env.example build.env
Run the following from the repository root on the host machine (recommended):
./deploy/one-click/build-release-bundle-builder.sh
To embed a default envd binary into the packaged cubemastercli, prepare the binary on the build host and pass ENVD_LOCAL_PATH to the recommended builder entry point:
ENVD_LOCAL_PATH=/abs/path/to/envd \
./deploy/one-click/build-release-bundle-builder.sh
When this variable is set, the host wrapper copies the file into deploy/one-click/.work/envd; the builder container then builds cubemastercli with that file embedded. If ENVD_LOCAL_PATH is omitted, the packaged cubemastercli does not include a default envd, and template builds that opt in to envd injection must pass --envd-path at runtime.
This entry point will:
- Compile
cubemaster,cubemastercli,templatecenter,cubelet,cubecli,cube-api,cube-agent,containerd-shim-cube-rs, andcube-runtimeinside a container using the root-level builder image. The network runtime is embedded incubeletand no standalone network runtime binary is built. - Run
go mod downloadforCubeMaster,CubeTemplateCenter, andCubeletinside the builder. The first build will fetch Go modules online; subsequent builds reuse the module cache under the builder's HOME directory. - Place the pre-built artifacts in
deploy/one-click/.work/prebuilt/. - Return to the host machine and call
build-release-bundle.shto build the WebUI static assets, continue with guest image generation, and finish final packaging.
If the build machine already has a complete toolchain, or you want to specify ONE_CLICK_*_BIN manually, you can invoke the low-level entry point directly:
./deploy/one-click/build-release-bundle.sh
Regardless of which entry point is used, CubeMaster / Cubelet no longer depend on the vendor/ directory in the repository; dependencies are resolved at build time via Go modules.
The WebUI build runs on the build machine during final packaging and requires npm. The target machine does not build a WebUI image; it mounts the packaged webui/dist directory into a standard nginx container. To reuse an already built dashboard, set:
export ONE_CLICK_WEB_DIST_DIR=/abs/path/to/web/dist
Go Modules Dependency Download
go mod downloadis executed the first timeCubeMasterandCubeletare built.- The build machine must be able to reach the relevant module sources. If you are behind a private network, configure
GOPROXY,GOPRIVATE, and private repository credentials in advance. - The recommended entry point persists the builder HOME to a host-side cache directory, so subsequent builds on the same machine typically do not require a full re-download.
cubelogis still referenced as a local module via../pkgs/CubeLogand is not downloaded from a remote source.
On success, the following file will be generated:
deploy/one-click/dist/cube-sandbox-one-click-<version>.tar.gz
The release package contains:
sandbox-package.tar.gzrelease-manifest.jsonCubeAPI/bin/cube-apicontainerd-shim-cube-rs,cube-runtime- Locally built
cube-image/cube-guest-image-cpu.img(withcube-initas/sbin/init) - Independent
cube-agent/cube-agent.ext4(+cube-agent/version) cubeproxy/directory and its build contextsupport/directory and its compose templateswebui/directory, its compose template, nginx configuration, and builtweb/distassetscube-kernel-scf.zippackaged on the fly from the ordinary/PVM guest kernel artifactsinstall.sh/install-compute.sh/down.sh/smoke.shready to run on the target machine
During installation, the top-level release-manifest.json is copied to:
/usr/local/services/cubetoolbox/release-manifest.json
When VERSION.txt declares manifest=release-manifest.json, install.sh
validates that the manifest is present and parseable before it starts replacing
the existing installation.
Configuration Mapping
One-click does not create an extra global configs/ layer on the target machine; instead, files are placed directly into each component's native configuration paths:
configs/single-node/cubemaster.yaml→CubeMaster/conf.yamlcubelet_conf.default_timeout_insec: cluster default sandbox idle TTL when the client omitstimeout; unset or<= 0means no cluster-wide idle timeout (shipped default-1). See lifecycle — Operational Notes.
Cubelet/config/→Cubelet/config/Cubelet/dynamicconf/→Cubelet/dynamicconf/CUBE_L7_MARK_{HTTP,HTTPS,MASK}(env) →/etc/cubeegress/l7-marks.conf— the L7 egress skb->mark values shared by Cubelet's embedded network runtime eBPF dataplane (which stampsskb->mark) and thecube-proxy-iptables-initTPROXY rules (which match it). Both read the same file, and both validate the values (HTTP != HTTPS, bits confined to the mask). Seeenv.examplefor the shipped defaults and how to override them.CubeAPI/bin/cube-api→/usr/local/services/cubetoolbox/CubeAPI/bin/cube-apisupport/→/usr/local/services/cubetoolbox/support/cubeproxy/→/usr/local/services/cubetoolbox/cubeproxy/webui/→/usr/local/services/cubetoolbox/webui/
Cubelet uses the existing dynamicconf/conf.yaml from the repository as-is, and its embedded network runtime reads the network plugin configuration from Cubelet/config/config.toml directly. cube-api and cubeops read environment variables from .one-click.env on startup. CubeOps warehouse knobs are CUBE_OPS_WAREHOUSE_* (timeouts, GitHub/CNB allow-lists and tokens) plus CUBE_OPS_S3_*; by default the warehouse reuses the volume MinIO/S3 connection with a dedicated cube-ops bucket, so no extra S3 setup is needed. There is no CubeOps YAML file in the one-click layout. cube-api listens on 0.0.0.0:3000 by default and forwards to the local cubemaster. MySQL/Redis are always deployed to /usr/local/services/cubetoolbox/support and run in Docker containers managed by dedicated systemd services on the target machine. cube proxy is always deployed to /usr/local/services/cubetoolbox/cubeproxy, built locally from the bundled build context, and managed by systemd. WebUI is deployed to /usr/local/services/cubetoolbox/webui, listens on 12088 by default, serves the packaged webui/dist directory through a standard nginx container, and proxies /cubeapi to CubeAPI through Docker host-gateway under systemd management.
Target Machine Installation
After copying cube-sandbox-one-click-<version>.tar.gz to the target machine:
tar -xzf cube-sandbox-one-click-<version>.tar.gz
cd cube-sandbox-one-click-<version>
cp env.example .env
sudo ./install.sh
The one-click installation path is fixed at /usr/local/services/cubetoolbox.
New one-click installations are managed by systemd only:
- control node:
cube-sandbox-control.target - compute node:
cube-sandbox-compute.target
The installer copies the unit files into /etc/systemd/system/ and runs enable --now for the selected role automatically. Legacy shell up/down scripts are kept only as a short-term upgrade bridge for older pre-systemd installs and are not part of the runtime interface for new installations.
Common commands:
sudo ./smoke.sh
sudo ./down.sh
After a control-node installation, open the dashboard at:
http://<target-host>:12088
Before installation, you can explicitly set the current node's internal IP in .env. If not set, install.sh will attempt to auto-detect the IPv4 address of eth0:
# CUBE_SANDBOX_NODE_IP=10.0.0.10
If CUBE_SANDBOX_NODE_IP is explicitly set, the installation script will use that value directly; otherwise, the auto-detected node IP is persisted in the runtime environment and used to render cube proxy / DNS addresses.
CubeS3lvol stop/upgrade semantics
CubeS3lvol (s3lvol) is a Wants= member of the cube-sandbox-* role target. It
is deliberately not PartOf= it (see the unit): stopping a role target does
not stop s3lvol, because a stop here is a full teardown and an upgrade must not
inherit one.
- Stopping (
down.sh):down.shstops the role target and then stops s3lvol explicitly. That goes throughcube-s3lvol-stop.sh's conditional unload — when the target process is alive it runs the fullrcow_stop.sh(disconnect initiators -> flush/unload lvstore -> stop the target); when the target has already crashed it only clears target-side residue and never disconnects the NVMf initiators.down.shonly stops services, it does not delete any data (/data/cubelet/rcow/wal_bdev.imgand the bstore metadata are kept); the next start recovers via attach/replay. - Upgrading (
install.sh --mode=upgrade): nothing to configure and nothing to remember between releases. The component is installed into a versioned directory (CubeS3lvol-<version>/) with the bare nameCubeS3lvolas a symlink to it; the version just replaced is kept beside it and older ones are pruned.- The first upgrade of an install from before this, or of a target too old
to describe its own on-disk formats, or one whose scripts predate the rename
to
rcow_upgrade.sh: the target is stopped and started — an interruption, for that one upgrade only.install.shsays why. - Every upgrade after that is done in place: the target is flushed and
checkpointed online, killed outright, and the replacement rebuilds the same
NQN/(subsys, nsid)/UUID grid, so the host reconnects to the same
/dev/nvmeXnYand a sandbox's I/O only pauses (about 40s). The initiator is never disconnected and the lvstore never unloaded. This runscube-s3lvol-hot-upgrade.shbefore the rest of the install stops anything, because an online flush needs both the running target and the S3 endpoint. - A swap that does not come back with the layout intact is rolled back to
the previous version;
install.shstill finishes the rest and then exits non-zero. The node is complete but on the old s3lvol. wal_bdev.imgis never overwritten (created only on first install; its size fixes the journal/WAL layout), and theRCOW_*settings in.one-click.envare merged and kept across the upgrade.
- The first upgrade of an install from before this, or of a target too old
to describe its own on-disk formats, or one whose scripts predate the rename
to
- If a stop is refused — a live target the stop script will not touch — the
unit ends stopped while the target keeps running and serving. Nothing was
disconnected and the lvstore is still loaded, and the marker is kept, so the
next upgrade picks the same target up and stops it in place: with no unit
running there is none to be asked, and the upgrade drives that stop itself.
rcow_stop.shby hand is the planned stop instead — it disconnects the initiator and unloads the lvstore — so it is only for when that outage is what is wanted. The one refusal that asks for operator work is a marker naming a target this host cannot confirm: the stop script says so, and that marker has to be resolved before anything will start. - Enable/disable: preferred
ONE_CLICK_ENABLE_S3LVOL=0|1 ./install.sh(honored on upgrade as well). Or put only that key in the bundle.envand re-runinstall.sh. Do notcp env.example .envas a full copy before upgrade — that resets this switch to0. Hand-editing.one-click.envis no longer required.systemctl enable/disable cube-sandbox-s3lvol.servicestill works as a direct systemd toggle.CUBE_PVM_ENABLEfollows the same rule: appearing in.envor the process environment always counts as an explicit choice (so a fullcp env.example .envbefore an upgrade also resets it to0). - S3 backend: when enabled,
install.shwrites/data/cubelet/s3.cfgfromCUBE_S3_*(bundled MinIO fill, or the operator's external store). s3lvol uses its own bucket (CUBE_S3LVOL_BUCKET, defaultcube-s3lvol) so it never shares prefixes with the volume plugin'scube-volumes. The supervisor idempotently creates that bucket with a stdlib SigV4 tool — no awscli. A hand-writtens3.cfgwithout the one-click sentinel is left alone. There is no fallback from the old/data/cubelet/cos.cfg; rename it and switch to the new field names.
Digital Assistant Environment Variables
The Digital Assistant (AgentHub) uses MySQL through CubeOps to persist assistant instances, snapshots, templates, and operation history. In one-click deployments, DATABASE_URL is derived from CUBE_SANDBOX_MYSQL_HOST, CUBE_SANDBOX_MYSQL_PORT, CUBE_SANDBOX_MYSQL_USER, CUBE_SANDBOX_MYSQL_PASSWORD, and CUBE_SANDBOX_MYSQL_DB when it is not set explicitly — written urlencoded into .one-click.env, with the start scripts' fallback exporting the split fields for CubeOps to map directly:
# Optional; derived from CUBE_SANDBOX_MYSQL_* when omitted.
DATABASE_URL=mysql://cube:cube_pass@127.0.0.1:3306/cube_mvp
Before creating or reconfiguring OpenClaw-based digital assistants, configure the LLM API key (and provider, base URL, model) on the AgentHub settings page in the WebUI.
Compute Node Installation
If the first machine has already been deployed as a combined control + compute node, the same release package can be reused on a second machine as a compute-only node:
tar -xzf cube-sandbox-one-click-<version>.tar.gz
cd cube-sandbox-one-click-<version>
cp env.example .env
Set at minimum the following in .env:
ONE_CLICK_DEPLOY_ROLE=compute
ONE_CLICK_CONTROL_PLANE_IP=10.0.0.11
If the control node runs bundled MinIO (or any S3 backend), copy CUBE_S3_* from
that node's runtime env file so the volume plugin reaches the same store:
# Run on the control node; paste the output into the compute node's .env
grep '^CUBE_S3_' /usr/local/services/cubetoolbox/.one-click.env
Compute nodes never deploy MinIO; they only consume CUBE_S3_*. These values are
optional but strongly recommended: if missing, the installer prints a prominent
warning and continues; the S3 volume plugin stays disabled until configured. Allow
TCP 9000 from compute to the control node when using bundled MinIO.
If you need to explicitly specify the compute node IP, or if the default NIC on the target machine is not eth0, also set:
CUBE_SANDBOX_NODE_IP=10.0.0.12
Then run:
sudo ./install-compute.sh
In compute node mode, the installer will:
- Install
Cubeletwith the embedded network runtime,cube-shim,cube-image,cube-kernel-scf,cube-egress, the required scripts, anddocker. - Start
cubelet, and bring upcube-egressviacube-sandbox-compute.target(the transparent egress MITM proxy, run as a docker container, which enforces per-sandbox egress policy). - Before
cube-egressstarts, pull the MITM root CA (cert + key) from the control node's/cube/ca/<file>endpoint so it matches the CA baked into templates — templates then trust the leaf certs the compute-nodecube-egresssigns. - Point
Cubelet'smeta_server_endpointtoONE_CLICK_CONTROL_PLANE_IP:3010(CubeOps node-agent). - Automatically register the node via the control node's
/internal/v1/node-agentAPI.
Notes:
- All compute nodes must have
Cubeletlistening on the same gRPC port as configured on the control node (default9999). CUBE_SANDBOX_NODE_IPis used both as the one-click configuration value and as theCubeletnode registration IP.- The control node must be able to reach port
9999/tcpon all compute nodes; compute nodes must be able to reach port8089/tcpon the control node (and9000/tcpwhen using bundled MinIO as the S3 volume backend).
MySQL/Redis dependencies are deployed by default to:
/usr/local/services/cubetoolbox/support
During installation, runtime files are prepared in this directory and the following containers are managed individually by systemd:
mysql:8.0redis:7-alpineminio(S3-compatible volume backend; explicit viaCUBE_SANDBOX_MINIO_ENABLED, default on)
Using an external MySQL / PostgreSQL / Redis
To point CubeSandbox at an existing MySQL, PostgreSQL, or Redis server instead
of the bundled local containers, set the following in .env before running
install.sh (see env.example). CUBE_DATABASE_DRIVER mirrors Helm
database.driver: mysql (default) or postgres (always external).
# External MySQL (default driver; any subset of the credential fields may be overridden)
# CUBE_DATABASE_DRIVER=mysql
CUBE_EXTERNAL_MYSQL_HOST=10.0.0.20
CUBE_EXTERNAL_MYSQL_PORT=3306
CUBE_EXTERNAL_MYSQL_USER=cube
CUBE_EXTERNAL_MYSQL_PASSWORD=cube_pass
CUBE_EXTERNAL_MYSQL_DB=cube_mvp
# External PostgreSQL (one-click never ships a local PostgreSQL)
# CUBE_DATABASE_DRIVER=postgres
# CUBE_EXTERNAL_POSTGRES_HOST=10.0.0.20
# CUBE_EXTERNAL_POSTGRES_PORT=5432
# CUBE_EXTERNAL_POSTGRES_USER=cube
# CUBE_EXTERNAL_POSTGRES_PASSWORD=cube_pass
# CUBE_EXTERNAL_POSTGRES_DB=cube_mvp
# External Redis
CUBE_EXTERNAL_REDIS_HOST=10.0.0.21
CUBE_EXTERNAL_REDIS_PORT=6379
CUBE_EXTERNAL_REDIS_PASSWORD=ceuhvu123
When CUBE_EXTERNAL_MYSQL_HOST, CUBE_EXTERNAL_POSTGRES_HOST (with
CUBE_DATABASE_DRIVER=postgres), and/or CUBE_EXTERNAL_REDIS_HOST is set,
install.sh:
- patches
instance_db_config(driver, address, user, password, database) in bothCubeMaster/conf.yamlandCubeTemplateCenter/conf.yaml; - writes
DATABASE_URL(mysql://orpostgresql://) andCUBE_PROXY_REDIS_*to.one-click.envso every service consumes the external endpoint; - masks the corresponding
cube-sandbox-mysql.service/cube-sandbox-redis.serviceso the local container is never started; and - makes
quickcheck.shandup-support.shskip lifecycle management of the now-external dependency. (down-support.shhas no external-dep awareness and still issues adocker compose down, but this is a harmless no-op because the local containers were never started for the external dependency.)
The external database must already grant the configured user access to the target database. CubeMaster and CubeTemplateCenter both open that database and run embedded schema migrations on first start.
Bundled MinIO vs the S3 volume plugin
CUBE_SANDBOX_MINIO_* only deploys the MinIO container. The S3 volume plugin
always reads CUBE_S3_* and writes volume-s3.conf from those values.
The bundled MinIO occupies two host ports (both overridable via environment variables):
| Port | In container | Host mapping | Bind address |
|---|---|---|---|
| S3 API | 9000 | CUBE_SANDBOX_MINIO_API_PORT (default 9000) | CUBE_SANDBOX_MINIO_API_BIND, default CUBE_SANDBOX_NODE_IP (falls back to 127.0.0.1 when no node IP is detected) |
| Web console | 9001 | CUBE_SANDBOX_MINIO_CONSOLE_PORT (default 9001) | always 127.0.0.1, localhost only |
The S3 API is published on the node IP by default so compute-node Cubelets can
reach it directly. To keep S3 local-only, set
CUBE_SANDBOX_MINIO_API_BIND=127.0.0.1 — at the cost of compute nodes losing
access to the bundled MinIO (use an external S3 store instead). The console
port is always bound to 127.0.0.1 and is never exposed.
On a control node with CUBE_SANDBOX_MINIO_ENABLED=1 (default), install.sh
starts MinIO, generates a 24-character random password if you left it empty,
then fills CUBE_S3_* from that MinIO (http://<node-ip>:9000, the MinIO
user/password, path-style s3fs options) and persists both families in
.one-click.env. Do not set CUBE_S3_ENDPOINT yourself while MinIO is
enabled. A later upgrade may reload that filled local endpoint from
.one-click.env; that is expected and allowed. Only a different,
operator-supplied external store requires CUBE_SANDBOX_MINIO_ENABLED=0.
Other MinIO deployment parameters: default user cubeminio
(CUBE_SANDBOX_MINIO_ROOT_USER; the password must be at least 8 characters or
MinIO refuses to start), bucket cube-volumes (CUBE_SANDBOX_MINIO_BUCKET),
data volume cube-sandbox-minio-data (CUBE_SANDBOX_MINIO_VOLUME, mounted at
/data), container name cube-sandbox-minio (CUBE_SANDBOX_MINIO_CONTAINER),
and image CUBE_SANDBOX_MINIO_IMAGE (default selected by MIRROR=cn|int).
MinIO runs under cube-sandbox-minio.service; after startup, readiness is
verified via curl http://<node-ip>:9000/minio/health/live (a 200 response
means it is healthy).
Template artifacts and the CubeOps warehouse can use CUBE_ARTIFACT_STORE_BACKEND=fs
and CUBE_OPS_STORE_BACKEND=fs instead of MinIO (S3 volumes still need MinIO
or external S3).
To use an existing S3-compatible store instead, set
CUBE_SANDBOX_MINIO_ENABLED=0 and CUBE_S3_* before install.sh:
CUBE_SANDBOX_MINIO_ENABLED=0
CUBE_S3_ENDPOINT=https://s3.example.com
CUBE_S3_ACCESS_KEY_ID=...
CUBE_S3_SECRET_ACCESS_KEY=...
CUBE_S3_BUCKET=cube-volumes
# CUBE_S3_REGION=us-east-1
# CUBE_S3_S3FS_EXTRA_OPTS=-ouse_path_request_style
Compute nodes never run MinIO. Copy the filled CUBE_S3_* values from the
control node's runtime env file into the compute .env:
# Run on the control node; paste the output into the compute node's .env
grep '^CUBE_S3_' /usr/local/services/cubetoolbox/.one-click.env
These values are optional but strongly recommended — if missing, the installer warns and continues; the S3 volume plugin stays disabled until configured. Allow TCP 9000 from compute to the control node if using bundled MinIO:
ONE_CLICK_DEPLOY_ROLE=compute
ONE_CLICK_CONTROL_PLANE_IP=10.0.0.11
CUBE_S3_ENDPOINT=http://10.0.0.11:9000
CUBE_S3_ACCESS_KEY_ID=cubeminio
CUBE_S3_SECRET_ACCESS_KEY=<from control .one-click.env>
CUBE_S3_BUCKET=cube-volumes
CUBE_S3_S3FS_EXTRA_OPTS=-ouse_path_request_style
cube proxy and its DNS resolution are mandatory capabilities in one-click. The following two values in .env must remain 1:
CUBE_PROXY_ENABLE=1
CUBE_PROXY_DNS_ENABLE=1
Other common parameters:
CUBE_PROXY_HTTPS_PORT=443
CUBE_PROXY_HTTP_PORT=80
CUBE_PROXY_GRPC_PORT=9090
CUBE_EGRESS_ADMIN_PORT=9091
# Deprecated: CUBE_PROXY_HOST_PORT is ignored; configure CUBE_PROXY_HTTP_PORT instead.
CUBE_PROXY_CERT_DIR=/usr/local/services/cubetoolbox/cubeproxy/certs
CUBE_PROXY_DNS_ANSWER_IP="${CUBE_SANDBOX_NODE_IP}"
WEB_UI_ENABLE=1
WEB_UI_IMAGE=cube-sandbox-image.tencentcloudcr.com/opensource/openresty:1.21.4.1-6-alpine-fat
WEB_UI_HOST_PORT=12088
WEB_UI_UPSTREAM=http://host.docker.internal:3010
CUBE_API_BIND=0.0.0.0:3000
CUBE_API_HEALTH_ADDR=127.0.0.1:3000
CUBE_API_SANDBOX_DOMAIN=cube.app
During installation, the following steps are performed:
- If
mkcertis not already installed on the system, it is copied from the bundledsupport/bin/mkcertto/usr/local/bin/mkcert. Thenmkcert -installis run on the host underCUBE_PROXY_CERT_DIR(default/usr/local/services/cubetoolbox/cubeproxy/certs/) to generatecube.app+3.pemandcube.app+3-key.pem. - Runtime configuration and rendered files are prepared under
/usr/local/services/cubetoolbox/support/,cubeproxy/,coredns/, andwebui/. cubeproxy/global.confis rendered usingCUBE_SANDBOX_NODE_IP.cube-sandbox-*.service|target|timerunit files are installed under/etc/systemd/system/, and both host processes and Docker containers are managed uniformly by systemd.- MySQL, Redis, cube proxy, WebUI, and CoreDNS still run in Docker, but their lifecycle is managed directly by dedicated systemd services instead of relying on runtime
docker compose up -d. - If
resolvectlis available, one-click creates a dedicated dummy link (defaultcube-dns0) with a local address, binds CoreDNS to169.254.254.53on that link by default, and routescube.appthrough the link without affecting the host's default public DNS path. Ifresolvectlis unavailable on the target machine, the installer falls back toNetworkManager + dnsmasq: it still creates the same dummy link, asksdnsmasqto additionally listen on169.254.254.53, takes/etc/resolv.confownership away from NetworkManager (rc-manager=unmanaged) and rewrites it to point at the same non-loopback IP. This keeps the host resolver symmetrical with thesystemd-resolvedpath and avoids the Docker daemon's silent fallback to public DNS (8.8.8.8) that happens when/etc/resolv.confcontains only loopback nameservers — without it, every container on the host (includingdocker build'sapk updatestep) ends up using DNS servers that internal machines cannot reach. On hosts where NetworkManager initializes itsdnsmasqplugin but never spawns the child process (for example bonded interfaces managed viaifcfg+assume), setCUBE_PROXY_DNSMASQ_MODE=standaloneso the DNS scripts launch and owndnsmasqdirectly instead of relying on the NetworkManager plugin; the client-facing resolver layout (dummy link, listen addresses, entry IP) is otherwise identical. - Host processes
cubemaster,cube-api, andcubeletare started through systemd, andquickcheck.shverifies both unit state and service health. - A standard WebUI nginx container is started under
/usr/local/services/cubetoolbox/webui/. It mountswebui/distas read-only static content, publishesWEB_UI_HOST_PORT(12088by default), mapshost.docker.internalto Dockerhost-gateway, and verifies/healththrough the nginx reverse proxy (served by CubeOps).
Stopping one-click will simultaneously stop MySQL/Redis under /usr/local/services/cubetoolbox/support, WebUI, cube proxy / CoreDNS, and the host processes cubemaster / cube-api / cubelet, and will roll back the host DNS routing configuration for cube.app.
After deployment, to point the E2B official SDK to the one-click node, set the following on the client side:
export E2B_API_URL=http://<target-host>:3000
export E2B_API_KEY=e2b_000000
Pre-Installation Preflight Checklist
install.sh / install-compute.sh performs a one-time preflight check early in the startup process to ensure dependencies fail fast rather than partway through.
Compute Role (install-compute.sh)
Required commands:
docker(cube-egress runs as a docker container; the installer installs it automatically — this is a hard prerequisite, so in offline/air-gapped environments where automatic installation isn't possible, install Docker beforehand). Snap Docker is rejected: it cannot read/usr/local/services(sudo snap remove docker, then install docker-ce).tarssbashcurlgrepsedpgrepdate
Conditional commands:
- If
ONE_CLICK_ENABLE_TENCENT_DOCKER_MIRROR=1is enabled and/etc/docker/daemon.jsonalready exists,python3is required. - If the packaged
Cubelet/config/config.tomlenablesstorage_backend = "cubecow", one-click also checks:mkfs.ext4,mount,umount,losetup - If
ONE_CLICK_ENABLE_S3LVOL=1and the package shipsCubeS3lvol/bin/s3lvol_tgt, one-click also checks:nvme(nvme-cli),python3,truncate, remainings3lvol_tgtshared libraries (ldd; OpenSSL is static), and (x86_64)avx2in/proc/cpuinfo. Releases3lvol_tgtis built for Haswell/AVX2, not the packager's native CPU.install.shinstallsnvme-clivia the system package manager whennvmeis missing.python3must be able to runCubeS3lvol/scripts/rpc.py --help(Python 3.8 is enough; the packaged launcher supplies the 3.9argparsebits SPDK's client needs).
Recommended packages to satisfy the cubecow command set:
- Debian / Ubuntu:
e2fsprogs,util-linux - OpenCloudOS / RHEL / CentOS:
e2fsprogs,util-linux
Example install commands:
# Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y e2fsprogs util-linux
# OpenCloudOS / RHEL / CentOS
sudo dnf install -y e2fsprogs util-linux || \
sudo yum install -y e2fsprogs util-linux
Additional packages for ONE_CLICK_ENABLE_S3LVOL=1 (libraries only; nvme-cli is installed by install.sh):
# Debian / Ubuntu
sudo apt-get install -y python3 libaio1 libnuma1 uuid-runtime
# OpenCloudOS / RHEL / CentOS
sudo dnf install -y python3 libaio libnuma libuuid || \
sudo yum install -y python3 libaio libnuma libuuid
Control Role (install.sh, default)
Required commands:
docker(snap Docker is rejected; install docker-ce / docker.io)tarssbashcurlgrepsedpgrepdateipawk
One-of-two commands:
- Certificate preparation:
mkcert(bundled in the release package; auto-installed from the package if not present on the system). - DNS split routing:
resolvectl, or (for the defaultnetworkmanagerdnsmasq fallback)systemctl + NetworkManager. Thestandalonednsmasq mode (CUBE_PROXY_DNSMASQ_MODE=standalone) does not require a loaded/restartableNetworkManager. - If
dnsmasqis missing and either dnsmasq fallback path is taken (networkmanagerorstandalone), one of the following package managers is also required:dnf/yum/apt-get.
Conditional commands:
- If
ONE_CLICK_ENABLE_TENCENT_DOCKER_MIRROR=1is enabled and/etc/docker/daemon.jsonalready exists,python3is required. - If the packaged
Cubelet/config/config.tomlenablesstorage_backend = "cubecow", one-click also checks:mkfs.ext4,mount,umount,losetup - If
ONE_CLICK_ENABLE_S3LVOL=1and the package shipsCubeS3lvol/bin/s3lvol_tgt, one-click also checks:nvme(nvme-cli),python3,truncate, remainings3lvol_tgtshared libraries (ldd; OpenSSL is static), and (x86_64)avx2in/proc/cpuinfo. Releases3lvol_tgtis built for Haswell/AVX2, not the packager's native CPU.install.shinstallsnvme-clivia the system package manager whennvmeis missing.python3must be able to runCubeS3lvol/scripts/rpc.py --help(Python 3.8 is enough; the packaged launcher supplies the 3.9argparsebits SPDK's client needs).
Recommended packages to satisfy the cubecow command set:
- Debian / Ubuntu:
e2fsprogs,util-linux - OpenCloudOS / RHEL / CentOS:
e2fsprogs,util-linux
Example install commands:
# Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y e2fsprogs util-linux
# OpenCloudOS / RHEL / CentOS
sudo dnf install -y e2fsprogs util-linux || \
sudo yum install -y e2fsprogs util-linux
Additional packages for ONE_CLICK_ENABLE_S3LVOL=1 (libraries only; nvme-cli is installed by install.sh):
# Debian / Ubuntu
sudo apt-get install -y python3 libaio1 libnuma1 uuid-runtime
# OpenCloudOS / RHEL / CentOS
sudo dnf install -y python3 libaio libnuma libuuid || \
sudo yum install -y python3 libaio libnuma libuuid
Prerequisites
Security: All core services bind
0.0.0.0by default. Before deploying on a machine reachable from untrusted networks, review the Network Hardening Guide for bind-address configuration, firewall rules, and credential rotation.
- The target machine requires
rootprivileges. - The target machine preferentially uses
systemd-resolved/resolvectlfor split DNS ofcube.app. The current implementation creates a dedicated dummy link (defaultcube-dns0), assigns it a local/32address, binds CoreDNS to169.254.254.53on that link by default, and attaches that address plus~cube.appto the link. If that capability is unavailable, the installation script will fall back toNetworkManager + dnsmasq: the same dummy link is created anddnsmasqis configured (vialisten-address/bind-interfaces) to listen on both127.0.0.1and169.254.254.53./etc/resolv.confis then written by the installer (NetworkManager runs withrc-manager=unmanaged) to point at169.254.254.53, so host applications and Docker containers see the same non-loopback resolver. When NetworkManager loads itsdnsmasqplugin but never spawns the child (for example bonded interfaces managed viaifcfg+assume), setCUBE_PROXY_DNSMASQ_MODE=standalonein.one-click.envso the DNS scripts start and managednsmasqdirectly. - The target machine pulls
mysql:8.0andredis:7-alpinefrom the internet by default. - The
mkcertbinary is bundled in the release package (support/bin/mkcert). Ifmkcertis not pre-installed on the system, it is automatically copied from the package to/usr/local/bin/mkcert— no internet download required. - The S3 volume plugin (
{CubeMaster,Cubelet}/plugin/cube-volume-s3) is a static Go binary with a built-in S3 client, compiled at pack time fromexamples/volume/s3. Control nodes need no S3 command line tool; nodes that mount volumes still needs3fs. Ship a prebuilt binary withONE_CLICK_VOLUME_S3_BIN. - TLS certificates and private keys for
cube proxyare stored on the host underCUBE_PROXY_CERT_DIRand mounted read-only into the container viadocker compose. After updating certificates, simply restartcube-proxyor reload nginx inside the container — no image rebuild required. - The recommended entry point
build-release-bundle-builder.shrequires the host machine to havedocker,make,tar,python3,truncate,ldd,mkfs.ext4, and similar tools. - The recommended entry point only runs component compilation inside the builder; guest image generation and final packaging are still performed on the host machine.
- If invoking the low-level entry point
build-release-bundle.shdirectly, the build machine must also have local toolchains such asgo,cargo, andmakeinstalled, depending on the build mode. - If using the low-level entry point directly or running the recommended entry point for the first time, the build machine must be able to download Go modules from the internet. Configure a usable
GOPROXYin advance for restricted network environments. - If the VM path is enabled, the target machine must still satisfy the runtime permission requirements for the Cubelet embedded network runtime, tap interfaces, routing, etc.
Known Limitations
- If
vmlinuxis missing fromassets/kernel-artifacts/,build-vm-assets.shandbuild-release-bundle.shwill fail immediately.vmlinux-pvmis optional at build time, but installation withCUBE_PVM_ENABLE=1requires it to be present in the package. The installedcube-kernel-scf/vmlinuxpath is an active symlink tovmlinux-bmorvmlinux-pvm. Thecube-kernel-scf.zipin the release package is generated automatically during the packaging phase. - If the
deploy/guest-image/Dockerfilebuild fails, or the build machine'smkfs.ext4does not support the-dflag, guest image generation will fail immediately. cube-snapshot/spec.jsonis not a mandatory artifact in the current first release of one-click. If absent, the related plugin degrades to a warning rather than blocking the basic startup.- The default
NetworkManager + dnsmasqfallback relies on NetworkManager to spawn thednsmasqchild. On hosts where NetworkManager initializes the plugin but never spawns it (for example bonded interfaces managed viaifcfg+assume), setCUBE_PROXY_DNSMASQ_MODE=standaloneso the DNS scripts launch and managednsmasqthemselves. Standalone mode does not require a restartableNetworkManager, but on hosts with no resolver manager at all you must ensure nothing else overwrites/etc/resolv.confafterwards. In this modednsmasqruns as a bare child that systemd does not supervise, so if it later crashes nothing restarts it automatically; recover withsystemctl restart cube-sandbox-dns. - Snap Docker is not supported. Ubuntu Server's installer (Subiquity) offers Docker as a "Featured Snap" during OS setup; users who check that option — or later run
snap install docker— end up with a sandboxed Docker daemon that cannot access paths outside its AppArmor confinement (e.g./usr/local/services). The install scripts detect this automatically and abort with remediation steps. Fix:sudo snap remove docker, then install docker-ce per https://docs.docker.com/engine/install/ubuntu/.
DNS Troubleshooting
- Inspect the current split-DNS state:
resolvectl status - Verify the host stub resolver path:
dig +tcp +timeout=3 docker.cnb.cool @127.0.0.53 - Verify the local CoreDNS path: on the
systemd-resolvedpath and on bothdnsmasqfallback paths (NetworkManager-managed orstandalone), the client entry point is the same dummy-link IP, so rundig +tcp +timeout=3 foo.cube.app @169.254.254.53. CoreDNS itself stays bound to127.0.0.54internally; only thesystemd-resolvedpath talks to CoreDNS directly, while the fallback paths go throughdnsmasq. - Verify the host stub resolver path also routes through the new entry point:
cat /etc/resolv.confshould shownameserver 169.254.254.53on both paths. - Verify the container view:
docker run --rm alpine cat /etc/resolv.confshould also shownameserver 169.254.254.53. If it showsnameserver 8.8.8.8instead, the host's/etc/resolv.confregressed to a loopback nameserver and Docker fell back to its built-in public DNS. - On the
systemd-resolvedpath, the local CoreDNS address should appear only on the dedicated dummy link, not on the default network interface.
Tencent Cloud Cluster Deployment (Terraform)
Full guide (architecture, resource list, TKE / PrivateDNS / CFS preflight, E2B and
*.cube.appDNS, capacity planning, hardening, troubleshooting): Tencent Cloud Cluster Deployment (Terraform).
In addition to the single-machine install.sh, the release bundle ships a
Terraform-based deployer that stands up a clustered CubeSandbox on Tencent
Cloud: a managed TKE control plane running cubemaster / cube-api /
cube-proxy / cube-webui, backed by cloud MySQL + Redis, with one or more CVM
PVM compute nodes. A jumpserver (SSH on port 443) is the build host and bastion
for the otherwise-private VPC.
The default deployment mode (matching env.example / variables.tf) uses public pre-built images (TENCENTCLOUD_USE_TCR=false) with no image build on the jumpserver; cubemaster defaults to single replica with no CFS (TENCENTCLOUD_USE_CFS=false, Pod-local storage). Set TENCENTCLOUD_USE_CFS=true and raise TENCENTCLOUD_CUBEMASTER_REPLICAS to create a CFS share for multi-replica cubemaster at /data/CubeMaster/storage.
cube-proxy runs a single replica by default
(TENCENTCLOUD_CUBE_PROXY_REPLICAS=1). Auto-pause/auto-resume only works
correctly with one replica, because each sidecar sweeper only sees traffic
hitting its own pod. To scale beyond 1 replica the front-end LB must hash on
SandboxID (session affinity); otherwise auto-pause/auto-resume will misfire.
Pre-deployment setup (summary)
Before the first create.sh apply:
- TKE service role authorization (required): log in to the TKE console and complete service authorization. Docs: Service authorization role permissions. Sub-accounts also need TKE preset policy authorization.
- Private DNS (as needed): required for
USE_TCR=trueor E2B SDK access to*.cube.app. Console: DNSPod Private DNS. Docs: Private DNS product overview. - CFS (as needed): only when
TENCENTCLOUD_USE_CFS=trueand cubemaster runs multiple replicas. Console: CFS. Docs: CFS quick start.
TKE workers and PVM compute nodes are separate:
TENCENTCLOUD_TKE_NODE_COUNTcontrols TKE workers (control-plane Pods);TENCENTCLOUD_COMPUTE_NODE_COUNTcontrols PVM compute nodes (Cubelet / sandboxes). Both default to2but serve different roles.
E2B SDK: the cluster deployment does not include single-machine CoreDNS split DNS. Besides
E2B_API_URL, you must configure*.cube.appresolution (Private DNS or equivalent). See the full guide — E2B and the cube.app domain.
The deployer is surfaced at the top level of the extracted bundle, so right after extracting the package you can run it directly:
tar -xzf cube-sandbox-one-click-<version>.tar.gz
cd cube-sandbox-one-click-<version>
export TENCENTCLOUD_SECRET_ID="your-secret-id"
export TENCENTCLOUD_SECRET_KEY="your-secret-key"
./terraform/tencentcloud/create.sh
create.sh runs entirely from the extracted bundle:
- It auto-detects the local bundle (the outer
cube-sandbox-one-click-<version>.tar.gz, or re-packs the extracted directory if the tarball is gone) and uses it as the offline source for component images and compute-node installation. When a local bundle is detected or set viaTENCENTCLOUD_LOCAL_BUNDLE=/path/to.tar.gz, no public download is required; otherwise the jumpserver falls back to an online install (it downloadsonline-install.shand the package), which needs public network access. - It generates an SSH key pair under
terraform/tencentcloud/.ssh/if none exists. - It generates the cube-proxy CLB's TLS certificate (
cube.app/*.cube.app) on the jumpserver using the bundledmkcert(shipped insideassets/package/sandbox-package.tar.gz, i.e.sandbox-package/support/bin/mkcertonce that inner package is extracted; the same flow asscripts/one-click/up-cube-proxy.sh), keeping a copy under/root/cubeproxy-certson the jumpserver and downloading it toterraform/tencentcloud/cubeproxy-certs/for the Secret mount. - Default mode (
TENCENTCLOUD_USE_TCR=false): pull public pre-built images and deploy TKE addons and CVM compute nodes. - TCR mode (
TENCENTCLOUD_USE_TCR=true): create TCR, build and push the four component images on the jumpserver, then deploy TKE addons and compute nodes. Default creates 2 compute nodes; useTENCENTCLOUD_COMPUTE_NODE_COUNTto adjust.
cube-webui's nginx config (webui-nginx.conf) is not maintained separately: it
is derived from the canonical deploy/one-click/webui/nginx.conf (placed there
by the bundle build, or copied by create.sh when run from the source tree).
Requirements on the machine running create.sh: ssh, scp, nc, and network
access to the Tencent Cloud APIs. terraform and jq are auto-installed if
missing — terraform from the HashiCorp release site (needs curl/wget +
unzip), jq from the system package manager or, failing that, a static binary
from GitHub. mkcert/openssl are not required locally — certificates are
produced on the jumpserver.
Common environment overrides (these match the create.sh and variables.tf
defaults):
export TENCENTCLOUD_REGION=ap-guangzhou
export TENCENTCLOUD_AVAILABILITY_ZONE=ap-guangzhou-6
export TENCENTCLOUD_COMPUTE_NODE_COUNT=2 # CVM PVM compute nodes (default 2)
export TENCENTCLOUD_TKE_NODE_COUNT=2 # TKE worker nodes (default 2)
export TENCENTCLOUD_COMPUTE_INSTANCE_TYPE=SA9.MEDIUM8
export TENCENTCLOUD_USE_TCR=false # default: public pre-built images
export TENCENTCLOUD_USE_CFS=false # default: no CFS, cubemaster single replica
export TENCENTCLOUD_CUBE_IMAGE_TAG=v0.7.2
For non-interactive / CI runs, also set these (without a TTY the interactive
menus fall back to defaults, so set them explicitly to stay in control). The
password variables are the exception: a non-interactive run refuses to start
with the built-in, publicly-known demo passwords and requires them to be set —
or set TENCENTCLOUD_ALLOW_INSECURE_DEFAULTS=1 to opt into the insecure
defaults for a throwaway sandbox.
export TENCENTCLOUD_AVAILABILITY_ZONE=ap-guangzhou-6
export TENCENTCLOUD_COMPUTE_INSTANCE_TYPE=SA9.MEDIUM8
export TENCENTCLOUD_LOCAL_BUNDLE=/path/to/cube-sandbox-one-click-<version>.tar.gz # auto-detected when run from inside an extracted bundle
export TENCENTCLOUD_PVM_KERNEL_VMLINUX=/path/to/vmlinux-pvm # only needed if the bundle ships no vmlinux-pvm
export TENCENTCLOUD_MYSQL_PASSWORD=... # required for non-interactive runs (no insecure fallback)
export TENCENTCLOUD_REDIS_PASSWORD=... # required for non-interactive runs
export TENCENTCLOUD_CUBE_PASSWORD=... # required for non-interactive runs
export TENCENTCLOUD_BUILD_IMAGES=0 # TCR mode: reuse already-pushed images
Tear everything down with:
./terraform/tencentcloud/destroy.sh
destroy.sh also needs TENCENTCLOUD_SECRET_ID / TENCENTCLOUD_SECRET_KEY and
reuses the selections saved in terraform/tencentcloud/.env from create.sh. It
runs without prompting — running destroy.sh itself confirms the teardown.
⚠ Avoid unexpected billing: if
destroy.shcannot remove every resource (for example MySQL/Redis stuck in the recycle bin / isolated state, or leftovers Terraform can no longer see), log in to the Tencent Cloud console and delete the remaining resources by hand so you are not billed for orphans: VPC / network, MySQL recycle bin, Redis recycle bin, CFS file systems (ifUSE_CFS=truewas enabled).destroy.shalso prints these same links when a teardown step fails or a recycle-bin cleanup is not confirmed.
The same files are also embedded inside assets/package/sandbox-package.tar.gz
(consumed by the jumpserver-side build_images.sh); the top-level copy simply
makes the deployer reachable without first extracting the inner package.
Environment requirements & how Terraform is used
create.sh drives Terraform from your local machine; you do not need a
pre-installed Terraform:
- Credentials: export
TENCENTCLOUD_SECRET_ID/TENCENTCLOUD_SECRET_KEY(create an API key pair at https://console.cloud.tencent.com/cam/capi). The commonTENCENTCLOUD_*variables are listed interraform/tencentcloud/env.example; advanced toggles are documented in thecreate.shheader comments. - Local tools:
ssh,scp,nc, plus network access to the Tencent Cloud APIs.terraformandjqare auto-installed when missing — into/usr/local/binwhen it is writable (e.g. running as root), otherwise into a local.bin/.terraformis fetched from the HashiCorp release site (needscurl/wget+unzip);jqcomes from the system package manager, falling back to a static binary from GitHub.mkcert/opensslare not needed locally — the cube-proxy certificate is produced on the jumpserver. - Terraform state lives locally under
terraform/tencentcloud/(*.tfstate, gitignored — there is no remote backend). Keep that directory and the generated.env, so a laterdestroy.shor re-run can find and manage the same resources. Do not runcreate.shfrom a throwaway copy and then expect a different copy to clean it up. - Phased, fail-fast apply: resources are created in order — network
(VPC / subnet / NAT) → (when
USE_TCR=true) TCR → CVMs (jump-server + compute) → (TCR mode) image build/push on the jump-server → MySQL / Redis → (whenUSE_CFS=true) CFS shared storage → TKE cluster + Kubernetes addons → health checks → compute-node setup. The Kubernetes provider is only engaged after the TKE API server exists. On teardown, if CFS was created, it is destroyed before its subnet (its NFS mount target is an ENI in that subnet). - Resolved selections are saved to
terraform/tencentcloud/.envand auto-loaded on the next run; explicit environment variables always win.
Retrying after a partial failure
If a stage fails part-way (for example an instance type or availability zone that is sold out in the chosen region/zone, an account quota limit, or a transient API error), you do not have to destroy everything and start over:
- Fix the cause — most often by changing configuration: pick a different
TENCENTCLOUD_AVAILABILITY_ZONE/TENCENTCLOUD_COMPUTE_INSTANCE_TYPE/TENCENTCLOUD_REGION, raise the quota, set a password, etc. — then simply re-run./terraform/tencentcloud/create.sh. - On a re-run,
create.shreloads the saved selections from.env, reconciles state with what already exists in the cloud (refreshing and importing stateful resources rather than recreating them), and continues from where it left off. Existing compute nodes are kept (it never scales down). - Availability genuinely varies by region and availability zone — a type offered in one zone may be unavailable in another. The interactive zone / instance-type menus are queried live for your region, and the final choice is validated at apply time.
- Only run
destroy.shwhen you actually want to tear the deployment down; it is not required between ordinary retries.
Advanced: cube-proxy TLS certificates (bring your own)
cube-proxy terminates TLS for cube.app / *.cube.app, and its bundled nginx
config hard-codes the certificate paths …/certs/cube.app+3.pem and
…/certs/cube.app+3-key.pem:
- By default,
create.sh(prepare_cubeproxy_certs) generates a self-signed pair on the jumpserver with the bundledmkcert(SANs:cube.app,*.cube.app,localhost,127.0.0.1), downloads it toterraform/tencentcloud/cubeproxy-certs/, and Terraform packs every file in that directory into thecubeproxy-certsSecret (a Secret, not a ConfigMap, because it holds the TLS private key), mounted read-only into the cube-proxy pod at/usr/local/openresty/nginx/certs/. - Bring your own certificate: before running
create.sh, drop your PEM cert + key intoterraform/tencentcloud/cubeproxy-certs/, named exactlycube.app+3.pemandcube.app+3-key.pem(the names nginx expects) and covering thecube.appand*.cube.appSANs.create.shreuses existing files instead of generating new ones, so a CA-signed certificate (for example a real domain mapped ontocube.app) is used as-is, with no self-signed warning. - Rotate a certificate: replace the two files and re-run
create.sh; the deploy stage refreshes thecubeproxy-certsSecret and restarts cube-proxy to pick up the new material. The self-signed default trips browsers/clients with an "untrusted CA" warning, so replace it for any non-throwaway use.