Prepare an ISO image for OS autoinstall on a dedicated server
You can prepare your own OS image that will be compatible with the OS autoinstall mechanism on a dedicated server in Selectel.
If you have no specific OS requirements, we recommend using standard images — from them you can install the OS automatically or manually.
- Create an ISO image.
- Upload the image to S3 storage.
- Get a link to the object in storage.
- Add the ISO image to the control panel.
1. Create an ISO image
The size of the OS image archive must be at least 3 GB less than the RAM size of the dedicated server. For example, if the server RAM size is 8 GB, the image archive size must not exceed 5 GB.
Ubuntu
Debian
Windows Server
VMware ESXi
We recommend preparing the system as the root user.
-
To prepare the archive with the image, install the OS without software RAID and without a
swappartition. We recommend automatically installing the OS on your dedicated server; then some steps of the instructions will already be completed. -
Check the status of the
sssdpackage and remove it if it is installed:2.1. Check the status of the
sssdpackage:dpkg -l sssdIf the package is installed, the output will contain a line with the
iistatus, for exampleii sssd 2.9.1-....2.2. If the
sssdpackage is installed, remove it:sudo apt purge -y sssdsudo apt autoremove -y -
Install the required packages:
sudo apt install -y \openssh-server \cloud-init \grub2 \grub-efi-amd64-bin \mdadm \nftables -
Update the OS kernel to the latest stable version:
linux-image-generic-hwe-24.04linux-headers-generic-hwe-24.04 -
Update the system packages:
apt update && apt upgrade -y -
Prepare the
cloud-initservice for use in the image:6.1. Delete the files and directories that contain the current
cloud-initconfiguration:rm -rf /etc/cloud/cloud.cfg.d /etc/cloud/cloud-init.disabled6.2. Delete data from the previous
cloud-initrun:cloud-init clean --logs6.3. Create the
/etc/cloud/cloud.cfg.ddirectory:mkdir -p /etc/cloud/cloud.cfg.d6.4. Create the
/etc/cloud/cloud.cfgfile using thevitext editor:vi /etc/cloud/cloud.cfg6.5. Add the
cloud-initconfiguration to the file:File
/etc/cloud/cloud.cfgdatasource_list:- NoCloud- Noneusers:- default#disable_root: 1#ssh_pwauth: 0mount_default_fields: [~, ~, 'auto', 'defaults,nofail,x-systemd.requires=cloud-init.service', '0', '2']resize_rootfs_tmp: /devssh_deletekeys: 1ssh_genkeytypes: ['rsa', 'ecdsa', 'ed25519']syslog_fix_perms: ~disable_vmware_customization: falsecloud_init_modules:- disk_setup- growpart- resizefs- set_hostname- update_hostname- update_etc_hosts- users-groups- ssh- apt-configure- bootcmd- ca_certs- rsyslog- write_filescloud_config_modules:- mounts- locale- set-passwords- timezone- runcmd- snap- ssh_import_id- ubuntu_drivers- ntpcloud_final_modules:- package-update-upgrade-install- scripts-per-once- scripts-per-boot- scripts-per-instance- scripts-user- ssh-authkey-fingerprints- keys-to-console- final-message- power-state-change- lxd- puppet- chef- mcollective- salt_minion- phone_homesystem_info:default_user:name: cloud-userlock_passwd: truegecos: Cloud Usergroups: [adm, systemd-journal]sudo: ["ALL=(ALL) NOPASSWD:ALL"]shell: /bin/bashdistro: ubuntupaths:cloud_dir: /var/lib/cloudtemplates_dir: /etc/cloud/templatesssh_svcname: sshdvendor_data:enabled: True# vim:syntax=yaml6.6. Exit the
vitext editor saving changes::wq6.7. Create the
cloud-init-local.servicefile using thevitext editor:sudo vi /usr/lib/systemd/system/cloud-init-local.service6.8. Add the configuration to the file:
[Unit]Description=Initial cloud-init job (pre-networking)DefaultDependencies=noWants=network-pre.targetAfter=hv_kvp_daemon.serviceAfter=systemd-remount-fs.serviceBefore=NetworkManager.serviceBefore=network-pre.targetBefore=shutdown.targetBefore=sysinit.targetConflicts=shutdown.targetRequiresMountsFor=/var/lib/cloudConditionPathExists=!/etc/cloud/cloud-init.disabledConditionKernelCommandLine=!cloud-init=disabledConditionEnvironment=!KERNEL_CMDLINE=cloud-init=disabled[Service]Type=oneshotExecStartPre=sleep 30ExecStart=/usr/bin/cloud-init init --localRemainAfterExit=yesTimeoutSec=0# Output needs to appear in instance console outputStandardOutput=journal+console[Install]WantedBy=cloud-init.target6.9. Exit the
vitext editor saving changes::wq -
Add and configure the per-boot script:
7.1. Create the
boot_order_sort.shscript file using thevitext editor:sudo vi /var/lib/cloud/scripts/per-boot/boot_order_sort.sh7.2. Add the script text to the file:
File
boot_order_sort.sh#!/bin/bash# Retrieve the output of efibootmgrefi_output=$(LC_ALL=C efibootmgr 2>&1)# Check if the system supports EFIif [[ $? -ne 0 ]]; thenecho "[WARNING] System does not support EFI, boot order will not be changed."exit 0fi# Extract the current boot entry (BootCurrent)current_os=$(echo "$efi_output" | grep -oP '(?<=BootCurrent: )[0-9A-F]{4}')# Extract the boot order (BootOrder)boot_order=$(echo "$efi_output" | grep -oP '(?<=BootOrder: )[0-9A-F,]+')# Declare an associative array to store BootXXXX values and their descriptionsdeclare -A boot_dict# boot_order_sorted will contain the sorted boot order# removed_entries will contain boot numbers that are not in the sorted list (and are removed)boot_order_sorted=()removed_entries=()# Parse all BootXXXX entries with their descriptions (remove 'Boot' prefix and *)boot_entries=$(echo "$efi_output" | grep -oP '(?<=Boot)[0-9A-F]{4}\*?.*')if [[ -z "$boot_entries" ]]; thenecho "[ERROR] No boot entries found in EFI output."exit 1fi# Do not delete entries if the current boot entry cannot be identified.if [[ -z "$current_os" ]] || ! printf '%s\n' "$boot_entries" | grep -qE "^${current_os}(\*|[[:space:]])"; thenecho "[ERROR] Missing current boot entry; no changes made" >&2exit 1fi# Debug outputecho "[INFO] Current Boot: ${current_os:-None}"echo "[INFO] Boot Order: ${boot_order:-None}"# Loop through and process each boot entrywhile read -r entry; do# Extract the four-digit boot number (excluding 'Boot' and '*')boot_number=$(echo "$entry" | grep -oP '^[0-9A-F]{4}')# Extract the description (everything after the boot number)# Compare labels only: different disks have different device paths/GUIDs.description=$(printf '%s\n' "$entry" | sed -E \-e 's/^[0-9A-F]{4}\*?[[:space:]]+//' \-e 's/\t.*//' \-e 's/[[:space:]]+(HD|PciRoot|Pci|VenHw|VenMsg|Acpi|USB|MAC|BBS|FvFile|File|Fv|NVMe)\(.*//' \-e 's/[[:space:]]+$//')# Ensure the boot_number is not empty and add to the dictionaryif [[ -n "$boot_number" ]]; thenboot_dict["$boot_number"]="$description"fi# Add entries containing "IP4" or "PXE", but not containing "HTTP", to the sorted listif [[ ("$description" =~ "IP4" || "$description" =~ "PXE" || "$description" =~ "IPv4" || "$description" =~ "NIC") && ! "$description" =~ "HTTP" ]]; thenboot_order_sorted+=("$boot_number")fiecho "$boot_number: $description"done <<< "$boot_entries"# Add the current OS to the main list if it existsif [[ -n "$current_os" ]]; thenboot_order_sorted+=("$current_os")fi# Find all entries with the same description as current_os and add them to the sorted listif [[ -n "$current_os" ]]; thencurrent_description="${boot_dict[$current_os]}"for boot_number in "${!boot_dict[@]}"; do# If description matches the current OS but boot number is not the same, add it to the sorted listif [[ "$boot_number" != "$current_os" && "${boot_dict[$boot_number]}" == "$current_description" ]]; thenboot_order_sorted+=("$boot_number")fidonefi# Find all boot numbers that are not in the main sorted list and remove themif [[ ${#boot_dict[@]} -eq 0 ]]; thenecho "[INFO] No boot entries to process."elsefor boot_number in "${!boot_dict[@]}"; doif [[ ! " ${boot_order_sorted[*]} " =~ " $boot_number " ]]; then# Remove the boot entry from the systemif efibootmgr -b "$boot_number" -B; thenremoved_entries+=("$boot_number")echo "[INFO] Removed Boot Entry: $boot_number"elseecho "[ERROR] Failed to remove Boot$boot_number" >&2exit 1fifidonefi# Convert the main boot order list to a comma-separated stringif [[ ${#boot_order_sorted[@]} -eq 0 ]]; thenboot_order_sorted_str="None"elseboot_order_sorted_str=$(IFS=,; echo "${boot_order_sorted[*]}")if ! efibootmgr -o "$boot_order_sorted_str"; thenecho "[ERROR] Failed to write BootOrder" >&2exit 1fiecho "[INFO] Updated boot order: $boot_order_sorted_str"fi# Show the final EFI boot entries for verificationfinal_output=$(LC_ALL=C efibootmgr) || exit 1printf '%s\n' "$final_output"actual_order=$(printf '%s\n' "$final_output" | sed -n 's/^BootOrder: //p')if [[ "$boot_order_sorted_str" != None && "$actual_order" != "$boot_order_sorted_str" ]]; thenecho "[ERROR] BootOrder verification failed" >&2exit 1fi7.3. Exit the
vitext editor saving changes::wq7.4. Set permissions for the
boot_order_sort.shfile:sudo chmod 700 /var/lib/cloud/scripts/per-boot/boot_order_sort.sh -
Optional: configure SSH. By default, when installing the OS, the
cloud-initservice sets a password only for therootuser. You can use your own authentication scheme, for example, disable password login and login as therootuser by creating a separate user with SSH key authorization. In this case, therootpassword passed during installation will be set, but will not be used.8.1. Open the
/etc/ssh/sshd_configconfiguration file using thevitext editor:sudo vi /etc/ssh/sshd_config8.2. If you want to allow SSH login as the
rootuser, modify or add thePermitRootLogin yesparameter.8.3. If you want to allow password authentication, modify or add the
PasswordAuthentication yesparameter.8.4. Exit the
vitext editor saving changes::wq -
Optional: configure
grub:9.1. Open the
/etc/default/grubconfiguration file using thevitext editor:sudo vi /etc/default/grub9.2. Change or add the parameters:
GRUB_DEFAULT=0GRUB_TIMEOUT=5GRUB_DISTRIBUTOR=UbuntuGRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet nomodeset"GRUB_CMDLINE_LINUX="rd.auto=1 net.ifnames=0 biosdevname=0"GRUB_PRELOAD_MODULES="part_gpt part_msdos"GRUB_TIMEOUT_STYLE=menuGRUB_TERMINAL_INPUT=consoleGRUB_GFXMODE=autoGRUB_GFXPAYLOAD_LINUX=keepGRUB_DISABLE_RECOVERY=trueGRUB_DISABLE_OS_PROBER=false9.3. Exit the
vitext editor saving changes::wq -
Optional: install and configure
Fail2ban— a service for blocking IP addresses in case of failed authorization attempts:10.1. Install
Fail2ban:fail2ban10.2. Open the
/etc/fail2ban/jail.d/service.confconfiguration file using thevitext editor:sudo vi /etc/fail2ban/jail.d/service.conf10.3. Change or add the parameters:
[sshd]enabled = trueport = sshfilter = sshdaction = nftables[name=sshd, port=ssh, protocol=tcp]logpath = /var/log/auth.logmaxretry = 10findtime = 3600bantime = 8640010.4. Exit the
vitext editor saving changes::wq -
Optional: make additional changes to the system, such as installing extra software, packages, or performing other necessary settings for your image. Ensure that the changes made do not contradict the previous instruction steps.
-
Disable sleep and hibernation mode:
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target -
Create a symbolic link for
mdamd:ln -s /usr/sbin/mdadm /usr/bin/mdadm -
Enable the
systemd-resolvedservice:systemctl enable --now systemd-resolvedln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf -
Remove unused packages:
apt autoremove -y -
Apply the changes; to do this, reboot the system:
systemctl reboot -
Reset the unique system identifier so that each instance of the installed system receives its own identifier:
truncate -s 0 /etc/machine-id /var/lib/dbus/machine-id -
Prepare a file with a list of directories that should not be included in the OS image archive:
18.1. Create the
exclude.txtfile using thevitext editor:sudo vi /root/exclude.txt18.2. Add to the
exclude.txtfile the list of directories to exclude from the archive:/tmp/*/proc/*/sys/*/dev/*/run/*/mnt/*/media/*/var/lib/lxcfs/var/spool/postfix/lost+found18.3. Exit the
vitext editor saving changes::wq -
Create a file system archive:
tar -czp -f /mnt/base.tar.gz --exclude-from=/root/exclude.txt /
2. Upload the image to S3 storage
To upload the archive with the image to S3 storage, follow the Upload object instructions.
If, when creating the ISO image of VMware ESXi, you prepared the setup.tpl file, upload it to storage along with the image.
3. Get a link to the object in the storage
To get a link to an object in S3 storage, follow the Get a link to an object instructions.
4. Add the image to the control panel
-
In the control panel, in the top menu, click Products and select Dedicated servers.
-
Go to the Images section.
-
Click Add image.
-
Select the OS family.
-
Enter the image name.
-
Add the link to the image that you uploaded in step 2 to S3 storage.
-
If in step 6 you added the path to the image in S3 as a link, enter the Access key and Secret key.
-
Click Create image.