How to Prepare a Linux, Unix, or BSD Image & Deploy it on a Cloud Server
You can deploy a cloud server from your own custom image. This is useful when you need:
- An operating system that is not yet available among our ready-to-use images in the customer portal
- A preconfigured image with your own software and settings
For the purpose of this article, images are grouped into two categories:
- Cloud-compatible images – provided by the OS vendor for cloud environments. They typically include the required drivers, cloud-init support, and other components required for cloud deployment
- Non-cloud images – generic images, such as ISO-based installers, that require additional configuration before they can run properly in the Servers.com cloud
This guide covers how to create, customize, and prepare a virtual machine (VM) image and then upload it to the customer portal to deploy a cloud server. It walks through three scenarios:
- Scenario 1: Deploying a cloud server using a cloud-compatible image without modifications (as-is)
- Scenario 2: Preparing and customizing a cloud-compatible image before uploading
- Scenario 3: Preparing a non-cloud image (for example, from an ISO file) for deployment in the Servers.com cloud
This article does not cover the operating system installation process.
Prerequisites
- Customer portal account
- Added SSH key to the customer portal (required only if you plan to use key-based authentication)
- Local machine (Windows, Linux, or macOS)
- Installed virtualization software (we use QEMU in our examples)
- Source OS image
Image requirements
You can configure all parameters and applications as needed, and ensure your VM meets the following requirements:
Linux
| Requirement | Description |
|---|---|
| Architecture | x64 OS |
| Image format | qcow2 (recommended) |
| Boot firmware | Only BIOS boot firmware. UEFI is not supported |
| Disk layout | Use the standard disk layout provided by your distribution's cloud images. Enable automatic root expansion on first boot with cloud-init's growpart and resizefs modules. |
| No hard-coded networking | Use DHCP and remove any static IP and MAC assignments from network configuration files (such as /etc/network/interfaces, /etc/netplan/*.yaml, etc). |
| Configured remote access | Ensure that the SSH server is enabled and starts on boot. |
| Hypervisor/driver compatibility |
Modern Linux distributions include all necessary virtio drivers. Only very old kernels (pre-3.0) may require adding virtio and network drivers. Legacy Xen-PV is not needed. |
| Image size | There is no size limit for uploaded images. However, uploads through the Customer Portal are limited to 5 GB. |
FreeBSD, OpenBSD, NetBSD
| Requirement | FreeBSD | OpenBSD | NetBSD |
|---|---|---|---|
| Architecture | x64 OS | ||
| Image format | qcow2 (recommended) |
||
| Boot firmware | Only BIOS boot firmware. UEFI is not supported | ||
| Disk layout |
Use GPT with two partitions. The first partition is The root partition must be the last one on the disk. This is required for correct auto-resize during the first boot with |
Use MBR with the root partition placed last on the disk. OpenBSD 7.3 and later support automatic root expansion (auto-grow) with |
Use GPT (recommended). Automatic root expansion is not supported. Resizing requires manual steps: gpt resize + growfs. |
| No hard-coded networking | Use DHCP on vtnet0 and remove any static network configuration from /etc/rc.conf (or related includes). |
Use DHCP on vio0 and remove any static network configuration from /etc/hostname.* files. |
Use DHCP on vioif0 and remove any static network configuration from /etc/ifconfig.* files. |
| Configured remote access | Ensure that sshd is enabled. bsd-cloudinit injects SSH keys automatically. Grant sudo access to the target user if required. |
sshd is enabled by default |
Ensure that sshd is enabled. root SSH login may be disabled by default depending on the image. |
| Hypervisor/driver compatibility |
Ensure that virtio disk and network drivers are enabled and that comconsole is configured. Verify that the image boots correctly under KVM/QEMU. |
Ensure that virtio disk ( Virtio is fully supported in OpenBSD 5.3 and later, and provides optimal performance under KVM. |
Ensure that basic virtio disk and network support via virtio(4) is available in the image. |
| Image size | There is no size limit for uploaded images. However, uploads through the Customer Portal are limited to 5 GB. | ||
For the complete list of requirements, see the OpenStack Image Guide.
Cloud-init compatibility
Cloud-init supports a wide range of Linux and BSD distributions. For the list of supported platforms, see: https://docs.cloud-init.io/en/latest/reference/availability.html.
You can find unofficial BSD images that include cloud-init and have been tested on OpenStack at: https://bsd-cloud-image.org/.
Installing QEMU
If you want to deploy a vendor-provided cloud image as is (Scenario 1), you can skip this section and proceed directly to uploading the image to the customer portal.
QEMU is required only if you plan to customize an image (Scenario 2) or prepare a non-cloud-compatible ISO image (Scenario 3).
In this guide we use QEMU, a free and open-source cross-platform virtualization environment. After installing QEMU, you can start a VM with your OS image.
Ubuntu
sudo apt-get install qemu-system
Fedora
sudo dnf install qemu qemu-kvm
# or
sudo dnf install @virtualization
Red Hat/CentOS
sudo yum install qemu qemu-kvm
SUSE
sudo zypper install qemu
Arch Linux
sudo pacman -S qemu
macOS
From Homebrew:
brew install qemu
From MacPorts:
sudo port install qemu
Windows
- Open PowerShell as an administrator
-
Enable Windows Hypervisor Platform:
Enable-WindowsOptionalFeature -FeatureName HypervisorPlatform -Online -
Install QEMU:
-
Execute the following command:
winget install SoftwareFreedomConservancy.QEMU - Or use a ready-made installer for Windows
-
- Restart Windows
Cloud-init setup
If you want to deploy a vendor-provided cloud image as is (Scenario 1), you can skip this section and proceed directly to uploading the image to the customer portal.
This section is required only if you plan to customize an image (Scenario 2) or prepare a non-cloud-compatible ISO image (Scenario 3).
Cloud-init is a cross-platform tool used to initialize a virtual machine during its first boot and apply configuration through metadata. Typical tasks include setting the hostname, configuring networking, adding SSH keys, creating users, and running initialization commands or scripts.
Cloud-init configuration files
When preparing an image locally, this guide uses a small ISO file called seed.iso to provide initialization data. During the first boot, cloud-init reads the contents of this ISO and applies the defined settings.
The ISO contains two files:
user-data– defines users, SSH keys, and other initialization settingsmeta-data– specifies basic instance parameters, such as hostname
Create two plain text files named user-data and meta-data (without file extensions) and place them in the same directory before building the ISO.
Example user-data:
#cloud-config
users:
- name: cloud-user
shell: /bin/bash
groups: sudo
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- INSERT_YOUR_SSH_KEY_HERE
- name: admin
shell: /bin/bash
groups: sudo
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- INSERT_YOUR_SSH_KEY_HERE
ssh_pwauth: false
hostname: fedora-vmIn the example above, ssh_pwauth is set to false to allow authentication only with SSH keys. Setting it to true can be helpful during testing or when verifying that cloud-init applied your configuration.
Before uploading your prepared image to the customer portal, make sure to disable password authentication again and rely on SSH keys only.
Example meta-data:
local-hostname: fedora-vm
Specifying local-hostname in meta-data is sufficient; instance-id is generated automatically by cloud-init.
Then pack these files into an ISO image that will later be attached to the virtual machine.
Build the seed.iso
Use one of the supported tools to generate a compatible ISO. The recommended method is cloud-localds. If not available, use mkisofs or genisoimage.
Linux / macOS
Run the command directly in the directory that contains these files.
cloud-localds seed.iso user-data meta-data
Alternatives:
mkisofs -output seed.iso -volid cidata -joliet -rock user-data meta-data
or
genisoimage -output cidata.iso -volid cidata -joliet -rock -graft-points user-data=./user-data meta-data=./meta-data
Windows
Use the open-source ISOHelper PowerShell module to create the ISO image.
-
Open Windows Powershell and install the ISOHelper module:
Install-Module -Name ISOHelper -
Create the ISO image:
New-ISOImageFile -SourcePath C:\path-to-cloud-init-config-files -DestinationPath .\seed.iso
A seed.iso file will be created in the current directory.
Launching a VM with QEMU
This section applies to Scenarios 2 and 3 only.
If you want to deploy a vendor-provided cloud image as is (Scenario 1), you can skip this section and proceed directly to uploading the image to the customer portal.
Start the virtual machine with your OS image.
If your image already has cloud-init configured, attach the seed.iso created in Cloud-init setup.
Before starting the VM, note the following parameter used in the launch command: -netdev user,id=net0,hostfwd=tcp::2222-:22
This option sets up port forwarding from the host to the VM. It maps host port 2222 to port 22 inside the VM. You will use this forwarded port later when connecting to the VM.
Using the -m and -smp parameters, set the VM's memory and vCPUs to match the configuration you plan to use on the cloud server.
Linux
qemu-system-x86_64 \
-m 2048 -smp 2 \
-drive file=path-to-cloud-image.qcow2,format=qcow2,if=virtio \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device virtio-net,netdev=net0 \
-nographic \
-drive file=seed.iso,format=raw,if=virtioRemove the -drive file=seed.iso,format=raw,if=virtio line if cloud-init is not installed or configured in the image.
If cloud-init is used, remove this line after the initial configuration is applied.
macOS
qemu-system-x86_64 \
-accel tcg,thread=multi \
-m 2048 -smp 2 \
-drive file=path-to-cloud-image.qcow2,format=qcow2,if=virtio \
-device virtio-net-pci,netdev=n1 \
-netdev user,id=n1,hostfwd=tcp::2222-:22 \
-nographic \
-cdrom seed.isoRemove the -cdrom seed.iso line if cloud-init is not installed or configured in the image.
If cloud-init is used, remove this line after the initial configuration is applied.
Windows
Navigate to the QEMU installation directory (%ProgramFiles%\qemu, by default) and run the VM with the following command:
.\qemu-system-x86_64.exe `
-accel whpx `
-m 2048 -smp 2 `
-drive file=C:\path-to-cloud-image.qcow2,format=qcow2,if=virtio `
-device virtio-net-pci,netdev=n1 `
-netdev user,id=n1,hostfwd=tcp::2222-:22 `
-cdrom C:\path-to-seed.isoRemove the -cdrom <C:\path-to-seed.iso> line if cloud-init is not installed or configured in the image.
If cloud-init is used, remove this line after the initial configuration is applied.
Scenario 1: Deploying from a cloud-compatible image without customization (as is)
You can deploy a cloud server directly from a vendor-provided cloud-compatible image without creating a local virtual machine first. This method is useful if you need to deploy a cloud server from a vendor-provided image that is not available in our catalog and you do not require any customization. To do this:
- Download the vendor cloud image
- Upload the image to the customer portal and create a cloud server (see Uploading the image to the customer portal)
- Connect via SSH as the default user preinstalled in the image (see Default users in vendor-provided cloud images)
For Alpine, this isalpine:
ssh alpine@<cloud_server_public_ip>
Some cloud-compatible images come with only a minimal default user. For example, an Alpine cloud image may include only the alpine default user with limited permissions. After you deploy a cloud server from such an image and connect via SSH as the default user, create a separate privileged account (for example, an admin user with sudo access) and use it for further work with the server.
Example: Creating a privileged user after creating a cloud server
-
Switch to the root user
doas -s - Install and configure sudo
apk update apk add sudo adduser alpine wheel - Create a new administrative user
adduser cloud-user adduser cloud-user wheel - Configure SSH access for the new user
mkdir -p /home/cloud-user/.ssh echo 'ssh-rsa INSERT_YOUR_SSH_KEY_HERE' > /home/cloud-user/.ssh/authorized_keys chmod 700 /home/cloud-user/.ssh chmod 600 /home/cloud-user/.ssh/authorized_keys chown -R cloud-user:cloud-user /home/cloud-user/.ssh - Edit SSH configuration
- Open
/etc/ssh/sshd_configwith vi or install nano (recommended):
apk add nano nano /etc/ssh/sshd_config - Make sure these lines are present and uncommented:
PubkeyAuthentication yes PasswordAuthentication no -
Check if the user is unlocked
Open
/etc/shadowand ensure the line for cloud-user does not contain"!"If it does, replace it with:
cloud-user:*:::::::This allows SSH login while keeping password authentication disabled.
- Open
- Restart the SSH service
rc-service sshd restart - From your local machine, connect to the server as the new user:
ssh admin@<server_ip>
The server now supports login using the admin account with key-based SSH authentication and administrative privileges via sudo.
Scenario 2: Deploying from a cloud-compatible image with customization
This section explains how to customize a vendor-provided cloud-compatible image before uploading it to the customer portal.
These images already include cloud-init, so you can configure the VM, install software, adjust system settings, create users, and then save the updated image for deployment.
- Download the vendor's cloud image
-
Create a cloud-init configuration (
user-dataandmeta-data) and generate the seed.iso- See Cloud-init setup
- Start the VM with QEMU
- Launch the VM using the cloud image and attach the seed.iso so cloud-init applies your configuration on the first boot
- See Launching a VM with QEMU
- Verify cloud-init configuration
cloud-init status --long - Inside the VM, configure the guest OS as you need and install any required software
- Verify the VM meets the image requirements
- Shut down the VM and optimize the image
- Upload the image to the customer portal and deploy a cloud server
Scenario 3: Deploying from a non-cloud-compatible ISO image
This section covers preparing a VM created from an ISO image that does not include cloud-init or any cloud-specific configuration. The goal is simply to make the image boot correctly in the Servers.com cloud and accept initialization metadata during the first launch.
If you already have the operating system installed in your VM, skip ahead to the step where you verify that the SSH server is installed and running. Otherwise, start by creating a VM from the installation ISO.
Create a local virtual disk
When installing an OS from an ISO, you must first create a virtual disk on which you will install the OS. You can call it, for example, my-cloud-image.qcow2.
Specify a <disk-size> value that fits your OS installation requirements. QEMU uses standard size suffixes such as G for gigabytes (for example, 4G).
Note: Creating a virtual disk does not mount it anywhere. Later we will mount it to the VM with a -drive file=… option.
Linux / macOS
qemu-img create -f qcow2 my-cloud-image.qcow2 <disk-size>
Windows
Navigate into the QEMU installation directory (%ProgramFiles%\qemu, by default) and run the following command:
.\qemu-img.exe create -f qcow2 C:\path-to\my-cloud-image.qcow2 <disk-size>
Start QEMU with the OS installation ISO
The first boot must use the installation ISO (for example, ubuntu-25.10-live-server-amd64.iso). The seed.iso is not used at this stage, because cloud-init is not yet installed.
Start the VM using the command from Launching a VM with QEMU and add the following parameters:
...
-cdrom ubuntu-25.10-live-server-amd64.iso \
-boot order=d \
...
Where:
- The
-cdromparameter attaches the installation ISO as a virtual CD-ROM device - The
-boot order=dparameter tells QEMU to boot from the installation ISO instead of the empty virtual disk
Install and configure the guest OS
Cloud-init does not require a "clean" system. Your ISO-based image may already contain:
- Installed packages (for example, MySQL, nginx)
- System users created by those packages
- Preconfigured settings and services that start on boot
After the OS installation and configuration is complete, shut down the VM.
Boot the VM from the virtual disk
Start the VM using the same command from Launching a VM with QEMU, but remove the following parameters:
...
-cdrom ubuntu-25.10-live-server-amd64.iso \
-boot order=d \
...
The VM will then boot from the installed operating system on the virtual disk.
Install and enable the SSH server
sudo apt install openssh-server
sudo systemctl enable ssh
sudo systemctl start ssh
Install cloud-init or an alternative initialization service
The initialization service is responsible for:
- Retrieving metadata and user data (SSH keys, hostname, and so on)
- Creating users
- Running first-boot initialization tasks, including automatic disk expansion
Install disk auto-expansion utilities to allow the root partition and filesystem to grow automatically when the flavor disk is larger than the original image.
On Linux distributions, cloud-init is commonly used together with utilities such as cloud-utils or cloud-utils-growpart.
Some Unix and BSD systems may use different initialization tools or require manual configuration. Refer to your operating system documentation.
Examples for Linux distributions:
RPM-based
sudo dnf install -y cloud-init
sudo systemctl enable cloud-init
Debian-based
sudo apt update
sudo apt install -y cloud-init
sudo systemctl enable cloud-init
Verify cloud-init configuration
If cloud-init is installed in the image, verify that it initializes successfully after the VM starts:
cloud-init status --long
During local QEMU testing, non-cloud images may report limited or missing metadata sources. This is expected. After deployment in the Servers.com cloud, the VM receives the required metadata from the platform.
Configure cloud-init datasources
-
Create a configuration file in the VM:
/etc/cloud/cloud.cfg.d/99-datasources.cfgThe
/etc/cloud/cloud.cfg.d/directory is the standard place for user overrides. The99-prefix in the file name ensures this configuration is loaded last. Files are processed in alphabetical order, so99-datasources.cfgoverrides earlier datasource_list settings. - Add, for example:
This tells cloud-init to look for instance metadata and user data from the supported metadata sources, including NoCloud, which you will emulate via an ISO file.datasource_list: [NoCloud, Ec2, ConfigDrive]
Apply cloud-init metadata and start QEMU
Non-cloud ISO installations do not include a metadata service, so cloud-init must read initialization data from a local source.
To provide initialization data for your image, attach the previously created seed.iso (see Cloud-init setup). When the ISO is attached to the VM, cloud-init reads it as a NoCloud datasource and applies the configuration from user-data and meta-data during the first boot. After initialization completes, seed.iso is no longer required and can be removed for all subsequent VM launches.
Verify the disk layout and auto-expansion
The image must be able to expand the root partition and filesystem on first boot if the flavor disk size is larger than the original image.
Do the following:
- Make sure the disk layout is simple (a single root partition, as created by the default Ubuntu installer)
- Check that
growpartandresizefsare present in the module lists in/etc/cloud/cloud.cfg
grep -A3 -i growpart /etc/cloud/cloud.cfg
grep -A3 -i resizefs /etc/cloud/cloud.cfg
Remove hard-coded network settings
For modern Ubuntu (netplan):
- Check
/etc/netplan/*.yamland make suredhcp4: trueis used - There must be no static IP, MAC address, or interface names tied to a specific MAC
If the system is configured for DHCP by default, you usually do not need to change anything.
If the file 70-persistent-net.rules exists, we recommend removing it to avoid persisting old MAC addresses:
cat /etc/udev/rules.d/70-persistent-net.rules
rm /etc/udev/rules.d/70-persistent-net.rules
Do a final cleanup
Remove VM-specific state and temporary data so that each new instance starts "clean":
- Clear cloud-init state (
/var/lib/cloud) - Optionally clear cloud-init logs and
systemdjournals - Make sure that:
- There are no permanently baked-in SSH host keys, if you want them to be generated on first boot
- There are no user
~/.ssh/authorized_keysfiles (keys will be injected through cloud-init) - The firewall is disabled
Preparing the image for upload
After you verify that the virtual machine meets the image requirements, finalize the disk so it is ready for upload to the customer portal. Some preparation steps differ depending on the operating system.
Remove cloud-init state
This ensures the new cloud server boots as a fresh instance.
Linux and FreeBSD/BSD
sudo cloud-init clean --logs
sudo rm -rf /var/lib/cloud/*
For Ubuntu/Debian, also confirm that /etc/cloud/cloud.cfg.d/*.cfg does not contain test-specific datasource overrides.
Delete VM-specific artifacts
Linux
- Delete SSH host keys:
sudo rm -f /etc/ssh/ssh_host_* - Remove temporary authorized_keys added during local testing:
sudo rm -f /home/*/.ssh/authorized_keys sudo rm -f /root/.ssh/authorized_keys - Remove persistent network rules, if present:
sudo rm -f /etc/udev/rules.d/70-persistent-net.rules
FreeBSD / OpenBSD / NetBSD
Remove host keys and cloud-init or bsd-cloudinit state:
sudo rm /etc/ssh/ssh_host_*
sudo rm -rf /var/db/cloud/*
Shut down the VM cleanly
This ensures that the file system and all services write their final state.
Linux
sudo shutdown -h now
FreeBSD / OpenBSD / NetBSD
sudo shutdown -p now
The resulting qcow2 disk is ready to upload to the Servers.com cloud and use for creating a cloud server.
(Optional) Optimize and compress the image
The maximum upload size in the customer portal is 5 GB.
Use this command to optimize the image:
qemu-img convert -O qcow2 -c original.qcow2 optimized.qcow2
Check the resulting size:
qemu-img info optimized.qcow2
Now the image file is ready for upload to the customer portal.
Uploading the image to the customer portal
We recommend disabling any VPN when uploading an image. If the upload fails, try to restart the process. If the issue persists, contact our support team for assistance.
- Upload your image to the customer portal (see How to upload your own image), selecting the same location where you plan to deploy the cloud server
- Create a cloud server (see Create a Cloud Server)
- In the Image section, select Other and choose your uploaded custom image. Use SSH authentication with the key that matches the public key in your
user-data.
- In the Image section, select Other and choose your uploaded custom image. Use SSH authentication with the key that matches the public key in your
Wait for the cloud server to deploy, then get a public IP address from the server's details page.
Default users and access
Access to the deployed server depends on the operating system in the image. See How to access the cloud server.
SSH access with root is disabled in most vendor images by default.
When a cloud server is provisioned from a custom image, the cloud-user is created and the SSH key you selected during server creation is injected into its ~/.ssh/authorized_keys.
Default users in vendor-provided cloud images
Usernames can vary slightly between distributions or custom vendor builds – check the image description page for confirmation.
Use this table to find the default login user for common vendor cloud images.
| Ubuntu | ubuntu |
| Debian | debian |
| Fedora | fedora |
| AlmaLinux | almalinux |
| Alpine Linux | alpine |
| RHEL | cloud-user |
| openSUSE | opensuse |
| FreeBSD | freebsd |
| OpenBSD | openbsd |
| NetBSD | netbsd |