Skip to content

Commit 7b6052f

Browse files
[New] Migrate a Disk Image to Akamai Cloud Using Cloud-Init (#7316)
* [New] Migrate a Disk Image to Akamai Cloud Using Cloud-Init * output code edits * cloud-init config code edits * copy and code edits * copy edit for disk name note * copy edit and add meta description * Copy edits * copy edits, release date change * release date change --------- Co-authored-by: Nathan Melehan <[email protected]>
1 parent a7a2ccf commit 7b6052f

File tree

1 file changed

+291
-0
lines changed
  • docs/guides/platform/migrate-to-linode/migrate-disk-image-using-cloud-init

1 file changed

+291
-0
lines changed
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
---
2+
slug: migrate-disk-image-using-cloud-init
3+
title: "Migrate a Disk Image to Akamai Cloud Using Cloud-Init"
4+
description: "This guide provides steps and configurations for migrating and deploying a disk image to a new Linode instance using cloud-init."
5+
authors: ["John Dutton","Abe Massry"]
6+
contributors: ["John Dutton","Abe Massry"]
7+
published: 2025-08-06
8+
keywords: ['cloud-init','migrate','migration','disk image','aws','azure','google cloud','gcp','metadata']
9+
license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)'
10+
external_resources:
11+
- '[Linode API & CLI Documentation](https://techdocs.akamai.com/linode-api/reference/api)'
12+
- '[Akamai Cloud-Init Guides](/docs/guides/applications/configuration-management/cloud-init/)'
13+
- '[Cloud-Init Official Documentation](https://cloudinit.readthedocs.io/en/latest/)'
14+
---
15+
16+
The ability to migrate a virtual machine’s disk image across cloud providers is essential to cloud architecture durability and a key component of multi-cloud portability. There are numerous methods of migrating a disk image, several of which can be found in our documentation library: [Migrate to Linode](/docs/guides/platform/migrate-to-linode/)
17+
18+
This tutorial includes steps for deploying a disk image to a new compute instance using *cloud-init* and the Ubuntu 24.04 LTS distribution. Cloud-init is an industry standard configuration tool that helps automate the configuration of new compute instances upon initial boot.
19+
20+
The method in this guide uses Object Storage on Akamai Cloud for disk image storage, and provides a custom cloud-init configuration compatible with [Akamai’s Metadata service](https://techdocs.akamai.com/cloud-computing/docs/overview-of-the-metadata-service).
21+
22+
## How It Works
23+
24+
Cloud-init configuration files act as a setup script that a compute instance reads during the initial boot process. During this process, metadata or other user-provided data (i.e. a custom disk image) is sourced and applied to the instance.
25+
26+
The cloud-init configuration provided in this guide uses the Linux `pivot_root` function to swap the standard root file system for the new instance’s RAM disk, where it can temporarily store a custom disk image sourced from an object storage bucket via signed URL. The original root disk is remounted upon reboot.
27+
28+
In order for the cloud-init script to function, the RAM disk of your new instance must be larger than the provided custom disk image file. Once the Linode is booted and the disk image is successfully loaded, the instance can be optionally downsized to a smaller plan with less RAM.
29+
30+
## Before You Begin
31+
32+
The following prerequisites are required for completing the steps in this guide:
33+
34+
- An Akamai Cloud account with permissions in place for managing Object Storage and Compute Instances
35+
36+
- The [Linode CLI](https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-the-linode-cli) installed and configured for use with your Akamai Cloud account
37+
38+
- An Object Storage bucket and valid access key for storing and accessing your disk image file
39+
40+
- The [s3cmd](https://techdocs.akamai.com/cloud-computing/docs/using-s3cmd-with-object-storage) command line utility installed and configured to interact with your Object Storage bucket and region
41+
42+
### Disk Image Requirements
43+
44+
There are several requirements that must be met for your disk image to successfully run on a Linode instance. See our documentation for guidance on [setting up](https://techdocs.akamai.com/cloud-computing/docs/upload-an-image#set-up-an-image-file) or obtaining an image file. The process for acquiring a disk image may vary depending on the cloud provider you are migrating from (i.e. AWS, Azure, Google Cloud, etc.).
45+
46+
- **Image size**: The total size of your disk image should be lower than the RAM disk size of your new compute instance. For example, if your disk image file is 12GB, your new instance should have 16GB of RAM available. If necessary, your new instance can be resized to a smaller plan size afterwards.
47+
48+
- **Linux kernel**: It is recommended to use a platform-agnostic kernel such as the distro-provided kernel with GRUB 2 as the bootloader - rather than a provider-optimized kernel (i.e. [Amazon Linux](https://docs.aws.amazon.com/linux/) kernels). This helps avoid any platform-based dependencies or compatibility issues.
49+
50+
- **Disk format and partitions**: The disk must use the ext3 or ext4 file system in order to be compatible. The cloud-init configuration provided in this guide only supports non-partitioned disk images.
51+
52+
- **File type**: Your disk image can be either in uncompressed `.img` or compressed `.gz` (gzip) format. At the time of this writing, `.iso` image files are not supported.
53+
54+
{{< note >}}
55+
The provided cloud-init configuration uses a file’s metadata to gather information and does not look for specific file extensions.
56+
{{< /note >}}
57+
58+
If you are using a disk image that requires direct disk to boot (i.e. an image with partitioned disks or a Master Boot Record, or MBR), you may need to take additional steps to configure your system before image creation or after deployment for full compatibility with the Akamai Cloud platform. See our guide on [Installing a Custom Distribution](/docs/guides/install-a-custom-distribution/#make-the-system-compatible-with-the-linode-platform) for guidance and configuration options for a direct disk image.
59+
60+
## Upload a Disk Image to Object Storage
61+
62+
{{< note title ="Create an Object Storage Bucket" >}}
63+
If you haven’t done so already, [create an Object Storage bucket](https://techdocs.akamai.com/cloud-computing/docs/create-and-manage-buckets) in which to store your disk image.
64+
{{< /note >}}
65+
66+
The Object Storage item upload limit is 5GB. This is the same as our [Custom Image upload limit](https://techdocs.akamai.com/cloud-computing/docs/upload-an-image). If using an uncompressed or compressed disk image larger than 5GB, you must use an alternative tool such as s3cmd when uploading your image file.
67+
68+
You can optionally compress your disk image file using the following gzip command. The `-9` flag [regulates the speed](https://linux.die.net/man/1/gzip) of compression by running at a slower speed while maintaining the highest quality:
69+
70+
```command
71+
gzip -9 {{< placeholder "exampleimage.img" >}}
72+
```
73+
74+
### Upload Using Cloud Manager
75+
76+
1. While logged into Cloud Manager, select **Object Storage** from the side bar.
77+
78+
1. Select your bucket.
79+
80+
1. Drop and drag your disk image file, or click **Browse Files** to select the file from your local machine.
81+
82+
### Upload Using the Linode CLI
83+
84+
1. Confirm access to your account by viewing a list of available buckets. If prompted, specify your bucket region (i.e. `us-east-1`):
85+
86+
```command
87+
linode-cli obj ls
88+
```
89+
90+
```output
91+
2025-07-16 18:37 {{< placeholder "bucket1" >}}
92+
```
93+
94+
1. Upload your disk image file to your bucket, replacing {{< placeholder "exampleimage.img" >}} with the your disk image and {{< placeholder "bucket1" >}} with your bucket name:
95+
96+
```command
97+
linode-cli obj put --acl-public {{< placeholder "exampleimage.img" >}} {{< placeholder "bucket1" >}}
98+
```
99+
100+
1. Confirm the upload using the `ls` function:
101+
102+
```command
103+
linode-cli obj ls {{< placeholder "bucket1" >}}
104+
```
105+
106+
### Upload Using s3cmd
107+
108+
1. Confirm your s3cmd utility is configured to interact with your destination bucket by listing the available buckets:
109+
110+
```command
111+
s3cmd ls
112+
```
113+
114+
```output
115+
2025-07-16 18:37 s3://{{< placeholder "bucket1" >}}
116+
```
117+
118+
1. Upload your disk image using the `s3cmd put` command:
119+
120+
```command
121+
s3cmd put {{< placeholder "exampleimage.img" >}} s3://{{< placeholder "bucket1" >}}/
122+
```
123+
124+
s3cmd uses multi-part uploading for objects over a certain size. Allow this process to complete for the upload to finish:
125+
126+
```output
127+
upload: '/path/to/image/{{< placeholder "exampleimage.img" >}}' -> 's3:/{{< placeholder "bucket1" >}}/{{< placeholder "exampleimage.img" >}}' [part 1 of 80, 15MB] [1 of 1]
128+
15728640 of 15728640 100% in 1s 10.46 MB/s done
129+
upload: '/path/to/image/{{< placeholder "exampleimage.img" >}}' -> 's3://{{< placeholder "bucket1" >}}/{{< placeholder "exampleimage.img" >}}' [part 2 of 80, 15MB] [1 of 1]
130+
15728640 of 15728640 100% in 1s 13.66 MB/s done
131+
upload: '/path/to/image/{{< placeholder "exampleimage.img" >}}' -> 's3:/{{< placeholder "bucket1" >}}/{{< placeholder "exampleimage.img" >}}' [part 3 of 80, 15MB] [1 of 1]
132+
15728640 of 15728640 100% in 1s 14.60 MB/s done
133+
...
134+
```
135+
136+
1. Confirm presence of the disk image file in your bucket:
137+
138+
```command
139+
s3cmd ls s3://{{< placeholder "bucket1" >}}
140+
```
141+
142+
```output
143+
2025-07-16 22:11 26289897472 s3://{{< placeholder "bucket1" >}}/{{< placeholder "exampleimage.img" >}}
144+
```
145+
146+
## Create a Signed URL for the Disk Image
147+
148+
Signed URLs create a web link to specific objects in a bucket while limiting permission and applying a defined time limit for access.
149+
150+
Use the following command to create a signed URL for your disk image [with the Linode CLI](https://techdocs.akamai.com/cloud-computing/docs/using-the-linode-cli-with-object-storage#create-a-signed-url-with-the-cli). Replace `{{< placeholder "bucket1" >}}` with your bucket name, and `{{< placeholder "exampleimage.img" >}}` with the name of your disk image. Replace `1000` (~16 minutes) with the number of seconds you wish to allow access to your object:
151+
152+
```command {title="Linode CLI"}
153+
linode-cli obj signurl {{< placeholder "bucket1" >}} {{< placeholder "exampleimage.img" >}} +1000
154+
```
155+
156+
Alternatively, if you wish to create a signed URL for your disk image [using s3cmd](https://techdocs.akamai.com/cloud-computing/docs/using-s3cmd-with-object-storage#create-a-signed-url-with-s3cmd), use the following command, replacing the same values as above:
157+
158+
```command {title="s3cmd"}
159+
s3cmd signurl s3://{{< placeholder "bucket1" >}}/{{< placeholder "exampleimage.img" >}} +1000
160+
```
161+
162+
This generates output in the form of a signed URL. Note that the below URL has been scrubbed of identifying information and is invalid:
163+
164+
```output
165+
https://us-east-1.linodeobjects.com/{{< placeholder "bucket1" >}}/{{< placeholder "exampleimage.img" >}}?AWSAccessKeyId=ABCNLJNAESNMPEE8N123&Signature=2cxU6qOKE2%2BjADFNRrdb1lMWubI%3D&Expires=1752849789
166+
```
167+
168+
Save your signed URL somewhere secure so that it can be used in the cloud-init configuration in the following section.
169+
170+
## Deploy an Instance Using Your Disk Image
171+
172+
1. While in Cloud Manager, select **Linodes** from the side bar, and click **Create Linode**.
173+
174+
1. Use the following parameters as guidelines when creating your Linode instance:
175+
176+
- **Region**: The same region as your Object Storage bucket (optional).
177+
- **Linux Distribution**: Ubuntu 24.04 LTS
178+
- **Linode Plan**: Select a compute plan with a larger RAM disk than your disk image size. For example, if your disk image is 25GB, select a Linode plan with 32GB RAM. The cloud-init script used in this guide swaps out the root boot disk and uses your new instance’s RAM disk to temporarily house your uploaded disk image. After your new instance is up and running, you can optionally downsize your Linode to a smaller plan with less RAM.
179+
180+
{{< note title="Linode Plan Size Minimum" >}}
181+
A [Linode plan](https://www.linode.com/pricing/) with a minimum of 16GB RAM is recommended for the cloud-init configuration to successfully run.
182+
{{< /note >}}
183+
184+
1. Under **Add User Data**, insert the following cloud-init config file contents in the **User Data** field. Make sure the config appears exactly as displayed below with no leading or trailing spaces.
185+
186+
In the line `mount none /tmp/tmproot -t tmpfs -o size=30G` (row 9), adjust the temporary disk size in accordance with your chosen RAM disk size. Make sure to specify a smaller `tmpfs` (temporary file system) than your total RAM. It must be large enough to hold the cloud-init Ubuntu OS and disk image coming from Object Storage, and small enough that it doesn’t use up all the RAM available. Generally this means a `tmpfs` 2-4GB smaller than your total plan RAM size.
187+
188+
For example, if your Linode plan has 64GB of RAM and your disk image size is between 30GB and 60GB (i.e. 48GB), change the RAM disk size to `60G`. This allows for some extra room between your disk image size and the total RAM disk. The `30G` example in the script assumes your disk image is large enough to require a plan size of 32GB.
189+
190+
Replace the `SIGNED_URL` placeholder in the `wget` command (row 13) with the signed URL for your disk image generated in the previous section:
191+
192+
```file {title="cloud-init config" hl_lines="9 13"}
193+
#cloud-config
194+
write_files:
195+
- path: /run/scripts/test-script.sh
196+
content: |
197+
#!/bin/bash
198+
echo 'making directory /tmp/tmproot' >&2
199+
mkdir /tmp/tmproot
200+
echo 'mounting tmpfs to /tmp/tmproot' >&2
201+
mount none /tmp/tmproot -t tmpfs -o size={{< placeholder "30G" >}}
202+
echo 'changing directory to /tmp/tmproot' >&2
203+
cd /tmp/tmproot
204+
echo 'downloading signed image from object storage' >&2
205+
wget "{{< placeholder "SIGNED_URL" >}}" -O /tmp/tmproot/signed_image.img
206+
filename="signed_image.img"
207+
if [[ $(file -b signed_image.img) == *gzip* ]]; then
208+
echo 'detected a compressed file gzip' >&2
209+
mv signed_image.img signed_image.img.gz
210+
filename="signed_image.img.gz"
211+
fi
212+
echo 'running telinit 2 to switch to runlevel 2' >&2
213+
telinit 2
214+
echo 'making directories in /tmp/tmproot' >&2
215+
mkdir /tmp/tmproot/{proc,sys,usr,var,oldroot}
216+
echo 'copying files to /tmp/tmproot' >&2
217+
cp -ax /{bin,etc,mnt,sbin,lib} /tmp/tmproot/
218+
echo 'copying directories to /tmp/tmproot/usr' >&2
219+
cp -ax /usr/{bin,sbin,lib} /tmp/tmproot/usr/
220+
echo 'copying directories to /tmp/tmproot/var etc.' >&2
221+
cp -ax /var/{account,empty,lib,local,lock,nis,opt,preserve,run,spool,tmp,yp} /tmp/tmproot/var/
222+
echo 'copying directories to /tmp/tmproot/dev' >&2
223+
cp -a /dev /tmp/tmproot/dev
224+
echo 'copying directories to /tmp/tmproot/run' >&2
225+
cp -ax /run /tmp/tmproot/
226+
echo 'copying directories to /tmp/tmproot/lib64 and /tmp/tmproot/usr/lib64' >&2
227+
cp -ax /lib64 /tmp/tmproot/
228+
cp -ax /usr/lib64 /tmp/tmproot/usr/
229+
echo 'mount make-rprivate /' >&2
230+
mount --make-rprivate /
231+
echo 'pivoting root to /tmp/tmproot' >&2
232+
pivot_root /tmp/tmproot/ /tmp/tmproot/oldroot
233+
echo 'mounting none /proc -t proc' >&2
234+
mount none /proc -t proc
235+
echo 'mounting none /sys -t sysfs' >&2
236+
mount none /sys -t sysfs
237+
echo 'mounting none /dev/pts -t devpts' >&2
238+
mount none /dev/pts -t devpts
239+
echo 'moving mounts from /oldroot to /' >&2
240+
for i in dev proc sys run; do mount --move /oldroot/$i /$i; done
241+
echo 'telinit u to switch to runlevel 1' >&2
242+
telinit u
243+
echo 'systemctl isolate rescue.target' >&2
244+
systemctl isolate default.target
245+
echo '#!/bin/bash' > /tmp/test-script-cont.sh
246+
chmod +x /tmp/test-script-cont.sh
247+
echo 'exec >/dev/ttyS0 2>&1' >> /tmp/test-script-cont.sh
248+
echo 'cd /' >> /tmp/test-script-cont.sh
249+
echo 'echo "started sub script"' >> /tmp/test-script-cont.sh
250+
echo 'pids=$(fuser -vm /oldroot | xargs | sed "s/kernel //") >> /tmp/test-script-cont.sh'
251+
echo 'echo "got pids: $pids"' >> /tmp/test-script-cont.sh
252+
echo 'kill $pids' >> /tmp/test-script-cont.sh
253+
echo 'echo "killed pids"' >> /tmp/test-script-cont.sh
254+
echo 'umount /oldroot' >> /tmp/test-script-cont.sh
255+
echo 'echo "umounted /oldroot"' >> /tmp/test-script-cont.sh
256+
# if filename matches gzip, decompress it
257+
if [[ $filename == "signed_image.img.gz" ]]; then
258+
echo 'echo "detected a compressed file gzip"' >> /tmp/test-script-cont.sh
259+
echo 'which gzip' >> /tmp/test-script-cont.sh
260+
echo 'gzip -cd signed_image.img.gz | dd of=/dev/sda status=progress' >> /tmp/test-script-cont.sh
261+
else
262+
echo 'dd if=signed_image.img of=/dev/sda status=progress' >> /tmp/test-script-cont.sh
263+
fi
264+
echo 'echo "dd finished about to reboot"' >> /tmp/test-script-cont.sh
265+
echo 'reboot' >> /tmp/test-script-cont.sh
266+
nohup bash -c /tmp/test-script-cont.sh &
267+
permissions: '0755'
268+
269+
runcmd:
270+
- [ bash, "/run/scripts/test-script.sh" ]
271+
```
272+
273+
1. Finish selecting your Linode instance parameters, and click **Create Linode**. Depending on the contents and configuration of your disk image, this may take some time to complete. You can monitor the status of your instance’s deployment using the [Lish console](https://techdocs.akamai.com/cloud-computing/docs/access-your-system-console-using-lish).
274+
275+
1. Once fully booted, log into your new instance using the user and credentials from your original disk image, replacing {{< placeholder "IP_ADDRESS" >}} with the IPv4 of your Linode instance:
276+
277+
```command
278+
ssh {{< placeholder "USER" >}}@{{< placeholder "IP_ADDRESS" >}}
279+
```
280+
281+
1. Complete the steps in our [Set Up and Secure a Linode](https://techdocs.akamai.com/cloud-computing/docs/set-up-and-secure-a-compute-instance) guide to secure your instance, including updating your system, adding a limited sudo user, hardening SSH access with public key authentication, and configuring a firewall.
282+
283+
{{< note title="Disk Name" >}}
284+
Once the process is complete, the Linode instance's configuration displays the disk name as “Ubuntu 24.04” in Cloud Manager upon first boot regardless of the incoming operating system used by the origin disk. The *actual* operating system on the instance will match that of the incoming disk image. The configuration can be renamed within the Cloud Manager interface.
285+
{{< /note >}}
286+
287+
## Resizing the Instance
288+
289+
Since the cloud-init script initially requires a larger amount of RAM, you may find yourself with more RAM than your use case requires. You can optionally resize your instance to a smaller Linode plan size to reduce unnecessary compute resources and lower billing cost. When downsizing, remember to select a Linode plan with enough CPU, RAM, and storage to support your original workload.
290+
291+
See our [Resize a Linode](https://techdocs.akamai.com/cloud-computing/docs/resize-a-compute-instance) guide for instructions on how to change your Linode plan.

0 commit comments

Comments
 (0)