Cubicle

Build a template Box

Build a reusable Cubicle template Box instead of a custom image, by installing software over SSH, stopping and snapshotting the Box, and creating new Boxes from the snapshot with the TypeScript SDK, the CLI or the HTTP API, with the Node, capacity, grant and copied-data caveats.

Cubicle has no custom base images: every Box starts from the default or omarchy base image, or from a snapshot of another Box. To start Boxes with your own software already installed, prepare one Box as a template, snapshot it, and create new Boxes from that snapshot. Each new Box starts with a copy of the template's disk.

Know the limits first

A template is an ordinary Box and its snapshot is an ordinary snapshot, so their rules apply. Four of them shape a template:

  • Every new Box goes on the snapshot's Node. The snapshot is stored only on the template Box's Node, so all Boxes created from it share that one Node's capacity. The template Box, even stopped, keeps its own vCPUs, memory and disk reserved there (Resources). When that Node is unavailable or full, create fails with 503 capacity.
  • The snapshot copies every home directory. It holds the whole disk, including the home directory of every person and service account that has had a grant on the template Box. In a new Box, accounts without a grant are locked, but anyone with sudo in it can read their files.
  • Only the creator gets a grant on a new Box. A Box that a service account creates is granted to that service account only. Grant people access separately; see Grant access to a Box.
  • Some files are rewritten in every new Box. Cubicle writes /etc/hostname, /etc/hosts, /etc/resolv.conf, /etc/machine-id, the SSH host key, the SSH server policy and Cubicle's own sudo rules into each new disk, so your edits to those files do not carry over.

A new Box also keeps the template Box's base image and defaults to its size; its disk can grow but not shrink (size rules).

Before you begin

  • The cubicle CLI, signed in as yourself, with SSH set up. See SSH into a Box. You install software as a person: commands that a service account sends cannot rely on sudo (see Commands and operations), so a template is where system packages go.
  • An organization that can create Boxes. Otherwise create fails with 403 box_create_disabled.
  • To create Boxes from the template in code: a service account with a write API key, and either a grant on the template Box or the admin role. See Run CI jobs in Boxes.
  • The curl examples read that key from CB_SDK_API_KEY, and use jq and uuidgen.

The examples use a template Box acme/template, a snapshot template-v1 and a new Box acme/job-1.

Prepare the template Box

  1. Create the template Box. Its disk size is the smallest disk a Box created from it can have, so choose the smallest size your Boxes need. Its vCPUs and memory only set the defaults.

    cubicle boxes create --org acme template --disk 20 --wait

    You created it, so you have a grant on it.

  2. Open a shell in it:

    cubicle ssh acme/template
  3. Install software system-wide. The default base image is Debian-based, so use apt-get:

    sudo apt-get update
    sudo apt-get install -y git build-essential

    Install into system locations, such as packages or /usr/local/bin, not into your home directory. In a new Box, commands run as the guest account of whoever sends them, with PATH set to /usr/local/bin:/usr/bin:/bin. To copy your own files in, see Move files into and out of a Box.

  4. Remove what you do not want copied into every new Box: credentials, tokens, shell history and other personal files in your home directory. Then leave the shell with exit.

Snapshot the template

Stop the template Box, then take a snapshot of it.

const template = cubicle.boxes.ref("acme/template")
await template.stop() // waits until the Box is stopped
const snapshot = await template.snapshot("template-v1") // waits until it is complete
console.log(snapshot.id, snapshot.status) // => SNAPSHOT_ID complete

The service account needs a grant on acme/template or the admin role.

The template Box can stay stopped. Creating Boxes from its snapshot works in any state of the template Box.

Create Boxes from the template

import { Cubicle } from "@cubicle/sdk"

const cubicle = new Cubicle()
const snapshot = (await cubicle.listSnapshots({ org: "acme" })).find(
  (candidate) => candidate.box.name === "template" && candidate.name === "template-v1",
)
if (!snapshot) throw new Error("no template-v1 snapshot of acme/template")

// Waits until the new Box is ready. It runs on the Node in snapshot.node.
const box = await cubicle.boxes.create({ org: "acme", name: "job-1", snapshotId: snapshot.id })
const result = await box.exec(["git", "--version"])
console.log(result.exitCode) // => 0

cubicle.listSnapshots() returns the complete snapshots the caller may create Boxes from, newest first for each Box.

Pass vcpus, memoryMiB or diskGiB (--vcpus, --memory, --disk) to size the new Box differently. Create a Box from a snapshot covers what else a new Box gets.

Update the template

  1. Start the template Box and change it over SSH:

    cubicle boxes start acme/template --wait
    cubicle ssh acme/template
  2. Stop it and take a snapshot with a new name, such as template-v2, as in Snapshot the template. Names need not be unique, so a new name keeps the versions apart.

  3. Create new Boxes from the new snapshot's ID.

  • Boxes created from an earlier snapshot keep their disks. Changing the template does not change them.
  • A single snapshot cannot be deleted. Every snapshot stays until the template Box is deleted.
  • To retire the template, delete the template Box. This deletes all of its snapshots.

Troubleshooting

  • 503 capacity "the node holding this snapshot is unavailable" or "the node holding this snapshot has no capacity for this box": a Box created from this snapshot can only run on that Node. Delete Boxes you no longer need, ask for a smaller size, or try again later.
  • 409 conflict "box job-1 is still being created from a snapshot of this box" when you delete the template Box: its Node is still copying the snapshot's disk for job-1. Try again shortly.
  • A program you installed is not found in a new Box: it is in your home directory or in a directory that is not on PATH. Install it system-wide in the template and take a new snapshot.
  • For other snapshot errors, such as 403 forbidden or 404 not_found "snapshot not found", see Snapshot and restore a Box.

On this page