---
title: "Examples"
description: "Copy-paste examples for common Metalhost tasks — CLI, Go SDK, and HTTP/JSON."
url: "https://metalhost.net/docs/developers/examples"
---

# Examples

Copy-paste recipes for common Metalhost tasks — the same operation in CLI, Go SDK, and curl. New to the platform? Read [Concepts](https://metalhost.net/docs/developers/concepts.md) first. Set these env vars once:

```
export METALHOST_ENDPOINT=https://api.metalhost.net
export METALHOST_API_KEY=aes_...
export METALHOST_PROJECT=projects/my-project
```

> **proto-JSON field names**
>
> HTTP bodies use camelCase (`projectName`, `publicIpv4`, `cpuClass`). Go structs use PascalCase exported fields with the same wire names. YAML manifests in the CLI use camelCase in the `spec` block when round-tripping `get … -o yaml`.

## Discover projects and datacenters

### CLI

```
metalhost auth whoami -o json
metalhost project list
metalhost catalog datacenter list
metalhost catalog capacity --datacenter datacenters/us-dal-1
```

### Go

```
projectv1connect.NewProjectServiceClient(httpClient, baseURL).ListProjects(ctx, ...)
catalogv1connect.NewCatalogServiceClient(httpClient, baseURL).ListDatacenters(ctx, ...)
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.project.v1.ProjectService/ListProjects" \
  -H "Authorization: Bearer $METALHOST_API_KEY" -H "Content-Type: application/json" -d '{}'
```

## Who am I?

### CLI

```
metalhost auth whoami -o json
```

### Go

```
import (
    iamv1connect "github.com/AES-Services/metalhost-sdk/gen/go/aes/iam/v1/iamv1connect"
    iamv1 "github.com/AES-Services/metalhost-sdk/gen/go/aes/iam/v1"
)

resp, err := iamv1connect.NewIamServiceClient(httpClient, baseURL).GetCallerIdentity(
    ctx, connect.NewRequest(&iamv1.GetCallerIdentityRequest{}),
)
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.iam.v1.IamService/GetCallerIdentity" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Register an SSH key

### CLI

```
metalhost vm ssh-key create \
  --id laptop \
  --public-key "$(cat ~/.ssh/id_ed25519.pub)"
```

### Go

```
keys := computev1connect.NewSSHKeysServiceClient(httpClient, baseURL)
_, err := keys.CreateSSHKey(ctx, connect.NewRequest(&computev1.CreateSSHKeyRequest{
    ProjectName: os.Getenv("METALHOST_PROJECT"),
    SshKeyId:    "laptop",
    PublicKey:   string(mustRead("~/.ssh/id_ed25519.pub")),
}))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.compute.v1.SSHKeysService/CreateSSHKey" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg p "$METALHOST_PROJECT" --arg k "$(cat ~/.ssh/id_ed25519.pub)" \
    '{projectName:$p, sshKeyId:"laptop", publicKey:$k}')"
```

## Create a VM (flags)

### CLI

```
metalhost vm create \
  --hostname web-1 \
  --vcpus 2 --ram-gib 8 --cpu-class cascadelake \
  --image ubuntu-24-04 --disk-size-gib 80 \
  --assign-public-ipv4 \
  --ssh-key-name "$METALHOST_PROJECT/ssh-keys/laptop" \
  --wait
```

### Go

```
compute := computev1connect.NewComputeServiceClient(httpClient, baseURL)
resp, err := compute.CreateVirtualMachine(ctx, connect.NewRequest(
    &computev1.CreateVirtualMachineRequest{
        Manifest: &computev1.VirtualMachineManifest{
            Metadata: &computev1.VirtualMachineMetadata{
                Name: "web-1", Project: os.Getenv("METALHOST_PROJECT"),
            },
            Spec: &computev1.VirtualMachineSpec{
                Region: "datacenters/us-dal-1",
                Compute: &computev1.VMComputeSpec{
                    CpuClass: "cascadelake", Vcpus: 2, RamGib: 8,
                },
                Boot:   &computev1.VMBootSpec{Image: "ubuntu-24-04", DiskGib: 80},
                Network: &computev1.VMNetworkSpec{PublicIpv4: true},
                Users: []*computev1.UserSpec{{
                    Name: "ubuntu",
                    SshKeys: []string{os.Getenv("METALHOST_PROJECT") + "/ssh-keys/laptop"},
                }},
            },
        },
    },
))
opName := resp.Msg.GetOperation().GetName()
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.compute.v1.ComputeService/CreateVirtualMachine" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<'EOF'
{
  "manifest": {
    "metadata": { "name": "web-1", "project": "projects/my-project" },
    "spec": {
      "region": "datacenters/us-dal-1",
      "compute": { "cpuClass": "cascadelake", "vcpus": 2, "ramGib": 8 },
      "boot": { "image": "ubuntu-24-04", "diskGib": 80 },
      "network": { "publicIpv4": true },
      "users": [{ "name": "ubuntu", "sshKeys": ["projects/my-project/ssh-keys/laptop"] }]
    }
  }
}
EOF
```

## Create a GPU VM

Whole-GPU passthrough — check capacity first with `metalhost catalog capacity`. Full manifest fields: [CLI → VMs (YAML)](https://metalhost.net/docs/developers/guides/cli-vms.md#yaml-manifest).

### CLI

```
metalhost vm create \
  --hostname gpu-1 \
  --vcpus 8 --ram-gib 32 --cpu-class cascadelake \
  --gpu-model rtx4090 --gpu-count 1 \
  --image ubuntu-24-04 --disk-size-gib 100 \
  --assign-public-ipv4 \
  --ssh-key-name "$METALHOST_PROJECT/ssh-keys/laptop" \
  --wait
```

### Go

```
compute.CreateVirtualMachine(ctx, connect.NewRequest(
    &computev1.CreateVirtualMachineRequest{
        Manifest: &computev1.VirtualMachineManifest{
            Metadata: &computev1.VirtualMachineMetadata{Name: "gpu-1", Project: project},
            Spec: &computev1.VirtualMachineSpec{
                Region: "datacenters/us-dal-1",
                Compute: &computev1.VMComputeSpec{
                    CpuClass: "cascadelake", Vcpus: 8, RamGib: 32,
                    Gpu: &computev1.GPUSpec{Model: "rtx4090", Count: 1},
                },
                Boot: &computev1.VMBootSpec{Image: "ubuntu-24-04", DiskGib: 100},
                Network: &computev1.VMNetworkSpec{PublicIpv4: true},
            },
        },
    },
))
```

## Create a VM (YAML manifest)

Full guide with GPU, billing, cloud-init, and multi-user examples: [CLI → VMs (YAML manifest)](https://metalhost.net/docs/developers/guides/cli-vms.md#yaml-manifest). Minimal example:

```
apiVersion: compute.metalhost.io/v1
kind: VirtualMachine
metadata:
  name: web-1
  project: projects/my-project
spec:
  region: datacenters/us-dal-1
  compute:
    cpuClass: cascadelake
    vcpus: 2
    ramGib: 8
  boot:
    image: ubuntu-24-04
    diskGib: 80
  network:
    publicIpv4: true
  users:
    - name: ubuntu
      sshKeys:
        - projects/my-project/ssh-keys/laptop
```

### CLI

```
metalhost vm apply -f vm.yaml --wait
```

### Go

Unmarshal YAML into `computev1.VirtualMachineManifest` (e.g. with `google.golang.org/protobuf/encoding/protojson` after a YAML→JSON hop), then pass to `CreateVirtualMachineRequest` with a `Manifest` field.

### curl

Convert the manifest to the JSON shape in the previous section and POST to `CreateVirtualMachine`.

## Poll an operation

### CLI

```
metalhost ops wait operations/op_abc123
# or block on the mutating command directly:
metalhost vm create ... --wait
```

### Go

```
ops := opsv1connect.NewOperationsServiceClient(httpClient, baseURL)
for {
    r, _ := ops.GetOperation(ctx, connect.NewRequest(
        &opsv1.GetOperationRequest{Name: opName},
    ))
    st := r.Msg.GetOperation().GetState()
    if st == opsv1.State_STATE_SUCCEEDED || st == opsv1.State_STATE_FAILED {
        break
    }
    time.Sleep(2 * time.Second)
}
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.ops.v1.OperationsService/GetOperation" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"operations/op_abc123"}' | jq '.operation.state'
```

## List and get VMs

### CLI

```
metalhost vm list
metalhost vm get web-1 -o json | jq '.virtualMachine.publicIpv4'
```

### Go

```
list, _ := compute.ListVirtualMachines(ctx, connect.NewRequest(
    &computev1.ListVirtualMachinesRequest{ProjectName: project, PageSize: 100},
))
one, _ := compute.GetVirtualMachine(ctx, connect.NewRequest(
    &computev1.GetVirtualMachineRequest{Name: "projects/my-project/virtual-machines/web-1"},
))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.compute.v1.ComputeService/ListVirtualMachines" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"projectName\":\"$METALHOST_PROJECT\"}"
```

## VM lifecycle

Start, stop, resize, snapshot, and clone — see [CLI → VMs](https://metalhost.net/docs/developers/guides/cli-vms.md).

### CLI

```
metalhost vm stop web-1
metalhost vm start web-1
metalhost vm resize web-1 --vcpus 4 --ram-gib 16 --cpu-class cascadelake
metalhost vm snapshot create --vm projects/my-project/virtual-machines/web-1 --display-name pre-upgrade
metalhost vm clone --source projects/my-project/virtual-machines/web-1 --display-name web-1-clone
```

### Go

```
compute.StopVirtualMachine(ctx, connect.NewRequest(&computev1.StopVirtualMachineRequest{Name: vm}))
compute.StartVirtualMachine(ctx, connect.NewRequest(&computev1.StartVirtualMachineRequest{Name: vm}))
compute.ResizeVirtualMachine(ctx, connect.NewRequest(&computev1.ResizeVirtualMachineRequest{
    Name: vm, Vcpus: 4, RamGib: 16, CpuClass: "cascadelake",
}))
compute.SnapshotVirtualMachine(ctx, connect.NewRequest(&computev1.SnapshotVirtualMachineRequest{
    VmName: vm, DisplayName: "pre-upgrade",
}))
compute.CloneVirtualMachine(ctx, connect.NewRequest(&computev1.CloneVirtualMachineRequest{
    SourceVmName: vm, TargetDisplayName: "web-1-clone",
}))
```

## Delete a VM

### CLI

```
metalhost vm delete web-1 --yes --wait
```

### Go

```
_, err := compute.DeleteVirtualMachine(ctx, connect.NewRequest(
    &computev1.DeleteVirtualMachineRequest{
        Name: "projects/my-project/virtual-machines/web-1",
    },
))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.compute.v1.ComputeService/DeleteVirtualMachine" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"projects/my-project/virtual-machines/web-1"}'
```

## Quote pricing

### CLI

```
metalhost catalog pricing quote \
  --vcpus 2 --ram-gib 8 --cpu-class cascadelake --boot-disk-gib 80
```

### Go

```
catalog := catalogv1connect.NewCatalogServiceClient(httpClient, baseURL)
_, err := catalog.QuoteVirtualMachine(ctx, connect.NewRequest(
    &catalogv1.QuoteVirtualMachineRequest{
        Vcpus: 2, RamGib: 8, CpuClass: "cascadelake", BootDiskGib: 80,
    },
))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.catalog.v1.CatalogService/QuoteVirtualMachine" \
  -H "Authorization: Bearer $METALHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"vcpus":2,"ramGib":8,"cpuClass":"cascadelake","bootDiskGib":80}'
```

## Lease bare metal

### CLI

```
metalhost baremetal offers
metalhost baremetal quote --host hosts/dal-r740-01 --billing-mode monthly-1
metalhost baremetal order my-box \
  --host hosts/dal-r740-01 \
  --os-image ubuntu-24-04 --ssh-key-name "$METALHOST_PROJECT/ssh-keys/laptop" \
  --billing-mode monthly-1 --ipv4-prefix-len 32
```

### Go

```
bm := baremetalv1connect.NewBareMetalServiceClient(httpClient, baseURL)
bm.ListAvailableBareMetal(ctx, connect.NewRequest(&baremetalv1.ListAvailableBareMetalRequest{}))
bm.CreateBareMetalInstance(ctx, connect.NewRequest(&baremetalv1.CreateBareMetalInstanceRequest{
    Name: "bare-metal-instances/my-box", ProjectName: project,
    HostName: "hosts/dal-r740-01", OsImage: "ubuntu-24-04",
    SshKeyNames: []string{project + "/ssh-keys/laptop"},
}))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.baremetal.v1.BareMetalService/ListAvailableBareMetal" \
  -H "Authorization: Bearer $METALHOST_API_KEY" -H "Content-Type: application/json" -d '{}'
```

Bare-metal leases include **unlimited bandwidth** — no transfer metering. Full lifecycle (power, ISO, release): [Go SDK — Bare metal](https://metalhost.net/docs/developers/guides/sdk-bare-metal.md).

## Create a file share

### CLI

```
metalhost storage file-share create \
  --size-gib 100 \
  --datacenter datacenters/us-dal-1 \
  --display-name shared-data
```

### Go

```
storage.CreateFileShare(ctx, connect.NewRequest(&storagev1.CreateFileShareRequest{
    ProjectName: project, DatacenterName: "datacenters/us-dal-1",
    SizeGib: 100, DisplayName: "shared-data",
}))
```

Mount from any VM on the same tenant network — [CLI → Storage](https://metalhost.net/docs/developers/guides/cli-storage.md).

## Create and attach a disk

### CLI

```
metalhost disk create --size-gib 100 --region datacenters/us-dal-1
metalhost disk attach projects/my-project/disks/data-1 \
  --vm projects/my-project/virtual-machines/web-1
metalhost disk resize projects/my-project/disks/data-1 --size-gib 200
```

### Go

```
storage := storagev1connect.NewStorageServiceClient(httpClient, baseURL)
storage.CreateDisk(ctx, connect.NewRequest(&storagev1.CreateDiskRequest{
    Parent: project,
    Disk: &storagev1.Disk{
        DatacenterName: "datacenters/us-dal-1",
        SizeGib: 100, StorageClass: "nvme",
    },
}))
storage.AttachDisk(ctx, connect.NewRequest(&storagev1.AttachDiskRequest{
    DiskName: "projects/my-project/disks/data-1",
    VmName:   "projects/my-project/virtual-machines/web-1",
}))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.storage.v1.StorageService/CreateDisk" \
  -H "Authorization: Bearer $METALHOST_API_KEY" -H "Content-Type: application/json" \
  -d '{"parent":"projects/my-project","disk":{"datacenterName":"datacenters/us-dal-1","sizeGib":100,"storageClass":"nvme"}}'
```

## Create a tenant network

Only needed for extra isolation — default network is auto-created per datacenter.

### CLI

```
metalhost network create \
  --id staging-net \
  --region datacenters/us-dal-1 \
  --display-name "Staging"
```

### Go

```
net.CreateNetwork(ctx, connect.NewRequest(&networkv1.CreateNetworkRequest{
    ProjectName: project, NetworkId: "staging-net",
    DatacenterName: "datacenters/us-dal-1", DisplayName: "Staging",
}))
```

## Open a firewall port

### CLI

```
metalhost firewall create \
  --vm projects/my-project/virtual-machines/web-1 \
  --port 22/tcp --source 203.0.113.10/32 \
  --display-name "ssh from office"
```

### Go

```
net := networkv1connect.NewNetworkServiceClient(httpClient, baseURL)
net.CreateFirewallRule(ctx, connect.NewRequest(&networkv1.CreateFirewallRuleRequest{
    ProjectName: project, DatacenterName: "datacenters/us-dal-1",
    TargetVm: project + "/virtual-machines/web-1",
    Sources: []string{"203.0.113.10/32"},
    Ports: []*networkv1.PortMapping{{Protocol: "tcp", Port: 22}},
}))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.network.v1.NetworkService/CreateFirewallRule" \
  -H "Authorization: Bearer $METALHOST_API_KEY" -H "Content-Type: application/json" \
  -d '{"projectName":"projects/my-project","datacenterName":"datacenters/us-dal-1","targetVm":"projects/my-project/virtual-machines/web-1","sources":["203.0.113.10/32"],"ports":[{"protocol":"tcp","port":22}]}'
```

## Register a webhook

The signing secret is returned once. Store it and verify deliveries with HMAC-SHA256:

### CLI

```
metalhost webhook create \
  --name webhook-subscriptions/deploy-hook \
  --endpoint https://hooks.example.com/metalhost \
  --event compute.vm.* \
  --event ops.operation.completed
```

### Go

```
hooks := webhooksv1connect.NewWebhooksServiceClient(httpClient, baseURL)
resp, _ := hooks.CreateSubscription(ctx, connect.NewRequest(
    &webhooksv1.CreateSubscriptionRequest{
        Name: "webhook-subscriptions/deploy-hook",
        ProjectName: project,
        EndpointUrl: "https://hooks.example.com/metalhost",
        EventTypes: []string{"compute.vm.*", "ops.operation.completed"},
    },
))
secret := resp.Msg.GetSecret() // store for HMAC verification
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.webhooks.v1.WebhooksService/CreateSubscription" \
  -H "Authorization: Bearer $METALHOST_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"webhook-subscriptions/deploy-hook","projectName":"projects/my-project","endpointUrl":"https://hooks.example.com/metalhost","eventTypes":["compute.vm.*"]}'
```

See [Go SDK — Webhooks](https://metalhost.net/docs/developers/guides/sdk-webhooks.md) for signature verification.

## Mint an API key

### CLI

```
metalhost iam keys create \
  --display-name ci-bot \
  --project projects/my-app \
  --project-scoped \
  -o json | jq -r '.secret'
```

### Go

```
iam.CreateApiKey(ctx, connect.NewRequest(&iamv1.CreateApiKeyRequest{
    DisplayName: "ci-bot", DefaultProject: project, ProjectScoped: true,
}))
// resp.Msg.Secret — shown once; store immediately
```

Dashboard walkthrough: [API keys](https://metalhost.net/docs/dashboard/guides/api-keys.md).

## Open a support ticket

### CLI

```
metalhost support ticket create \
  --org organizations/my-org \
  --subject "Quota increase" \
  --body "Need 50 VMs in projects/prod for load test." \
  --category BILLING
```

### Go

```
support.CreateTicket(ctx, connect.NewRequest(&supportv1.CreateTicketRequest{
    OrganizationName: org, Subject: "Quota increase",
    Body: "Need 50 VMs in projects/prod for load test.",
    Category: supportv1.TicketCategory_TICKET_CATEGORY_BILLING,
}))
```

## Check quota usage

### CLI

```
metalhost quota --project projects/my-app -o json
```

Request increases via support — [CLI → Support](https://metalhost.net/docs/developers/guides/cli-support.md).

## Check wallet balance

### CLI

```
metalhost wallet account list --org organizations/my-org
metalhost wallet balance organizations/my-org/wallets/default
```

### Go

```
wallet := walletv1connect.NewWalletServiceClient(httpClient, baseURL)
_, err := wallet.GetWalletBalance(ctx, connect.NewRequest(
    &walletv1.GetWalletBalanceRequest{
        Name: "organizations/my-org/wallets/default",
    },
))
```

### curl

```
curl -sS -X POST "$METALHOST_ENDPOINT/aes.wallet.v1.WalletService/GetWalletBalance" \
  -H "Authorization: Bearer $METALHOST_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"organizations/my-org/wallets/default"}'
```

## Top up with crypto (Coinbase)

Returns a checkout URL — settled 1:1 in USDC. Minimum $5 (`500` minor units).

### CLI

```
metalhost wallet top-up coinbase \
  --wallet organizations/my-org/wallets/default \
  --amount-minor 5000
```

Card top-ups and payment methods: [CLI → Wallet](https://metalhost.net/docs/developers/guides/cli-wallet.md).

## CI script pattern

A minimal bash deploy script using only the CLI and `jq`:

```
#!/usr/bin/env bash
set -euo pipefail

: "${METALHOST_API_KEY:?}"
: "${METALHOST_PROJECT:?}"

NAME="ci-$(date +%s)"

metalhost vm create \
  --hostname "$NAME" \
  --vcpus 2 --ram-gib 4 --cpu-class cascadelake \
  --image ubuntu-24-04 --disk-size-gib 40 \
  --ssh-key-name "$METALHOST_PROJECT/ssh-keys/ci" \
  --wait -o json > create.json

IP=$(jq -r '.operation.metadata.virtual_machine_name // empty' create.json)
VM=$(metalhost vm get "$NAME" -o json)
IP=$(echo "$VM" | jq -r '.virtualMachine.publicIpv4')
echo "VM $NAME at $IP"
# ... run tests over SSH ...
metalhost vm delete "$NAME" --yes --wait
```

## What's next

- [Concepts](https://metalhost.net/docs/developers/concepts.md) — platform mental model.
- [CLI quickstart](https://metalhost.net/docs/developers/quickstart.md) — guided first run.
- [Go SDK — Getting started](https://metalhost.net/docs/developers/sdk.md) — complete program and service map.
- [Go SDK — VMs](https://metalhost.net/docs/developers/guides/sdk-vms.md) — manifest reference.
- [CLI reference](https://metalhost.net/docs/developers/cli.md) — every command.
- [API reference](https://metalhost.net/docs/developers/api.md) — schema browser.
