Nexcess.com Servers.com LiquidWeb.com
Back

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:

  1. Scenario 1: Deploying a cloud server using a cloud-compatible image without modifications (as-is)
  2. Scenario 2: Preparing and customizing a cloud-compatible image before uploading
  3. 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 freebsd-boot (≈64 KB, no mount point). The second root partition is freebsd-ufs mounted as /.

The root partition must be the last one on the disk. This is required for correct auto-resize during the first boot with bsd-cloudinit.

Use MBR with the root partition placed last on the disk.

OpenBSD 7.3 and later support automatic root expansion (auto-grow) with growfs.

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 (vioblk) and network (vio) drivers are supported.

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

  1. Open PowerShell as an administrator
  2. Enable Windows Hypervisor Platform:

    Enable-WindowsOptionalFeature -FeatureName HypervisorPlatform -Online
  3. Install QEMU:

    • Execute the following command:

      winget install SoftwareFreedomConservancy.QEMU
    • Or use a ready-made installer for Windows
  4. 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 settings
  • meta-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-vm

In 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.

  1. Open Windows Powershell and install the ISOHelper module:

    Install-Module -Name ISOHelper
  2. 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=virtio

Remove 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.iso

Remove 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.iso

Remove 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:

  1. Download the vendor cloud image
  2. Upload the image to the customer portal and create a cloud server (see Uploading the image to the customer portal)
  3. Connect via SSH as the default user preinstalled in the image (see Default users in vendor-provided cloud images)
    For Alpine, this is alpine:
    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

  1. Switch to the root user

    doas -s
  2. Install and configure sudo
    apk update
    apk add sudo
    adduser alpine wheel
  3. Create a new administrative user
    adduser cloud-user
    adduser cloud-user wheel
  4. 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
  5. Edit SSH configuration
    • Open /etc/ssh/sshd_config with 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/shadow and 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.

  6. Restart the SSH service
    rc-service sshd restart
  7. 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.

  1. Download the vendor's cloud image
  2. Create a cloud-init configuration (user-data and meta-data) and generate the seed.iso

  3. 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
  4. Verify cloud-init configuration
    cloud-init status --long
  5. Inside the VM, configure the guest OS as you need and install any required software
  6. Verify the VM meets the image requirements
  7. Shut down the VM and optimize the image
  8. 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 -cdrom parameter attaches the installation ISO as a virtual CD-ROM device
  • The -boot order=d parameter 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

  1. Create a configuration file in the VM:

    /etc/cloud/cloud.cfg.d/99-datasources.cfg

    The /etc/cloud/cloud.cfg.d/ directory is the standard place for user overrides. The 99- prefix in the file name ensures this configuration is loaded last. Files are processed in alphabetical order, so 99-datasources.cfg overrides earlier datasource_list settings.

  2. Add, for example:
    datasource_list: [NoCloud, Ec2, ConfigDrive]
    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.

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 growpart and resizefs are 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/*.yaml and make sure dhcp4: true is 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 systemd journals
  • 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_keys files (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.

  1. 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
  2. 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.

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

Useful articles

Nothing found. Please try another keyword.