Yandex Cloud
Search
Contact UsGet started
  • Blog
  • Pricing
  • Documentation
  • All Services
  • System Status
    • Featured
    • Infrastructure & Network
    • Data Platform
    • Containers
    • Developer tools
    • Serverless
    • Security
    • Monitoring & Resources
    • ML & AI
    • Business tools
  • All Solutions
    • By industry
    • By use case
    • Economics and Pricing
    • Security
    • Technical Support
    • Customer Stories
    • Gateway to Russia
    • Cloud for Startups
    • Education and Science
  • Blog
  • Pricing
  • Documentation
Yandex project
© 2025 Yandex.Cloud LLC
Tutorials
    • All tutorials
    • Basic internet service architecture and protection
    • Cost analysis by resource using Object Storage
      • Getting started with Terraform
      • Terraform data sources
      • Uploading Terraform states to Object Storage
      • Getting started with Packer
      • Building a VM image with infrastructure tools using Packer
      • Locking Terraform states using Managed Service for YDB
      • Using Yandex Cloud modules in Terraform
      • Creating a VM and an instance group with a Container Optimized Image using Terraform
      • Transferring logs through Unified Agent HTTP input to Cloud Logging

In this article:

  • Get your cloud ready
  • Required paid resources
  • Set up your working environment
  • Prepare the image configuration
  • Build the image
  • Create a VM from the image
  • How to delete the resources you created
  1. Basic infrastructure
  2. Tools
  3. Building a VM image with infrastructure tools using Packer

Building a VM image with infrastructure tools using Packer

Written by
Yandex Cloud
Updated at May 7, 2025
  • Get your cloud ready
    • Required paid resources
  • Set up your working environment
  • Prepare the image configuration
  • Build the image
  • Create a VM from the image
  • How to delete the resources you created

In Yandex Compute Cloud, you can create a VM disk image with additional infrastructure tools using Packer.

Use Packer to build a VM image based on Ubuntu Linux 20.04 LTS, with its parameters specified in a configuration file. Add these tools commonly used with Yandex Cloud, to the image:

  • Yandex Cloud CLI 0.91.0 or higher
  • Terraform 1.1.9
  • kubectl 1.23
  • Docker 20.10.16 or higher
  • Git 2.25.1 or higher
  • Helm 3.9.0
  • jq 1.6 or higher
  • tree 1.8.0 or higher
  • gRPCurl 1.8.6
  • Pulumi 3.33.2
  • tmux 3.0a or higher

Using Packer, you will create an auxiliary VM that will have the required software installed on it. Next, you will build an image based on its boot disk. After that, you will delete the auxiliary VM and boot disk.

Follow the same steps to create your own image with all required software.

To build an image and create a VM from it:

  1. Get your cloud ready.
  2. Set up your working environment.
  3. Prepare the image configuration.
  4. Build the image.
  5. Create a VM from the image.

If you no longer need the resources you created, delete them.

Get your cloud readyGet your cloud ready

Sign up in Yandex Cloud and create a billing account:

  1. Navigate to the management console and log in to Yandex Cloud or register a new account.
  2. On the Yandex Cloud Billing page, make sure you have a billing account linked and it has the ACTIVE or TRIAL_ACTIVE status. If you do not have a billing account, create one and link a cloud to it.

If you have an active billing account, you can navigate to the cloud page to create or select a folder for your infrastructure to operate in.

Learn more about clouds and folders.

Required paid resourcesRequired paid resources

The cost of building a VM image and creating a VM from it includes:

  • Fee for storing built images (see Yandex Compute Cloud pricing).
  • Fee for VM computing resources (see Yandex Compute Cloud pricing).

Set up your working environmentSet up your working environment

  1. Install Packer:

    1. Download Packer and install it by following this guide on the Packer website.

      You can also download Packerfor your platform from this Yandex Cloud mirror.

    2. After the download is complete, add the path to the directory with the executable to the PATH variable. To do this, run this command:

      export PATH=$PATH:<Packer_executable_directory_path>
      

      Note

      Yandex Cloud requires Packer 1.5 or higher.

  2. Configure the Yandex Compute Builder plugin:

    1. Create a file named config.pkr.hcl with these contents:

      packer {
        required_plugins {
          yandex = {
            version = ">= 1.1.2"
            source  = "github.com/hashicorp/yandex"
          }
        }
      }
      
    2. Install the plugin:

      packer init <path_to_config.pkr.hcl>
      

      Result:

      Installed plugin github.com/hashicorp/yandex v1.1.2 in ...
      
  3. Install the Yandex Cloud CLI and create a profile.

  4. Get the info on the available subnets and availability zones. If you do not have any subnets, create one.

    CLI
    API
    • Run this command:

      yc vpc subnet list
      

      Result:

      +----------------------+----------------------+----------------------+----------------+---------------+-----------------+
      |          ID          |         NAME         |      NETWORK ID      | ROUTE TABLE ID |     ZONE      |      RANGE      |
      +----------------------+----------------------+----------------------+----------------+---------------+-----------------+
      | b0c29k6anelk******** | intro2-ru-central1-d | enp45glgitd6******** |                | ru-central1-d | [10.130.0.0/24] |
      | e2ltcj4urgpb******** | intro2-ru-central1-b | enp45glgitd6******** |                | ru-central1-b | [10.129.0.0/24] |
      | e9bn57jvjnbu******** | intro2-ru-central1-a | enp45glgitd6******** |                | ru-central1-a | [10.128.0.0/24] |
      +----------------------+----------------------+----------------------+----------------+---------------+-----------------+
      
    • From the ID column, save the ID of the subnet that will host the auxiliary VM for creating the image; from the ZONE column, save the appropriate availability zone. You will need these parameters later on.

    Use the list REST API method for the Subnet resource or the SubnetService/List gRPC API call.

  5. Specify the values of the variables required for building the image in the command line.

    export YC_FOLDER_ID=$(yc config get folder-id)
    export YC_ZONE="<availability_zone>"
    export YC_SUBNET_ID="<subnet_ID>"
    export YC_TOKEN=$(yc iam create-token)
    

    Where:

    • YC_FOLDER_ID: ID of the folder with the auxiliary VM used for creating the image. Its value gets populated automatically.
    • YC_ZONE: Availability zone that will host the auxiliary VM used for creating the image and that you got earlier.
    • YC_SUBNET_ID: ID of the subnet that will host the auxiliary VM used for creating the image and that you got earlier.
    • YC_TOKEN: IAM token required for creating VM images; it also gets populated automatically.
  6. Generate an SSH key pair. You will need these keys to create a VM and connect to it.

Prepare the image configurationPrepare the image configuration

  1. Create an HCL configuration file, such as toolbox.pkr.hcl.

  2. In the configuration file, describe the image parameters:

    # Yandex Cloud Toolbox VM Image based on Ubuntu 20.04 LTS
    #
    # Provisioner docs:
    # https://www.packer.io/docs/builders/yandex
    #
    
    variable "YC_FOLDER_ID" {
      type = string
      default = env("YC_FOLDER_ID")
    }
    
    variable "YC_ZONE" {
      type = string
      default = env("YC_ZONE")
    }
    
    variable "YC_SUBNET_ID" {
      type = string
      default = env("YC_SUBNET_ID")
    }
    
    variable "TF_VER" {
      type = string
      default = "1.1.9"
    }
    
    variable "KCTL_VER" {
      type = string
      default = "1.23.0"
    }
    
    variable "HELM_VER" {
      type = string
      default = "3.9.0"
    }
    
    variable "GRPCURL_VER" {
      type = string
      default = "1.8.6"
    }
    
    variable "GOLANG_VER" {
      type = string
      default = "1.17.2"
    }
    
    variable "PULUMI_VER" {
      type = string
      default = "3.33.2"
    }
    
    source "yandex" "yc-toolbox" {
      folder_id           = "${var.YC_FOLDER_ID}"
      source_image_family = "ubuntu-2004-lts"
      ssh_username        = "ubuntu"
      use_ipv4_nat        = "true"
      image_description   = "Yandex Cloud Ubuntu Toolbox image"
      image_family        = "my-images"
      image_name          = "yc-toolbox"
      subnet_id           = "${var.YC_SUBNET_ID}"
      disk_type           = "network-hdd"
      zone                = "${var.YC_ZONE}"
    }
    
    build {
      sources = ["source.yandex.yc-toolbox"]
    
      provisioner "shell" {
        inline = [
          # Global Ubuntu things
          "sudo apt-get update",
          "echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections",
          "sudo apt-get install -y unzip python3-pip python3.8-venv",
    
          # Yandex Cloud CLI tool
          "curl --silent --remote-name https://storage.yandexcloud.net/yandexcloud-yc/install.sh",
          "chmod u+x install.sh",
          "sudo ./install.sh -a -i /usr/local/ 2>/dev/null",
          "rm -rf install.sh",
          "sudo sed -i '$ a source /usr/local/completion.bash.inc' /etc/profile",
      
          # Docker
          "curl --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-keyring.gpg",
          "echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null",
          "sudo apt-get update",
          "sudo apt-get install -y docker-ce containerd.io",
          "sudo usermod -aG docker $USER",
          "sudo chmod 666 /var/run/docker.sock",
          "sudo useradd -m -s /bin/bash -G docker yc-user",
    
          # Docker Artifacts
          "docker pull hello-world",
          "docker pull -q amazon/aws-cli",
          "docker pull -q golang:${var.GOLANG_VER}",
    
          # Terraform (classic way)
          #"curl --fail --silent --show-error --location https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-keyring.gpg",
          #"echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/hashicorp.list > /dev/null",
          #"sudo apt-get update",
          #"sudo apt-get install -y terraform",
          #
          # Alternative Option
          "curl --silent --location https://hashicorp-releases.yandexcloud.net/terraform/${var.TF_VER}/terraform_${var.TF_VER}_linux_amd64.zip --output terraform.zip",
          "unzip terraform.zip",
          "sudo install -o root -g root -m 0755 terraform /usr/local/bin/terraform",
          "rm -rf terraform terraform.zip",
          # Terraform configuration file ?
          #"cat <<EOF > ~/.terraformrc \n provider_installation { network_mirror { url = \"https://terraform-mirror.yandexcloud.net/\" include = [\"registry.terraform.io/*/*\"] } direct { exclude = [\"registry.terraform.io/*/*\"] } } \n EOF",
    
          # kubectl
          "curl --silent --location --remote-name https://dl.k8s.io/release/v${var.KCTL_VER}/bin/linux/amd64/kubectl",
          "sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl",
          "rm -rf kubectl",
    
          # Helm
          "curl --silent --show-error --location --remote-name https://get.helm.sh/helm-v${var.HELM_VER}-linux-amd64.tar.gz",
          "tar zxf helm-v${var.HELM_VER}-linux-amd64.tar.gz",
          "sudo install -o root -g root -m 0755 linux-amd64/helm /usr/local/bin/helm",
          "rm -rf helm-v${var.HELM_VER}-linux-amd64.tar.gz",
          "rm -rf linux-amd64",
          # User can add own repo after login like this:
          # helm repo add stable https://charts.helm.sh/stable
    
          ## grpccurl
          "curl --silent --show-error --location --remote-name https://github.com/fullstorydev/grpcurl/releases/download/v${var.GRPCURL_VER}/grpcurl_${var.GRPCURL_VER}_linux_x86_64.tar.gz",
          "tar zxf grpcurl_${var.GRPCURL_VER}_linux_x86_64.tar.gz",
          "sudo install -o root -g root -m 0755 grpcurl /usr/local/bin/grpcurl",
          "rm -rf grpcurl_${var.GRPCURL_VER}_linux_x86_64.tar.gz",
          "rm -rf grpcurl",
    
          # Pulumi
          "curl --silent --show-error --location --remote-name https://get.pulumi.com/releases/sdk/pulumi-v${var.PULUMI_VER}-linux-x64.tar.gz",
          "tar zxf pulumi-v${var.PULUMI_VER}-linux-x64.tar.gz",
          "sudo cp pulumi/* /usr/local/bin/",
          "rm -rf pulumi-v${var.PULUMI_VER}-linux-x64.tar.gz",
          "rm -rf pulumi",
    
          # Other packages
          "sudo apt-get install -y git jq tree tmux",
    
          # Clean
          "rm -rf .sudo_as_admin_successful",
    
          # Test - Check versions for installed components
          "echo '=== Tests Start ==='",
          "yc version",
          "terraform version",
          "docker version",
          "kubectl version --client=true",
          "helm version",
          "grpcurl --version",
          "git --version",
          "jq --version",
          "tree --version",
          "pulumi version",
          "echo '=== Tests End ==='"
        ]
      }
    }
    

Warning

You cannot use both the provisioner "shell" and metadata parameters in the configuration file at the same time.

Build the imageBuild the image

  1. In the command line, navigate to the configuration file directory.

    cd <path_to_configuration_file_directory>
    
  2. Make sure the image configuration file is correct using this command:

    packer validate yc-toolbox.pkr.hcl
    

    Where yc-toolbox.pkr.hcl is the configuration file name.

    If the configuration is correct, you will get this message:

    The configuration is valid.
    
  3. Build the image with the command:

    packer build yc-toolbox.pkr.hcl
    

    Where yc-toolbox.pkr.hcl is the configuration file name.

  4. After the image is built, you will get a notification message:

    ...
    ==> Builds finished. The artifacts of successful builds are:
    --> yandex.yc-toolbox: A disk image was created: yc-toolbox (id: fd83j475posv********) with family name infra-images
    

    Save the ID of the image you built (id). Use this ID to create a VM later.

  5. Make sure you can see the image you built Yandex Cloud.

    CLI
    API

    Run this command:

    yc compute image list
    

    Result:

    +----------------------+------------+-----------+----------------------+--------+
    |          ID          |    NAME    |  FAMILY   |     PRODUCT IDS      | STATUS |
    +----------------------+------------+-----------+----------------------+--------+
    | fd83j475posv******** | yc-toolbox | my-images | f2ek1vhoppg2******** | READY  |
    +----------------------+------------+-----------+----------------------+--------+
    

    Use the list REST API method for the Image resource or the ImageService/List gRPC API call.

Create a VM from the imageCreate a VM from the image

  1. Specify the values of the variables required for creating your VM. To do this, run this command:

    export VM_NAME="<VM_name>"
    export YC_IMAGE_ID="<image_ID>"
    export YC_SUBNET_ID="<subnet_ID>"
    export YC_ZONE="<availability_zone>"
    

    Where:

    • VM_NAME: Name of the new VM.
    • YC_IMAGE_ID: ID of the image to create the VM from. You got this ID earlier.
    • YC_SUBNET_ID: ID of the subnet to host the VM. You got this ID earlier.
    • YC_ZONE: Availability zone for the VM that you got earlier.
  2. Create your VM from the image you built.

    CLI
    API

    Run this command:

    yc compute instance create \
      --name $VM_NAME \
      --hostname $VM_NAME \
      --zone=$YC_ZONE \
      --create-boot-disk size=20GB,image-id=$YC_IMAGE_ID \
      --cores=2 \
      --memory=8G \
      --core-fraction=100 \
      --network-interface subnet-id=$YC_SUBNET_ID,ipv4-address=auto,nat-ip-version=ipv4 \
      --ssh-key <path_to_public_part_of_SSH_key>
    

    Where:

    • --name: Name of the new VM.
    • --hostname: VM host name.
    • --zone: Availability zone.
    • --create-boot-disk: Boot disk properties: size stands for size, and image-id, for the ID of the image being used.
    • --cores: Number of vCPUs.
    • --memory: Amount of RAM.
    • --core-fraction: Basic vCPU performance in percentage.
    • --network-interface: Network interface proterties, where subnet-id is the subnet ID, ipv4-address, the internal IPv4 address, and nat-ip-version, the IP specification for the egress NAT.
    • --ssh-key: Public part of the SSH key.

    The command outputs info on the VM you created. Save the VM's public IP address:

    ...
    one_to_one_nat:
      address: 62.84.122.151
    ...
    

    Learn more about creating a VM from a custom image.

    Use the create REST API method for the Instance resource or the InstanceService/Create gRPC API call.

  3. Connect to the VM over SSH:

    ssh -i <path_to_private_part_of_SSH_key> yc-user@<VM_public_IP_address>
    

How to delete the resources you createdHow to delete the resources you created

To stop paying for the resources you created:

  • Delete the VM.
  • Delete the image.

Was the article helpful?

Previous
Getting started with Packer
Next
Locking Terraform states using Managed Service for YDB
Yandex project
© 2025 Yandex.Cloud LLC