> For the complete documentation index, see [llms.txt](https://docs.instruqt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.instruqt.com/sandboxes/hosts/create-a-custom-vm-image.md).

# Custom VM images

Create custom VM images for use in your sandboxes.

This guide explains how to create a custom **Virtual machine (VM)** image and use it as a sandbox host in a track.

## Advantages of custom VMs

#### Faster and more reliable

The main benefit of using a custom VM image is that it allows you to pre-install your software and the files needed for a track. Your track will start faster, since software does not need to be installed as part of the track setup script.

#### Increased image size

Custom images can be have any disk size you like and are not restricted to the default 10GB that ships with the stock Instruqt images.

## Custom VM image requirements

For custom VM images to work as part of an Instruqt sandbox environment, the following requirements need to be met:

* Google guest environment needs to be installed
  * The guest environment is used to bootstrap the VM into the sandbox environment, by injecting a metadata startup script.
* Allows TCP traffic to the Instruqt agent listening on ports 15778 and 15779
  * These ports are used to facilitate [Terminal and Editor tabs](/tracks/challenges/challenge-tabs.md) for end users, as well as running [lifecycle scripts](/sandboxes/lifecycle-scripts.md) by the platform.

## Prepare the VM before you save the image

Every sandbox boots a copy of your image on a new VM, with a new IP address. Anything you started while customizing the image is frozen into it as it was on the build VM: cluster databases, generated certificates and keys, node identities, and configuration that points at the build VM's IP address. Software that reads this state on boot tries to reach the old address, or presents the same identity in every sandbox. Sandboxes then get slow or fail to set up, often only some of them and only when many start at once.

Install your software and pull its container images into the image, but don't initialize or start it there. Either reset its runtime state as the last step before you save the image, or leave it uninitialized and start it from your track's [setup script](/sandboxes/lifecycle-scripts.md).

Reset state like this before saving:

* **Kubernetes clusters** (k3s, kubeadm, MicroK8s, minikube, kind): the cluster datastore, certificates, and kubeconfig files.
* **Other clustered software** (Docker Swarm, Consul, Nomad, Vault, Elasticsearch, Kafka, replicated databases): node IDs, cluster membership, and data directories.
* **Generated credentials**: TLS certificates and keys, API tokens, and license activations tied to one machine.
* **Machine identity**: `/etc/machine-id`.
* **Hardcoded addresses**: the build VM's IP address in configuration files or `/etc/hosts`.

For example, to reset k3s while keeping the container images you pulled:

```bash
/usr/local/bin/k3s-killall.sh
rm -rf /var/lib/rancher/k3s/server /etc/rancher/k3s/k3s.yaml
truncate -s 0 /etc/machine-id
```

k3s creates a fresh cluster with new certificates on the next boot. If your track deploys workloads such as Helm charts onto the cluster, deploy them from the setup script, not into the image.

{% hint style="info" %}
The Instruqt k3s images (`instruqt/k3s-*`) ship with k3s installed but never started, so they contain no cluster state. If you build on top of one and start k3s while customizing, reset it as shown above before saving.
{% endhint %}

## Create custom VM images

There are two ways to create a custom VM image:

* [Using the Instruqt Host images](#instruqt-host-images)
* [Using your own GCP project](#using-your-own-gcp-project)

{% hint style="info" %}
We recommend using Instruqt Host images if you are not familiar with Google Cloud, or want to avoid the complexity of managing a cloud account.
{% endhint %}

### Instruqt Host images

Instruqt Host images allow you to create VM images directly inside your Instruqt team. You create Host images using the Web UI.

{% tabs %}
{% tab title="🌐 Web UI" %}

1. Click **Settings** → **Host images**.
2. Click **Create image** in the upper-right corner.
3. On the *Image* details page, enter an image name, and optionally provide a description and disk size (the default disk is 10GB).
4. Select a base image. You can use an Instruqt preset image, or choose your own.
   * Custom image names follow a `PROJECT_ID/IMAGE_NAME` format.
   * [Here is a helpful link for finding public images](https://cloud.google.com/compute/docs/images#os-compute-support)
5. At the bottom of the page, click **Next.**
6. On the *Customize your image* page, use the terminal and code editor tabs to edit your image by running install commands, adding files, etc.
7. [Reset any runtime state](#prepare-the-vm-before-you-save-the-image), then click **Save** in the upper-right corner.
8. You can now reference your image in your sandboxes.
   * Instruqt host images follow a `TEAM_NAME/IMAGE_NAME` format.
     {% endtab %}
     {% endtabs %}

{% hint style="info" %}
Need to edit an existing host image? Open the *Host images* page. Select the image you want to edit. Click **Edit** in the top right corner, and then follow Step 3-7 again.
{% endhint %}

### Using your own GCP project

#### Before you begin

You should have a **Google Cloud Platform (GCP)** project available or have your GCP admin [create a GCP project](https://developers.google.com/workspace/marketplace/create-gcp-project) for you.

{% hint style="warning" %}
**Importing an existing image?**\
If you are importing an existing image to GCP make sure to install the [Google Guest Environment](https://cloud.google.com/compute/docs/images/install-guest-environment) in your image. Instruqt requires the guest environment to bootstrap VMs.
{% endhint %}

{% tabs %}
{% tab title="☁️ Google Cloud Console" %}

#### Step 1: Create a VM

1. Open your browser and navigate to your GCP project on the Google Cloud Platform.
2. Click **Compute Engine** → **VM instances**.
3. Click **\[+] CREATE INSTANCE**.
4. In the **Name** field, enter a name of your liking (ex: **my-build-instance**).
5. Click **Change** under *Boot disk*.
6. In the **Operating system** list, select your desired OS (ex: **Ubuntu**).
7. In the **Version** list, select the desired OS version (ex: **Ubuntu 23.04 LTS**).
8. Click **Select**.
9. Click **Create**.

#### Step 2: Customize the VM

1. Click **SSH** on the VM instance you created in step 1.\
   ↳ This opens a new `SSH-IN-BROWSER` window.
2. Modify your VM by running whatever commands you'd like.
3. [Reset any runtime state](#prepare-the-vm-before-you-save-the-image) you created while customizing.
4. Close the terminal.
5. Click ••• on your newly created VM instance, followed by **Stop**. And click **Stop** to confirm.

#### Step 3: Create an image

1. Under **Storage**, click **Images** on the navigation menu.
2. Click **\[+] Create Image.**
3. In the **Name** field, enter a name of your liking (ex: **ubuntu23-custom**).
4. In the **Source Disk** list, select the disk from your instance (ex: **my-build-instance**).
5. For **Location**, choose **Regional.**
6. In the **Select location** list, select **europe-west1 (Belgium)**.
7. Click **Create.**\
   ↳ The custom image is built for you and shown on the *Image page*. Your custom image is ready when the *Status* column shows a green checkmark.

{% hint style="danger" %}
In Google Cloud there are *Disk Images* and *Machine Images*. In this guide, we only work with *Disk Images.* **Machine Images will not work in Instruqt.**
{% endhint %}

#### Step 4: Grant permissions to Instruqt

1. Click **IAM & Admin** → **IAM** on the navigation menu.
2. Click **+ Grant access** to add a new principal.
3. In the **New principals** field, enter the following:\
   `instruqt-track@instruqt-prod.iam.gserviceaccount.com`
4. In the **Select a role** list, select `Compute Image User`
5. Click **Save**.
   {% endtab %}
   {% endtabs %}

#### Step 5: Use the custom VM image in your track

You are ready to incorporate the custom VM into your track.

Head over to your Web UI or Instruqt CLI to incorporate the custom VM image into your track:

{% tabs %}
{% tab title="🌐 Web UI" %}

1. Open your track from your *Content*.
2. In the **Sandbox** section, click **Edit**.
3. Click **+ Add a host**.
4. Select the **Virtual machine** host type.
5. In the **Hostname** field, enter a name of your liking (ex: `workstation`).
6. Select the **Choose your own** image type.
7. In the **GCP compute image** field, enter `<YOUR-GCPPROJECT-ID>/<YOUR-IMAGE-NAME>`
8. Select a machine type.
9. Click **Show optional settings**.
10. In the **Shell** field, enter `/bin/bash`
11. Click **Save host.**
    {% endtab %}

{% tab title="💻 Instruqt CLI" %}

1. Open the `config.yml` file of your track in your code editor.
2. Enter or adjust the following code into the `config.yml` file:

   ```
   virtualmachines:
   - name: workstation
     image: YOUR_GCPPROJECT_ID/YOUR_IMAGE_NAME
     shell: /bin/bash
     machine_type: n1-standard-1
   ```

   ⇨ Replace `YOUR_GCPPROJECT_ID` and `YOUR_IMAGE_NAME` with your GCP project id and image name (ex: `root-fort-99999/ubuntu22-openjdk11).`
3. Save the `config.yml` file and [deploy your track](broken://pages/h7G8pDJKT6sExCyrnOkM) to the Instruqt platform.

Now you can add a Terminal tab to a challenge and refer in this tab to the new host.
{% endtab %}
{% endtabs %}

#### Video tutorial

Watch the video tutorial on how to use your own GCP project to host VM images.

{% embed url="<https://drive.google.com/file/d/1hwgdx8PDMUKr5Ao_Rsm1_JhIYZOGzjkO/view?usp=sharing>" %}
Video walkthrough of creating a custom image.
{% endembed %}

{% hint style="info" %}
**Automate image building**

[Packer](https://www.packer.io/) is an open-source tool for creating identical VM images for multiple platforms from a single source configuration. See the [Google Cloud documentation](https://cloud.google.com/build/docs/building/build-vm-images-with-packer) for how to build VM images using Packer.
{% endhint %}
