Skip to main content

Independently provisioned machines

A Machine does not have to become a Kubernetes node. You can request one, get a provisioned server with your SSH keys on it, and stop there. Nothing installs a kubelet, and no tenant cluster is involved.

This is the path for customers who buy compute rather than clusters. It's also the starting point for anyone who wants to run something other than Kubernetes on vMetal-provisioned hardware.

This guide uses both contexts introduced in Set up your hardware: SSHKey and NodeClaim commands target the management cluster, while BareMetalHost commands target the control plane cluster. The platform UI does the same management-cluster work under a project's Machines view.

For the broader model behind Machines and servers, see Machine and server concepts. For the path where servers do join a tenant cluster, see Cluster attachment.

Modify the following with your specific values to replace them across the whole page:

Before you begin​

Work through Set up your hardware steps 1 through 5 first. Those steps apply to every use of vMetal. Step 6 there is a branch point that routes to one of three guides. This is the one for servers that run no Kubernetes. It picks up where step 5 ends.

By the end of those steps you have:

  • A working NodeProvider with at least one BareMetalHost in available state.
  • At least one node type on that NodeProvider, with an OS image. See Configuration.
  • A project to own the Machine. Machines live in the project namespace, loft-p-<project>.

Steps 1 and 2 below amend the node type you created while setting up your hardware. Do them before you claim a server. SSH keys are injected at provision time, and a server that is already provisioned does not pick up a key you add afterwards.

This guide provisions one server so you can see the whole path end to end. To do the same across a rack or a datacenter, see Scale to a fleet.

Licensing

Requesting a Machine without a tenant cluster requires the machine-management feature in your license. The platform rejects the request at creation time if your plan does not include it. To check your plan, in the platform navigate to Admin > License and Billing. If your plan does not include this feature, contact support@vcluster.com.

How this differs from a tenant cluster node​

Everything up to the moment the OS boots is identical. The platform selects an eligible server, allocates an IP, renders cloud-init, and hands the server to the driver for provisioning. The difference is what the platform appends to the cloud-init it generates.

Independently provisioned machineTenant cluster node
spec.vClusterRefEmptyNames a tenant cluster
Cloud-init containsYour user data and SSH keysYour user data, SSH keys, and a Kubernetes join command
ResultA server you reach over SSHA Kubernetes node in that cluster
On releaseThe server is cleanedThe node is removed, then the server is cleaned

Server selection, IP allocation, OS image resolution, network configuration, and the BareMetalHost lifecycle are the same in both cases. So is the return path. Deleting the Machine returns the server to the available pool for reuse.

1. Register an SSH key​

Without a Kubernetes join, SSH is how you reach the server. Register the public key as an SSHKey resource so node types and Machines can reference it by name.

apiVersion: management.loft.sh/v1
kind: SSHKey
metadata:
name: ops-key
spec:
displayName: "Ops team key"
publicKey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... ops@example.com"
kubectl --context <management-context> apply -f ops-key.yaml

SSHKey resources are cluster-scoped. Reference several of them from one Machine with a comma-separated list.

2. Add the SSH key to your node type​

Node types live inside the NodeProvider you created in Set up your hardware. Edit that NodeProvider and add vcluster.com/ssh-keys to the node type, so every Machine created from it gets the key. The value is a comma-separated list of SSHKey resource names, not the key material itself.

nodeTypes:
- name: "compute-node"
displayName: "Compute Node"
resources:
cpu: "32"
memory: 128Gi
bareMetalHosts:
selector:
matchLabels:
role: compute
properties:
vcluster.com/os-image: ubuntu-noble
vcluster.com/ssh-keys: ops-key

You can also set vcluster.com/ssh-keys directly on a Machine to add keys for one request only. Properties merge from the NodeProvider, then the node type, then the Machine. See Configuration properties.

If you need more than SSH keys at first boot, such as a package install or a service unit, set vcluster.com/user-data with a full cloud-config document. The platform appends your SSH keys to it rather than replacing it. See SSH and user data.

3. Request the machine​

Create a NodeClaim with no vClusterRef. That single omission is what makes this an independently provisioned machine.

apiVersion: management.loft.sh/v1
kind: NodeClaim
metadata:
name: machine-01
namespace: loft-p-my-project
spec:
displayName: "Compute 01"
providerRef: metal3-provider
typeRef: metal3-provider.compute-node
kubectl --context <management-context> apply -f machine-01.yaml

Two details to get right:

  • typeRef takes the node type's full resource name, which the platform generates as <provider>.<node type>. A node type called compute-node on a provider called metal3-provider becomes metal3-provider.compute-node. List them with kubectl --context <management-context> get nodetypes.
  • The namespace is the project namespace, loft-p-<project>. A Machine created anywhere else is not governed by that project's roles or quotas.

To pin the request to one specific physical server rather than letting the platform choose, set the metal3.vcluster.com/server-name property to a BareMetalHost name. See Server pinning.

You can do all of this in the platform UI instead. Under a project, open Machines, then Create Machine, and leave the tenant cluster unset. The machines list marks these as Provisioned manually, which is a useful filter once a project holds a mix of both kinds.

4. Watch it provision​

kubectl --context <management-context> get nodeclaim machine-01 \
-n loft-p-my-project -o wide

The Machine reaches Available once the driver finishes provisioning. Because there is no cluster to join, the Joined condition is satisfied as soon as provisioning completes. It does not indicate that any Kubernetes component is running.

Watch the underlying server move through its own states at the same time:

kubectl --context <control-plane-context> get baremetalhost \
-n metal3-system

The server passes through provisioning and reaches provisioned. For what each state means and what to do when one stalls, see BareMetalHost states and Troubleshooting.

5. Connect over SSH​

The platform records the allocated IP on the Machine as an annotation once provisioning completes.

kubectl --context <management-context> get nodeclaim machine-01 \
-n loft-p-my-project \
-o jsonpath='{.metadata.annotations.nodeclaim\.vcluster\.com/ip-address}'

Then connect as the user your OS image defines. For the Ubuntu cloud images used in these examples, that is ubuntu.

ssh ubuntu@<ip-address>

The IP annotation is best-effort. A driver that cannot report an address leaves it unset, and the Machine still provisions normally. When it's missing, find the address through your own DHCP or IPAM records. If the Machine is Available but SSH fails, follow Machine is available but SSH fails.

The platform UI shows the same address in the machines list, with a copy button.

6. Release the machine​

Delete the Machine when you're done with the server.

kubectl --context <management-context> delete nodeclaim machine-01 \
-n loft-p-my-project

The driver cleans the server and returns it to the available pool, where a later Machine can claim it. Deleting the Machine does not delete the BareMetalHost.

There is nothing for Kubernetes to drain, so quiesce your workloads before releasing the Machine. Metal3's default automated cleaning removes partition tables but does not perform full-disk data erasure. Apply any additional sanitization your security policy requires before returning a server to a shared pool. See Reuse and reprovisioning.

For removing a server from the fleet entirely rather than releasing a claim on it, see Removing servers.

Scale to a fleet​

Nothing above changes when you go from one server to a hundred. Register the servers, define node types that describe your hardware, and create one Machine per server you want to hand out.

Registering servers, labelling them, and designing node types are the same work whichever way you use the capacity. Only the last step, creating the Machines, differs between handing servers out directly and giving them to a tenant cluster.

Register servers in bulk​

Apply BareMetalHost and Secret resources together in a single manifest rather than one file per server. See Bulk registration.

Label each server as you register it. Labels are how node types select servers later, so setting them up front saves a relabeling pass across the whole fleet. Rack, datacenter, hardware generation, and GPU model are the keys that earn their place. See Label strategy, which applies to any fleet and not only GPU hardware.

Define node types for your hardware​

A node type groups servers by label selector and sets their provisioning defaults. Define one per distinct configuration you intend to hand out.

nodeTypes:
- name: "compute-gen9"
displayName: "Compute, Gen 9"
resources:
cpu: "32"
memory: 128Gi
bareMetalHosts:
selector:
matchLabels:
role: compute
generation: gen9
properties:
vcluster.com/os-image: ubuntu-noble
vcluster.com/ssh-keys: ops-key
- name: "compute-gen10"
displayName: "Compute, Gen 10"
resources:
cpu: "64"
memory: 256Gi
bareMetalHosts:
selector:
matchLabels:
role: compute
generation: gen10
properties:
vcluster.com/os-image: ubuntu-noble
vcluster.com/ssh-keys: ops-key

Selectors can overlap, so one server may be eligible under more than one node type. See Server selection and One node type per hardware profile.

Claim several servers at once​

Each Machine claims exactly one server, so ten servers means ten Machines. Create them from a single manifest.

apiVersion: management.loft.sh/v1
kind: NodeClaim
metadata:
name: machine-01
namespace: loft-p-my-project
spec:
providerRef: metal3-provider
typeRef: metal3-provider.compute-gen10
---
apiVersion: management.loft.sh/v1
kind: NodeClaim
metadata:
name: machine-02
namespace: loft-p-my-project
spec:
providerRef: metal3-provider
typeRef: metal3-provider.compute-gen10

The platform selects an eligible server for each claim independently. Claims that find no eligible server wait rather than fail, so a claim sitting in Pending usually means the pool is empty for that node type.

Track what is claimed and what is free​

List every Machine in a project with the server and IP it holds:

kubectl --context <management-context> get nodeclaims \
-n loft-p-my-project \
-o custom-columns='NAME:.metadata.name,PHASE:.status.phase,IP:.metadata.annotations.nodeclaim\.vcluster\.com/ip-address'

Count the servers still free to claim:

kubectl --context <control-plane-context> get baremetalhost \
-n metal3-system -o json | \
jq '[.items[] | select(.status.provisioning.state=="available")] | length'

For the rest of the fleet view, including which servers are in error and how to withhold one from the pool, see Checking fleet capacity.

What vMetal manages, and what it doesn't​

The boundary sits at the operating system. vMetal owns everything up to and including first boot, and your OS image and cloud-init own everything after it.

vMetal manages:

  • The server. Registration, inspection, hardware inventory, OS installation, and network configuration.
  • The claim. Which server is held by which Machine, and returning it to the pool when you release it.
  • Access and quota. Project roles govern who can request a Machine and how many. An independently provisioned machine is governed by the same project isolation model as any other Machine. See RBAC and project isolation.

vMetal does not manage:

  • Kubernetes. No kubelet, no container runtime, no CNI, unless your own OS image or user data installs them.
  • In-guest monitoring or agents. Platform observability covers the provisioning layer and the server's BMC-reported state, not what runs inside the OS.
  • The GPU driver stack. On tenant cluster nodes a device plugin advertises GPUs to Kubernetes, and that mechanism does not exist here. Install vendor drivers through your OS image or user data. See GPU presentation modes.
  • Anything you install after first boot. Configuration management, patching, and application deployment are yours.