> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxd.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up a golden image VM

> Install your app on one machine, snapshot it, and keep the snapshot fresh on every push to main.

A golden image is a [snapshot](/guides/snapshots) of a machine with your app fully set up: repo cloned, dependencies installed, services running. Once it exists, anyone (or any script) can stamp out a ready machine from it in one command, with the app already serving. Keep the snapshot refreshed on every push to main and it always reflects your latest code.

This is the base layer for [preview environments](/use-cases/preview-environments), disposable test machines, and warm agent sandboxes.

## 1. Set up the machine

Create a machine and install your app on it the way you would set up a fresh laptop:

```bash theme={"theme":"github-dark"}
boxd machine new myapp-golden
boxd connect myapp-golden
```

Inside the machine:

```bash theme={"theme":"github-dark"}
git clone https://github.com/you/myapp.git
cd myapp
npm ci                          # or pip install, cargo build, ...
sudo systemctl enable --now myapp   # start it under systemd, pm2, or docker compose
```

With the [GitHub integration](/guides/integrations/github) connected, the clone works against private repos with nothing to configure. Start the app under something that survives a reboot (`systemd`, `pm2`, or `docker compose`), because machines created from the snapshot boot through the same services.

Point the machine's proxy at your app's port and check it serves:

```bash theme={"theme":"github-dark"}
boxd machine proxy set-port --vm myapp-golden --port 3000
# open https://myapp-golden.boxd.sh
```

Secrets your app needs belong in [env vars and secrets](/guides/env-secrets), which boxd injects into every machine you own. Values baked into the snapshot's disk end up in every machine created from it, so keep credentials out of the image itself.

## 2. Snapshot it

```bash theme={"theme":"github-dark"}
boxd snapshots save myapp-golden myapp-main
```

The capture includes memory and disk together, so a machine created from it wakes with the app already running instead of booting and starting up. Test it:

```bash theme={"theme":"github-dark"}
boxd machine new test-1 --from-snapshot myapp-main
# open https://test-1.boxd.sh: your app, already serving
boxd machine remove test-1 -y
```

Re-saving under the same name adds a new version, and the latest version is what new machines get. `myapp-main` therefore stays one stable name your scripts and teammates can rely on while the content moves forward.

## 3. Refresh it on every push to main

Keep the golden current by re-syncing the machine and re-saving the snapshot whenever main changes. The recommended way to automate this is through the [TypeScript](/reference/typescript-sdk) or [Python](/reference/python-sdk) SDK, which reads a `BOXD_API_KEY` from the environment and handles authentication for you. The whole refresh is a few lines:

<Tabs>
  <Tab title="TypeScript">
    ```typescript refresh-golden.ts theme={"theme":"github-dark"}
    import { Boxd } from "@boxd-sh/sdk";

    const boxd = new Boxd();   // reads BOXD_API_KEY

    await boxd.machines.exec("myapp-golden", {
      command: "cd ~/myapp && git pull && npm ci && sudo systemctl restart myapp",
    });
    await boxd.snapshots.create("myapp-golden", "myapp-main");

    await boxd.close();
    ```
  </Tab>

  <Tab title="Python">
    ```python refresh_golden.py theme={"theme":"github-dark"}
    from boxd import Boxd

    with Boxd() as boxd:   # reads BOXD_API_KEY
        boxd.machines.exec(
            "myapp-golden",
            "cd ~/myapp && git pull && npm ci && sudo systemctl restart myapp",
        )
        boxd.snapshots.create("myapp-golden", "myapp-main")
    ```
  </Tab>
</Tabs>

Swap the sync command for whatever your stack needs (`pip install`, `cargo build`, `docker compose up -d --build`, a migration step). The `exec` also wakes the golden if it was hibernating, and the save requires it `running`, which it then is.

Run the script from anything that reacts to a push to main. Wiring it into GitHub Actions is one small job: run it on `push` to your default branch with `BOXD_API_KEY` stored as a repo secret. Mint the key once, without it touching your clipboard:

```bash theme={"theme":"github-dark"}
boxd auth keys create "refresh-golden" | gh secret set BOXD_API_KEY --repo you/myapp
```

The key is fenced to one org. See [API keys](/cli/authentication#api-keys).

<Tip>
  You can of course also run the boxd part on a boxd VM, for example on a self-hosted runner or a small webhook listener living there. Inside a machine, `new Boxd()` authenticates automatically, so the key disappears entirely and the script above runs unchanged.
</Tip>

## 4. Create machines from it

From now on, a ready copy of your app is one command away:

```bash theme={"theme":"github-dark"}
boxd machine new pr-482 --from-snapshot myapp-main       # a preview for a pull request
boxd machine new alice-dev --from-snapshot myapp-main    # a dev workspace for a teammate
boxd machine new agent-run-7 --from-snapshot myapp-main  # a warm sandbox for an agent
```

Each machine gets its own URL, SSH access, and disk, and starts from the latest version of the snapshot. [Preview environments](/use-cases/preview-environments) builds this into a per-PR workflow.

<Note>
  A [fork](/guides/fork) also copies a running machine, directly and in milliseconds, but it copies the machine as it is **right now**. The snapshot is what gives you a named, versioned baseline that stays stable while the golden machine itself moves, and that outlives the machine entirely.
</Note>
