---
title: "User data on a dedicated server"
sidebar_label: "User data"
sidebar_position: 9
description: "How to specify custom configuration parameters during auto-installation of the operating system, user data examples"
toc_max_heading_level: 3
---

import Formbricks from '@theme/MDXComponents/Formbricks'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import {TabItemLabel} from '@selectel/docux/components'
import CopyIcon from '@selectel/docux/icons/copy'

# User data on a dedicated server

User data are user-defined operating system configuration parameters for a server. They are described as scripts in cloud-config format (text files with YAML syntax) or as a bash script. The scripts are automatically encoded in Base64, transferred to the server, and executed by the cloud-init agent upon the first OS boot. Using user data helps automate server configuration.

[Specify user data](#enter-user-data) during the operating system installation.

For more information about cloud-config and bash script formats, see the [User data formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) guide in the cloud-init documentation.

Scripts can be used to pass individual operating system configuration parameters or entire sequences of parameters. For example:

* [set the time zone](#configure-time-zone);
* [create a directory and upload files to it](#create-directory);
* [update repositories and install packages](#update-repositories-and-packages);
* [place an SSH key on the server](#create-and-place-ssh-key);
* [configure the domain name resolver configuration file resolv.conf](#configure-configuration-file).

See other examples in the [Cloud config examples](https://cloudinit.readthedocs.io/en/latest/reference/examples.html#cloud-config-examples) guide in the cloud-init documentation.

## Specify user data \{#enter-user-data}

You can specify user data only during [OS autoinstallation](/dedicated/manage/autoinstall-os.mdx) on Linux-based distributions. Enter the script text in the **User data** field.

Once the automatic installation is complete, the text in the **User data** field cannot be changed.

The maximum size of a script containing data not encoded in Base64 is 16 KB.

## User data examples \{#user-data-script-examples}

### Set the time zone \{#configure-time-zone}

<Tabs queryString="configure-time-zone">
  <TabItem value="cloud-config" default>
    <TabItemLabel>
      Cloud-config
    </TabItemLabel>

    Example script to set the Europe/Moscow time zone:

    ```yaml
    #cloud-config

    timezone: Europe/Moscow
    ```
  </TabItem>

  <TabItem value="bash">
    <TabItemLabel>
      Bash script
    </TabItemLabel>

    Example script to set the Europe/Moscow time zone:

    ```bash
    #!/bin/bash

    timedatectl set-timezone Europe/Moscow
    ```
  </TabItem>
</Tabs>

### Create a directory and upload files to it \{#create-directory}

<Tabs queryString="create-directory">
  <TabItem value="cloud-config" default>
    <TabItemLabel>
      Cloud-config
    </TabItemLabel>

    Example script to create a directory and upload a file to it over the network:

    ```yaml
    #cloud-config

    runcmd:
    - mkdir <directory>
    - [ wget, "<url>", -O, <directory>/<file_name> ]
    ```

    Specify:

    * `<directory>` — a directory on the server, for example `/run/newdir`;
    * `<url>` — the URL to the file, for example `https://repo.local/static/page.html`;
    * `<file_name>` — the name under which the file will be saved in the directory, for example `index.html`.
  </TabItem>

  <TabItem value="bash">
    <TabItemLabel>
      Bash script
    </TabItemLabel>

    Example script to create a directory and upload a file to it over the network:

    ```bash
    #!/bin/bash
    mkdir <directory>
    wget <url> -O <directory>/<file_name>
    ```

    Specify:

    * `<directory>` — a directory on the server, for example `/run/newdir`;
    * `<url>` — the URL to the file, for example `https://repo.local/static/page.html`;
    * `<file_name>` — the name under which the file will be saved in the directory, for example `index.html`.
  </TabItem>
</Tabs>

### Update repositories and install packages \{#update-repositories-and-packages}

<Tabs queryString="update-repositories-and-packages">
  <TabItem value="cloud-config" default>
    <TabItemLabel>
      Cloud-config
    </TabItemLabel>

    Example script for installing packages:

    * `pwgen` — a utility for generating random passwords;
    * `pastebinit` — a command-line tool for publishing text (such as command outputs, logs, etc.) to online services from the terminal.

    ```yaml
    #cloud-config

    package_update: true
    packages:
    - pwgen
    - pastebinit
    ```
  </TabItem>

  <TabItem value="bash">
    <TabItemLabel>
      Bash script
    </TabItemLabel>

    Example script for installing packages:

    * `pwgen` — a utility for generating random passwords;
    * `pastebinit` — a command-line tool for publishing text from the terminal to online services, such as command outputs, logs, etc.

    ```bash
    #!/bin/bash
    apt update
    apt install pwgen pastebinit
    ```
  </TabItem>
</Tabs>

### Place an SSH key on the server \{#create-and-place-ssh-key}

<Tabs queryString="create-and-place-ssh-key">
  <TabItem value="cloud-config" default>
    <TabItemLabel>
      Cloud-config
    </TabItemLabel>

    An example of a script for adding two SSH keys to a server. The key will be added to the OS user; by default, this is the `root` user, in the `~/.ssh/authorized_keys` directory.

    ```yaml
    #cloud-config

    ssh_authorized_keys:
     - ssh-rsa <ssh_key_user_1> <user_name_1>@<host_name_1>
     - ssh-rsa <ssh_key_user_2> <user_name_2>@<host_name_2>
    ```

    Specify:

    * `<ssh_key_user_1>` — the public SSH key of the first user, for example `AAAAB3N…V7NZ`;
    * `<user_name_1>@<host_name_1>` — the comment for the first user's SSH key, where:
      * `<user_name_1>` — the name of the first user who generated the SSH key;
      * `<host_name_1>` — the name of the device on which the SSH key was generated;
    * `<ssh_key_user_2>` — the public SSH key of the second user, for example `AAAAB3N…NtHw==`;
    * `<user_name_2>@<host_name_2>` — the comment for the second user's SSH key, where:
      * `<user_name_2>` — the name of the second user who generated the SSH key;
      * `<host_name_2>` — the name of the device on which the SSH key was generated.
  </TabItem>

  <TabItem value="bash">
    <TabItemLabel>
      Bash script
    </TabItemLabel>

    An example of a script for adding two SSH keys to a server. The key will be added to the OS user; by default, this is the `root` user, in the `~/.ssh/authorized_keys` directory.

    ```bash
    #!/bin/bash
    echo "ssh-rsa <ssh_key_user_1> <user_name_1>@<host_name_1>" >> /root/.ssh/authorized_keys
    echo "ssh-rsa <ssh_key_user_2> <user_name_2>@<host_name_2>" >> /root/.ssh/authorized_keys
    ```

    Specify:

    * `<ssh_key_user_1>` — the public SSH key of the first user, for example `AAAAB3N…V7NZ`;
    * `<user_name_1>@<host_name_1>` — the comment for the first user's SSH key, where:
      * `<user_name_1>` — the name of the first user who generated the SSH key;
      * `<host_name_1>` — the name of the device on which the SSH key was generated;
    * `<ssh_key_user_2>` — the public SSH key of the second user, for example `AAAAB3N…NtHw==`;
    * `<user_name_2>@<host_name_2>` — the comment for the second user's SSH key, where:
      * `<user_name_2>` — the name of the second user who generated the SSH key;
      * `<host_name_2>` — the name of the device on which the SSH key was generated.
  </TabItem>
</Tabs>

### Configure the configuration file \{#configure-configuration-file}

<Tabs queryString="configure-configuration-file">
  <TabItem value="cloud-config" default>
    <TabItemLabel>
      Cloud-config
    </TabItemLabel>

    Example script for the domain name resolver `resolv.conf`:

    ```yaml
    #cloud-config

    manage_resolv_conf: true
    resolv_conf:
     nameservers: ['<dns_server_ip_address_1>', '<dns_server_ip_address_2>']
     searchdomains:
       - <searchdomain_1>
       - <searchdomain_2>
     domain: <domain>
     options:
       rotate: true
       timeout: 1
    ```

    Specify:

    * `<dns_server_ip_address_1>`, `<dns_server_ip_address_2>` — IP addresses of the DNS servers that the system will use to resolve domain names, for example `4.4.4.4`` and 8.8.8.8`;
    * `<searchdomain_1>`, `<searchdomain_2>` — domains to be appended to short (incomplete) hostnames when accessed;
    * `<domain>` — (legacy) the main DNS domain to be appended to short (incomplete) hostnames when accessed.
  </TabItem>

  <TabItem value="bash">
    <TabItemLabel>
      Bash script
    </TabItemLabel>

    Example script for the domain name resolver `resolv.conf`:

    ```bash
    #!/bin/bash
    cat <<EOF > /etc/resolv.conf
    domain <domain>
    nameserver <dns_server_ip_address_1>
    nameserver <dns_server_ip_address_2>
    search <searchdomain_1> <searchdomain_2>
    options rotate
    options timeout:1
    EOF
    ```

    Specify:

    * `<domain>` — (legacy) the main DNS domain to be appended to short (incomplete) hostnames when accessed;
    * `<dns_server_ip_address_1>`, `<dns_server_ip_address_2>` — IP addresses of the DNS servers that the system will use to resolve domain names, for example `4.4.4.4`` and 8.8.8.8`;
    * `<searchdomain_1>`, `<searchdomain_2>` — domains to be appended to short (incomplete) hostnames when accessed.
  </TabItem>
</Tabs>

### Disable internet access \{#disable-internet-access}

<Tabs queryString="disable-internet-access">
  <TabItem value="bash" default>
    <TabItemLabel>
      Bash script
    </TabItemLabel>

    Example script to turn off a network interface with a public IPv4 address:

    ```bash
    #!/bin/bash
    ip addr show
    public_interface=$(ip -4 addr show | awk '/inet/ && !/127.0.0.1/ && !/10\./ && !/172\.(1[6-9]|2[0-9]|3[0-1])\./ && !/192\.168\./ {print $NF}')
    if [ -n "$public_interface" ]; then
        ip link set down dev "$public_interface"
    else
        echo "Public interface not found."
    fi
    ```
  </TabItem>

  <TabItem value="python">
    <TabItemLabel>
      Python script
    </TabItemLabel>

    Example script to turn off a network interface with a public IPv4 address:

    ```python
    #!/usr/bin/env python3
    import subprocess
    import re
    import logging
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
    def disable_public_interface():
        logging.info('Starting disable_public_interface function.')
        output = subprocess.check_output('ip -4 addr show', shell=True).decode('utf-8')
        interfaces = re.findall(r'^\d+: (\S+):.*?\n(?:.*\n)*?\s+inet (\d+\.\d+\.\d+\.\d+)/\d+', output, re.MULTILINE)
        public_interfaces = []
        for iface, ip in interfaces:
            if iface == 'lo':
                continue
            if re.match(r'^(127\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)', ip):
                continue
            public_interfaces.append(iface)
        for interface in public_interfaces:
            command = ['ip', 'link', 'set', 'dev', interface, 'down']
            try:
                subprocess.run(command, check=True)
                logging.info(f'Successfully disabled interface: {interface}')
            except subprocess.CalledProcessError as e:
                logging.error(f'Failed to disable interface: {interface}, error: {e}')
    if __name__ == "__main__":
        logging.info('Script started.')
        disable_public_interface()
        logging.info('Script finished.')
    ```
  </TabItem>
</Tabs>

### Configure container configurations for installing an OS with the Containers Ready application \{#containers-ready}

When installing an [OS with the Containers Ready application](/dedicated/manage-applications/containers-ready.mdx), you can use the script in the **User data** field to configure containers. To access the Portainer panel via a domain, insert the following script in the **User data** field:

```bash
#cloud-config

write_files:
  - path: "/opt/containers/docker-compose.yaml"
    permissions: "0644"
    content: |
      version: "3.9"
      services:
        <containers>

  - path: "/opt/containers/.env"
    permissions: "0644"
    content: |
      <environment_variables>

  - path: "/opt/user-values.yaml"
    permissions: "0644"
    content: |
      portainer_use_le: true
      portainer_domain: "<example.com>"
      portainer_le_email: "<root@example.com>"
```

Specify:

* `<containers>` — content of the Docker Compose file for the `docker-compose.yaml` file. For more details, see the [docker compose](https://docs.docker.com/reference/cli/docker/compose/#examples) instructions in the Docker documentation; ;
* `<environment_variables>` — environment variables for the `.env` file. If the file is not needed, delete the code block. For more details, see the [Use environment variables](https://docs.docker.com/compose/how-tos/environment-variables/) instructions in the Docker documentation; ;
* in the `content:` code block for the `/opt/user-values.yaml` file, specify the configuration parameters for Portainer:

  * `portainer_use_le: true` — a parameter for automatic TLS(SSL) certificate issuance from Let’s Encrypt®;
  * `<example.com>` — the domain to access Portainer. To make the domain accessible via the server's public IP address, add an A record in your DNS hosting control panel and set the server's public IP address as the record value. You can copy the IP address from the [control panel](https://my.selectel.ru/servers/): in the top menu, click **Products** → **Dedicated Servers** → server page → **Operating System** tab → in the **IP** field, click <CopyIcon />. If your domain is delegated to [Selectel DNS Hosting (actual](/dns-hosting/)), use the [Add a resource record](/dns-hosting/records/add-record.mdx) guide. After OS installation, a TLS(SSL) certificate from Let’s Encrypt® is automatically issued for your domain. If the TLS(SSL) certificate issuance fails, the Portainer panel will be available via the server's IP address;
  * `<root@example.com>` — the email of the Containers Ready administrator for account creation and receiving Let’s Encrypt® notifications.

  <Formbricks />
