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

# Virtual machines

> Create and access an isolated macOS virtual machine

Use a virtual machine for isolated macOS workloads, parallel jobs, and fast
create-delete cycles.

## Create

Choose an image from the [catalog](/platform/images), then replace
`IMAGE_NAME` with its name:

```bash theme={null}
mthr vm create build-vm --image IMAGE_NAME --wait
```

Create discloses the prepaid hold it will place and asks you to confirm before
committing. Pass `-y` (`--yes`) to skip the prompt in automation. See
[Billing](/platform/billing) for funds and readiness.

The CLI creates or reuses `~/.ssh/id_ed25519` and sends only its public key.
The private key stays on your workstation.

Add application ports at creation:

```bash theme={null}
mthr vm create web-vm \
  --image IMAGE_NAME \
  --port web=tcp:8080 \
  --wait
```

The names `ssh`, `screen`, `vnc`, and `desktop`, and guest ports `22` and
`5900`, are reserved for built-in access.

## Connect

Open SSH:

```bash theme={null}
mthr vm ssh build-vm
```

Open Apple Screen Sharing:

```bash theme={null}
mthr vm desktop build-vm
```

List published ports:

```bash theme={null}
mthr vm ports web-vm
```

Forward the `web` port to local port `18080`:

```bash theme={null}
mthr vm port-forward web-vm \
  --port-name web \
  --local-port 18080
```

Every connection uses a private, authenticated tunnel. Guest addresses are not
public.

## Inspect and delete

```bash theme={null}
mthr vm ls
mthr vm get build-vm
mthr vm delete build-vm --wait
```

Deleting a VM keeps its image available.

## Use kubectl

After [configuring kubectl](/quickstart#use-kubectl), create `build-vm.yaml`.
Replace `IMAGE_NAME` with a catalog name and `SSH_PUBLIC_KEY` with the
contents of `~/.ssh/id_ed25519.pub`.

```yaml theme={null}
apiVersion: compute.mountthor.com/v1alpha1
kind: VirtualMachine
metadata:
  name: build-vm
spec:
  imageRef: IMAGE_NAME
  bootstrap:
    sshAuthorizedKeys:
      - username: admin
        publicKey: SSH_PUBLIC_KEY
  ports:
    - name: ssh
      protocol: tcp
      guestPort: 22
      exposure: private-tunnel
```

Apply and inspect it:

```bash theme={null}
kubectl apply -f build-vm.yaml
kubectl get virtualmachine build-vm --watch
```

Use the [lifecycle contract](/quickstart#observe-lifecycle) when polling.
`status.publishedPorts` contains the supported command for each available
port. Run that command instead of constructing a transport URL.

Delete the VM:

```bash theme={null}
kubectl delete virtualmachine build-vm
```

See [Images](/platform/images) to choose an image.
