Virtual Machine
The vm resource defines a virtual machine that runs as part of your lab sandbox environment. VMs provide full operating system compute for labs that need system services, OS-level configuration, extra disks, or workloads that do not fit a container-only design.
Use Cases
Section titled “Use Cases”As a lab author, you can use VMs to create richer sandbox environments:
- Full Operating Systems: Run workloads that need a complete Linux environment instead of a single container process
- System Configuration Labs: Teach package installation, service management, networking, or disk setup
- Nested Runtime Labs: Provide VM images that include Docker, Kubernetes, or other platform tooling
- Stateful Services: Attach additional disks for databases, data directories, or other storage-heavy exercises
Use a VM when the lab experience depends on OS behavior. Use a container when a lighter process-isolated runtime is enough.
HCL Syntax
Section titled “HCL Syntax”Basic Syntax
Section titled “Basic Syntax”resource "vm" "ubuntu" { image { name = "ubuntu:24.04" }}Full Syntax
Section titled “Full Syntax”resource "network" "main" { subnet = "10.50.0.0/24"}
resource "vm" "ubuntu" { config { arch = "x86_64" }
image { name = "ubuntu:24.04" username = "registry-user" password = variable.registry_token }
resources { cpu = 2 memory = 2048 }
environment = { GREETING = "hello" }
dns = ["1.1.1.1"] startup_script = <<-EOF #!/bin/sh apt-get update apt-get install -y nginx EOF
network { id = resource.network.main.meta.id ip_address = "10.50.0.10" aliases = ["web"] }
disk { destination = "/data" size = 20 }
volume { source = "./shared" destination = "/mnt/shared" read_only = true }
port { local = "8080" host = "8080" protocol = "tcp" open_in_browser = "/" }
port_range { range = "3000-3010" enable_host = true protocol = "tcp" }
health_check { timeout = "30s"
tcp { address = "localhost:8080" } }}Resource Structure
Section titled “Resource Structure”vm├─ config│ └─ arch├─ image (required)│ └─ name, username, password├─ resources│ └─ cpu, memory├─ environment├─ dns[]├─ startup_script├─ networking│ └─ network[] (repeatable)│ └─ id, ip_address, aliases├─ storage│ ├─ disk[] (repeatable)│ │ └─ destination, size│ └─ volume[] (repeatable)│ └─ source, destination, type, read_only├─ port exposure│ ├─ port[] (repeatable)│ │ └─ local, host, protocol, open_in_browser│ └─ port_range[] (repeatable)│ └─ range, enable_host, protocol└─ monitoring └─ health_check ├─ timeout ├─ http[] (address, method, body, headers, success_codes) ├─ tcp[] (address) └─ exec[] (command, script, exit_code)Fields
Section titled “Fields”| Field | Required | Type | Description |
|---|---|---|---|
config | block | VM architecture configuration | |
image | ✓ | block | VM image configuration |
resources | block | CPU and memory allocation | |
environment | map(string) | Environment variables passed to the VM agent, startup script, and exec health checks | |
dns | list(string) | DNS server IP addresses used by the VM | |
startup_ | string | Script to run after the VM boots and the agent is reachable. Use this where you would use entrypoint or command on a container. | |
network | block | Network attachments (repeatable) | |
disk | block | Additional VM disks (repeatable) | |
volume | block | Host directory shares attached to the VM (repeatable) | |
port | block | Port mappings (repeatable) | |
port_ | block | Port range mappings (repeatable) | |
health_ | block | VM health check |
Config Configuration
Section titled “Config Configuration”vm -> config
Configures VM runtime details.
| Field | Required | Type | Description |
|---|---|---|---|
arch | string | CPU architecture. Defaults to “x86_64”. Supported values are “x86_64” and “aarch64”. |
Image Configuration
Section titled “Image Configuration”vm -> image
Configures the VM image to boot.
| Field | Required | Type | Description |
|---|---|---|---|
name | ✓ | string | VM image name with optional tag |
username | string | Registry username for private images | |
password | string | Registry password or token for private images |
Resource Constraints
Section titled “Resource Constraints”vm -> resources
Configures CPU and memory allocation for the VM.
| Field | Required | Type | Description |
|---|---|---|---|
cpu | number | Number of virtual CPUs (1 = 1 core). Defaults to 1. container uses millicpu (1 CPU = 1000) instead. | |
memory | number | Memory in MB. Defaults to 512. |
Network Configuration
Section titled “Network Configuration”vm -> network
Defines network attachments for the VM. This block can be repeated to attach the VM to multiple sandbox networks.
| Field | Required | Type | Description |
|---|---|---|---|
id | ✓ | reference to network | Reference to the ID of a network resource |
ip_ | string | Static IP address. Auto-assigned if not specified. | |
aliases | list(string) | DNS aliases for the VM on the selected network. |
The first network attachment is used for the VM default route and DNS configuration. Attach the VM to a network when you need terminal access, startup scripts, exec health checks, service tabs, or communication with other sandbox resources.
Disk Configuration
Section titled “Disk Configuration”vm -> disk
Configures additional disks that are attached to the VM and mounted at boot. This block can be repeated to attach multiple disks. The boot disk is sized by the image block.
| Field | Required | Type | Description |
|---|---|---|---|
destination | ✓ | string | Absolute path inside the VM where the disk is mounted |
size | ✓ | number | Disk size in GB |
Disk destinations must be absolute paths, must be unique, and cannot use reserved system paths such as /, /proc, /sys, /dev, /run, or /opt/instruqt.
Volume Configuration
Section titled “Volume Configuration”vm -> volume
Configures a host directory share attached to the VM. VM volumes are attached as guest-visible shares; mount or consume them from the guest according to your VM image and startup configuration.
| Field | Required | Type | Description |
|---|---|---|---|
source | ✓ | string | Host path or directory to share |
destination | ✓ | string | Intended path inside the VM |
type | string | Volume type | |
read_ | bool | Attach the share read-only. Defaults to false. |
Port Configuration
Section titled “Port Configuration”vm -> port
Defines port mappings between the VM and the host. This block can be repeated to expose multiple ports.
| Field | Required | Type | Description |
|---|---|---|---|
local | ✓ | string | Port the service listens on inside the VM |
host | string | Host port to bind. If omitted, Instruqt assigns a free host port. | |
protocol | string | Protocol: “tcp” or “udp”. Defaults to “tcp”. | |
open_ | string | Path to open in browser when a host port is defined |
Port Range Configuration
Section titled “Port Range Configuration”vm -> port_range
Defines port range mappings for exposing multiple consecutive ports. This block can be repeated for multiple port ranges.
| Field | Required | Type | Description |
|---|---|---|---|
range | ✓ | string | Port range, for example “3000-3010” |
enable_ | bool | Enable host port mapping. Defaults to false. | |
protocol | string | Protocol: “tcp” or “udp”. Defaults to “tcp”. |
Health Monitoring
Section titled “Health Monitoring”vm -> health_check
Configures health checks to monitor VM readiness and availability.
| Field | Required | Type | Description |
|---|---|---|---|
timeout | string | Health check timeout. Defaults to ”30s”. | |
http | block | HTTP health check (repeatable) | |
tcp | block | TCP health check (repeatable) | |
exec | block | Execute command health check (repeatable) |
HTTP Health Check
Section titled “HTTP Health Check”vm -> health_check -> http
| Field | Required | Type | Description |
|---|---|---|---|
address | ✓ | string | HTTP endpoint URL |
method | string | HTTP method. Defaults to “GET”. | |
body | string | Request body | |
headers | map(list(string)) | HTTP headers | |
success_ | list(number) | Expected success codes. Defaults to [200]. |
TCP Health Check
Section titled “TCP Health Check”vm -> health_check -> tcp
| Field | Required | Type | Description |
|---|---|---|---|
address | ✓ | string | TCP address to check |
Exec Health Check
Section titled “Exec Health Check”vm -> health_check -> exec
| Field | Required | Type | Description |
|---|---|---|---|
command | list(string) | Command to execute | |
script | string | Script to execute | |
exit_ | number | Expected exit code. Defaults to 0. |
Computed Attributes
Section titled “Computed Attributes”These attributes are set by the system after the VM is created:
| Field | Type | Description |
|---|---|---|
network[].assigned_address |
string | IP address assigned by the system |
port[].host |
string | Host port assigned by the system when omitted |
Default Values
Section titled “Default Values”The following defaults are applied during processing:
| Field | Default Value |
|---|---|
config.arch |
"x86_64" |
resources.cpu |
1 |
resources.memory |
512 |
health_check.timeout |
"30s" |
port.protocol |
"tcp" |
port_range.enable_host |
false |
port_range.protocol |
"tcp" |
Validation Rules
Section titled “Validation Rules”image.nameis required- If the
resourcesblock is set,cpuandmemorymust be greater than 0 - Disk
sizemust be greater than 0 - Disk
destinationmust be an absolute path, must be unique, and cannot use reserved system paths - Duplicate
network.idattachments are rejected - Port and port range values must parse as valid port numbers
- Startup scripts and exec health checks require the VM agent to be reachable, so the VM should be attached to a network
Examples
Section titled “Examples”Basic VM
Section titled “Basic VM”resource "vm" "ubuntu" { image { name = "ubuntu:24.04" }}VM with Network and Terminal Access
Section titled “VM with Network and Terminal Access”resource "network" "main" { subnet = "10.50.0.0/24"}
resource "vm" "devbox" { image { name = "ubuntu:24.04" }
resources { cpu = 2 memory = 2048 }
network { id = resource.network.main.meta.id ip_address = "10.50.0.10" }
startup_script = <<-EOF #!/bin/sh apt-get update apt-get install -y curl jq EOF}
resource "terminal" "devbox" { target = resource.vm.devbox shell = "/bin/bash"}VM with Disk and Service Tab
Section titled “VM with Disk and Service Tab”resource "network" "main" { subnet = "10.50.0.0/24"}
resource "vm" "web" { image { name = "ubuntu:24.04" }
disk { destination = "/var/lib/app" size = 20 }
port { local = "8080" host = "8080" }
network { id = resource.network.main.meta.id }
health_check { http { address = "http://localhost:8080/health" } }}
resource "service" "web" { target = resource.vm.web port = 8080}Best Practices
Section titled “Best Practices”- Choose the right compute type: Use VMs for full OS behavior and containers for lightweight application runtimes
- Attach a network for interactive labs: Terminal access, service tabs, startup scripts, and exec health checks depend on VM reachability
- Size resources deliberately: VMs need enough CPU and memory for the guest operating system and your lab workload
- Use explicit image tags: Pin images to stable versions for repeatable lab runs
- Make startup scripts idempotent: Startup scripts should handle reruns and partially completed setup safely
- Use disks for durable paths: Mount additional disks at application data paths and avoid reserved system paths
- Expose only needed ports: Map the smallest set of ports required for the learner experience