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 Servercore.
If you do not have specific OS requirements, we recommend using standard images — you can install the OS via autoinstall or manually.
- Create an ISO image.
- Upload the image to S3 storage.
- Get a link to the object in the 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 image archive, install the OS without software RAID and without a
swappartition. We recommend installing the OS via autoinstall on your dedicated server, as some instruction steps will already be performed. -
Check the status of the
sssdpackage and remove it if it is installed:2.1. Check the status of the
sssd:package:dpkg -l sssdIf the package is installed, a line with the status
iiwill appear in the response, for exampleiisssd2.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. Remove files and directories that contain the current
cloud-init:configuration:rm -rf /etc/cloud/cloud.cfg.d /etc/cloud/cloud-init.disabled6.2. Delete data from the previous
cloud-init:execution:cloud-init clean --logs6.3. Create the
/etc/cloud/cloud.cfg.d:directory:mkdir -p /etc/cloud/cloud.cfg.d6.4. Create the
/etc/cloud/cloud.cfgfile using thevi:text editor:vi /etc/cloud/cloud.cfg6.5. Add the
cloud-init:configuration to the file:The
/etc/cloud/cloud.cfgfiledatasource_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 with your changes saved::wq6.7. Create the
cloud-init-local.servicefile using thevi:text 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 with your changes saved::wq -
Add and configure the per-boot script:
7.1. Create the
boot_order_sort.shscript file using thevi:text editor:sudo vi /var/lib/cloud/scripts/per-boot/boot_order_sort.sh7.2. Add the script text to the file:
The
boot_order_sort.shfile#!/bin/bash# Retrieve the output of efibootmgrefi_output=$(efibootmgr -D 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# 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)description=$(echo "$entry" | sed 's/^[0-9A-F]\{4\}\\* //')# 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 [[ $ -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 systemefibootmgr -b "$boot_number" -B > /dev/null 2>&1removed_entries+=("$boot_number") # Add removed boot number to the removed entries listecho "[INFO] Removed Boot Entry: $boot_number"fidonefi# Convert the main boot order list to a comma-separated stringif [[ $ -eq 0 ]]; thenboot_order_sorted_str="None"elseboot_order_sorted_str=$(IFS=,; echo "${boot_order_sorted[*]}")efibootmgr -o "$boot_order_sorted_str" > /dev/null 2>&1echo "[INFO] Updated boot order: $boot_order_sorted_str"fi# Show the final EFI boot entries for verificationefibootmgr -D7.3. Exit the
vitext editor with your changes saved::wq7.4. Set permissions for the
boot_order_sort.sh:file:sudo chmod 700 /var/lib/cloud/scripts/per-boot/boot_order_sort.sh -
Optional: configure SSH. By default, during OS installation, 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 provided during installation will be set but will not be used.8.1. Open the
/etc/ssh/sshd_configconfiguration file using thevi:text editor:sudo vi /etc/ssh/sshd_config8.2. If you want to allow SSH login as the
rootuser, change or add thePermitRootLogin yes.parameter.8.3. If you want to allow password authentication, change or add the
PasswordAuthentication yes.parameter.8.4. Exit the
vitext editor with your changes saved::wq -
Optional: configure
grub:9.1. Open the
/etc/default/grubconfiguration file using thevi:text 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 with your changes saved::wq -
Optional: install and configure
Fail2ban— a service that blocks an IP address in case of failed authorization attempts:10.1. Install
Fail2ban:fail2ban10.2. Open the
/etc/fail2ban/jail.d/service.confconfiguration file using thevi:text 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 with your changes saved::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
system-resolved:service: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 thevi:text editor:sudo vi /root/exclude.txt18.2. Add to the
exclude.txtfile the list of directories to be excluded from the archive:/tmp/*/proc/*/sys/*/dev/*/run/*/mnt/*/media/*/var/lib/lxcfs/var/spool/postfix/lost+found18.3. Exit the
vitext editor with your changes saved::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 image archive to S3 storage, use the Upload Object instruction.
If while creating the ISO image for VMware ESXi you prepared the setul.tpl file, upload it to the 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, use the Get a Link to an Object instruction.
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 a link to the image you uploaded to S3 storage in step 2.
-
If in step 6 you added the path to the image in S3 as the link, enter the Access key and Secret key.
-
Click Create Image.