# Creating a VM with a custom configuration script

You can create a VM with a preset software configuration by using the `user-data` key in the VM [metadata](../../concepts/vm-metadata.md).

The [cloud-init](https://cloudinit.readthedocs.io/en/latest/) agent running on the VM will process the configuration you set in the `user-data` key. `Cloud-init` supports different metadata transmission formats, e.g., [cloud-config](https://cloudinit.readthedocs.io/en/latest/reference/examples.html).

{% note info %}

To create, modify, and edit a [VM](../../concepts/vm.md), you need the `compute.editor` _minimum_ [role](../../security/index.md#compute-editor) for the [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder). To create a VM with a licensed image, you will additionally need the `license-manager.viewer` [role](../../../marketplace/security/index.md#license-manager-viewer).

To create a VM with a [public IP address](../../../vpc/concepts/address.md#public-addresses), you will additionally need the `vpc.publicAdmin` [role](../../../vpc/security/index.md#vpc-public-admin).

{% endnote %}

## Creating a VM with a custom configuration script {#create-vm-with-user-script}

{% note warning %}

In the `user-data` configuration, you must always set the user login and SSH key, even if you have already specified them under **Access** in the management console.

{% endnote %}

To create a VM with a custom configuration script:

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder to create your VM in.
  1. Navigate to **Compute Cloud**.
  1. In the left-hand panel, select ![image](../../../_assets/console-icons/server.svg) **Virtual machines**.
  1. Click **Create virtual machine**.
  1. [Set](create-linux-vm.md) the required VM parameters.
  1. Expand the **Metadata** section and specify:

      * **Key**: `user-data`.
      * **Value**: `cloud-config` configuration in YAML format. For `user-data` configuration examples, see [Examples](#examples).

  1. Click **Create VM**.

- CLI {#cli}

  If you do not have the Yandex Cloud CLI yet, [install and initialize it](../../../cli/quickstart.md#install).

  The folder used by default is the one specified when [creating](../../../cli/operations/profile/profile-create.md) the CLI profile. To change the default folder, use the `yc config set folder-id <folder_ID>` command. You can also specify a different folder for any command using `--folder-name` or `--folder-id`. If you access a resource by its name, the search will be limited to the default folder. If you access a resource by its ID, the search will be global, i.e., through all folders based on access permissions.

  Run this command:

  ```bash
  yc compute instance create \
    --name my-sample-instance \
    --zone ru-central1-a \
    --network-interface subnet-name=<subnet_name>,nat-ip-version=ipv4,security-group-ids=<security_group_ID> \
    --create-boot-disk image-folder-id=standard-images,image-family=ubuntu-2204-lts,kms-key-id=<key_ID> \
    --metadata-from-file user-data="<path_to_configuration_file>"
  ```

  Where:

  * `--name`: VM name. The naming requirements are as follows:

      * Length: between 3 and 63 characters.
      * It can only contain lowercase Latin letters, numbers, and hyphens.
      * It must start with a letter and cannot end with a hyphen.

      {% note info %}
      
      The VM name is used to generate an [internal FQDN](../../concepts/network.md#hostname), which is set only once, when you create the VM. If the internal FQDN is important to you, make sure to choose an appropriate name for your VM.
      
      {% endnote %}

  * `--zone`: [Availability zone](../../../overview/concepts/geo-scope.md) matching the selected subnet.
  * `--network-interface`: VM [network interface](../../concepts/network.md) settings:

      * `subnet-name`: Name of the [subnet](../../../vpc/concepts/network.md#subnet) in the [availability zone](../../../overview/concepts/geo-scope.md) specified in the `--zone` parameter.
      * `security-group-ids`: [Security group](../../../vpc/concepts/security-groups.md) ID.

  * `--create-boot-disk`: VM boot disk settings:

      * `image-family`: [Image family](../../concepts/image.md#family), e.g., `ubuntu-2204-lts`. This option allows you to install the latest version of the OS from the specified family.
      * `kms-key-id`: ID of the [KMS symmetric key](../../../kms/concepts/key.md) to create an encrypted boot disk. This is an optional setting.

        To create an encrypted disk, you need the `kms.keys.user` [role](../../../kms/security/index.md#kms-keys-user) or higher.
        
        {% note warning %}
        
        You can specify encryption settings only when creating a disk. You cannot disable or change disk encryption. You also cannot enable encryption for an existing disk.
        
        {% endnote %}

        If you deactivate the key used to encrypt a disk, image, or snapshot, access to the data will be suspended until you reactivate the key.
        
        {% note alert %}
        
        If you destroy the key or its [version](../../../kms/concepts/version.md) used to encrypt a disk, image, or snapshot, you will irrevocably lose access to the data. For details, see [Destroying key versions](../../../kms/concepts/version.md#version-distruct).
        
        {% endnote %}

  * `--metadata-from-file`: `user-data` key and its value, i.e., path to the `cloud-config` configuration file in YAML format, e.g., `--metadata-from-file user-data="/home/user/metadata.yaml"`.

      For `user-data` configuration examples, see [Examples](#examples).

  {% note info %}
  
  The commands [`yc compute instance create`](../../../cli/cli-ref/compute/cli-ref/instance/create.md) | [`create-with-container`](../../../cli/cli-ref/compute/cli-ref/instance/create-with-container.md) | [`update`](../../../cli/cli-ref/compute/cli-ref/instance/update.md) | [`add-metadata`](../../../cli/cli-ref/compute/cli-ref/instance/add-metadata.md) support substitution of environment variable values into VM metadata. When you execute a Yandex Cloud CLI command, these values, specified in the `user-data` key in `$<variable_name>` format, will be substituted into the VM metadata from the environment variables of the environment the command is executed in. 
  
  To change such behavior, i.e. to provide a variable name to the VM metadata in `$<variable_name>` format rather than take the variable value from the CLI command runtime environment, use the two-dollar syntax, e.g., `$$<variable_name>`.
  
  For more information, see [Specifics of providing environment variables in metadata via the CLI](../../concepts/metadata/sending-metadata.md#environment-variables).
  
  {% endnote %}

- Terraform {#tf}

  If you do not have Terraform yet, [install it and configure the Yandex Cloud provider](../../../tutorials/infrastructure-management/terraform-quickstart.md#install-terraform).
  
  
  To manage infrastructure using Terraform under a service account or user accounts (a Yandex account, a federated account, or a local user), [authenticate](../../../terraform/authentication.md) using the appropriate method.

  Add the VM configuration script to the `metadata` section of the `yandex_compute_instance` resource in the Terraform configuration file:

  ```hcl
  resource "yandex_compute_instance" "vm-1" {
    name        = "my-sample-instance"
    ...
    metadata    = {
      user-data = "${file("<path_to_configuration_file>")}"
    }
    ...
  }
  ```

  Where:
  * `user-data`: Path to the `cloud-config` configuration file in YAML format, e.g., `user-data = "${file("/home/user/metadata.yaml")}"`.
  
      For `user-data` configuration examples, see [Examples](#examples).

- API {#api}

  To create a VM, use the [create](../../api-ref/Instance/create.md) REST API method for the [Instance](../../api-ref/Instance/index.md) resource and provide the object with the `cloud-config` YAML configuration under `metadata` in the request body. For a multiline configuration, use `\n` as a separator. Here is an example:

  ```json
  {
    "folderId": "b1gvmob95yys********",
    "name": "my-sample-instance",
    "zoneId": "ru-central1-a",
    "platformId": "standard-v3",
    ...
    "metadata": {
      "user-data": "#cloud-config\ndatasource:\n  Ec2:\n    strict_id: false\nssh_pwauth: yes\nusers:\n- name: <username>\n  sudo: 'ALL=(ALL) NOPASSWD:ALL'\n  shell: /bin/bash\n  ssh_authorized_keys:\n  - <public_SSH_key>\nwrite_files:\n  - path: '/usr/local/etc/startup.sh'\n    permissions: '755'\n    content: |\n      #!/bin/bash\n      apt-get update\n      apt-get install -y nginx\n      service nginx start\n      sed -i -- 's/ nginx/ Yandex Cloud - ${HOSTNAME}/' /var/www/html/index.nginx-debian.html\n    defer: true\nruncmd:\n  - ['/usr/local/etc/startup.sh']"
    },
    ...
  }
  ```

  For `user-data` configuration examples, see [Examples](#examples).

{% endlist %}

For more information about creating a VM, see [Creating a VM from a public Linux image](create-linux-vm.md).

To make sure the configuration scripts ran successfully, [get the serial port output](../vm-info/get-serial-port-output.md) of your VM.

## Examples {#examples}

{% list tabs %}

- Nginx

  To install and run an [Nginx](https://nginx.org/) web server on the new VM, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: 'ALL=(ALL) NOPASSWD:ALL'
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  write_files:
    - path: "/usr/local/etc/startup.sh"
      permissions: "755"
      content: |
        #!/bin/bash

        apt-get update
        apt-get install -y nginx
        service nginx start
        sed -i -- "s/ nginx/ Yandex Cloud - ${HOSTNAME}/" /var/www/html/index.nginx-debian.html
      defer: true
  runcmd:
    - ["/usr/local/etc/startup.sh"]
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

- Docker

  To install [Docker](https://www.docker.com) on the new VM and to upload and run a container, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  write_files:
    - path: "/usr/local/etc/docker-start.sh"
      permissions: "755"
      content: |
        #!/bin/bash

        # Docker
        echo "Installing Docker"
        sudo apt update -y && sudo apt install docker.io -y
        echo "Grant user access to Docker"
        sudo usermod -aG docker ${USER}
        newgrp docker

      defer: true
    - path: "/usr/local/etc/docker-main.sh"
      permissions: "755"
      content: |
        #!/bin/bash

        # Docker run container
        docker pull hello-world:latest
        docker run hello-world

      defer: true
  runcmd:
    - [su, <username>, -c, "/usr/local/etc/docker-start.sh"]
    - [su, <username>, -c, "/usr/local/etc/docker-main.sh"]
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

- Yandex Cloud CLI

  To install the [Yandex Cloud CLI](../../../cli/quickstart.md) on the new VM, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: 'ALL=(ALL) NOPASSWD:ALL'
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  write_files:
    - path: "/usr/local/etc/yc-install.sh"
      permissions: "0755"
      owner: <username>:<username>
      content: |
        #!/bin/bash

        # CLI
        echo "Installing Yandex Cloud CLI"
        curl \
          --silent \
          --show-error \
          --location \
          https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash
        VM_ID=$(curl --silent http://169.254.169.254/latest/meta-data/instance-id)

        # Save YC params
        echo "Saving YC params to the ~/.bashrc"
        cat << EOF >> $HOME/.bashrc
          export PATH="$HOME/yandex-cloud/bin:$PATH"
          export YC_CLI_VM_ID="${VM_ID:-unknown}"
        EOF
      defer: true
  runcmd:
    - [su, <username>, -c, "/usr/local/etc/yc-install.sh"]
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

- Yandex Unified Agent

  {% note info %}

  When creating a VM with [Yandex Unified Agent](../../../monitoring/concepts/data-collection/unified-agent/index.md), link it to a [service account](../../../iam/concepts/users/service-accounts.md) with the `monitoring.editor` [role](../../../monitoring/security/index.md#monitoring-editor) for the current [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder).

  {% endnote %}

  To install Unified Agent on the new VM, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: 'ALL=(ALL) NOPASSWD:ALL'
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  runcmd:
    - wget -O - https://monitoring.api.cloud.yandex.net/monitoring/v2/unifiedAgent/config/install.sh | bash
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

  To link a service account to a VM when creating it using Terraform, add the following row to the configuration:
  
  ```hcl
  resource "yandex_compute_instance" "my-vm" {
    ...
    service_account_id = "ajehka*************"
  }
  ```

  For Unified Agent to send metrics to Yandex Managed Service for Prometheus®, specify the workspace ID in the configuration:
  
  ```hcl
  resource "yandex_compute_instance" "my-vm" {
    ...
    metadata    = {
      monitoring_workspaceid = "mon618clr**************"
    }
  }
  ```

- Terraform

  To install [Terraform](https://www.terraform.io/) on the new VM, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: 'ALL=(ALL) NOPASSWD:ALL'
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  write_files:
    - path: "/usr/local/etc/tf-install.sh"
      permissions: "755"
      content: |
        #!/bin/bash

        # Install 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

        # Install Terraform
        echo "Installing Terraform"
        sudo curl \
          --silent \
          --show-error \
          --location \
          https://hashicorp-releases.yandexcloud.net/terraform/1.8.5/terraform_1.8.5_linux_amd64.zip \
          --output /usr/local/etc/terraform.zip
        sudo unzip /usr/local/etc/terraform.zip -d /usr/local/etc/
        sudo install -o root -g root -m 0755 /usr/local/etc/terraform /usr/local/bin/terraform
        sudo rm -rf /usr/local/etc/terraform /usr/local/etc/terraform.zip /usr/local/etc/LICENSE.txt
      defer: true
  runcmd:
    - [su, <username>, -c, "/usr/local/etc/tf-install.sh"]
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

- kubectl

  To install [kubectl](https://kubernetes.io/docs/reference/kubectl/) on the new VM, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: 'ALL=(ALL) NOPASSWD:ALL'
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  write_files:
    - path: "/usr/local/etc/kubectl-install.sh"
      permissions: "755"
      content: |
        #!/bin/bash

        # Install kubectl
        echo "Installing kubectl"
        sudo curl \
          --silent \
          --show-error \
          --location \
          https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl \
          --output /usr/local/etc/kubectl
        sudo install -o root -g root -m 0755 /usr/local/etc/kubectl /usr/local/bin/kubectl
        sudo rm -rf /usr/local/etc/kubectl
      defer: true
  runcmd:
    - [su, <username>, -c, "/usr/local/etc/kubectl-install.sh"]
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

- Helm

  To install [Helm](https://helm.sh/) on the new VM, specify the following value for the `user-data` key:

  ```yaml
  #cloud-config
  datasource:
    Ec2:
      strict_id: false
  ssh_pwauth: no
  users:
  - name: <username>
    sudo: 'ALL=(ALL) NOPASSWD:ALL'
    shell: /bin/bash
    ssh_authorized_keys:
    - <public_SSH_key>
  write_files:
    - path: "/usr/local/etc/helm-install.sh"
      permissions: "755"
      content: |
        #!/bin/bash

        # Install Helm
        echo "Installing Helm"
        sudo curl \
          --silent \
          --show-error \
          --location \
          https://get.helm.sh/helm-v3.15.2-linux-amd64.tar.gz \
          --output /usr/local/etc/helm-v3.15.2-linux-amd64.tar.gz
        sudo tar xf /usr/local/etc/helm-v3.15.2-linux-amd64.tar.gz -C /usr/local/etc/
        sudo install -o root -g root -m 0755 /usr/local/etc/linux-amd64/helm /usr/local/bin/helm
        sudo rm -rf /usr/local/etc/helm-v3.15.2-linux-amd64.tar.gz /usr/local/etc/linux-amd64
      defer: true
  runcmd:
    - [su, <username>, -c, "/usr/local/etc/helm-install.sh"]
  ```

  Where:
  * `<username>`: User login to use when [connecting](../vm-connect/ssh.md) to the VM via SSH.
  * `<public_SSH_key>`: Contents of the [public key](../vm-connect/ssh.md#creating-ssh-keys) file. You need to [create](../vm-connect/ssh.md#creating-ssh-keys) a key pair for the SSH connection yourself.

{% endlist %}

#### See also {#see-also}

Other configuration examples for the `user-data` key:

* [Creating an instance group connected to a file storage](../instance-groups/create-with-filesystem.md)
* [Creating an instance group connected to Yandex Object Storage](../instance-groups/create-with-bucket.md)
* [Recovering VM network interfaces](../../qa/troubleshooting.md#unable-to-connect-to-new-multi-interface-vm)
* [Associating a Yandex Cloud Backup policy to a VM automatically](../../../tutorials/archive/vm-with-backup-policy/index.md)
* [Installing the agent for collecting Unified Agent metrics and logs](../../../monitoring/concepts/data-collection/unified-agent/installation.md#setup)
* [Installing the agent for collecting metrics in Prometheus format](../../../monitoring/operations/prometheus/ingestion/prometheus-agent.md)