A VM cloned from an image already provisioned by cloud-init sometimes boots with the same hostname, the same SSH host keys, and the same network configuration as the source machine, even though cloud-init was supposed to regenerate all of that on first boot. This isn’t a cloud-init bug: cloning preserved a state that cloud-init interprets as proof it already did its job on this exact instance.
What cloud-init does on first boot
cloud-init runs a set of modules (network configuration, SSH host key generation, user-data script execution) exactly once per instance, then records that execution state in /var/lib/cloud/instances/<instance-id>/, a directory keyed on the instance identifier provided by the cloud datasource (AWS, GCP, or other metadata).
# The instance ID that determines whether cloud-init
# considers itself to have already run on this machine
cat /var/lib/cloud/data/instance-id
Why a clone inherits this state
Cloning a VM (via a snapshot, a disk image, or a hypervisor template) copies the entire disk, /var/lib/cloud/ included. If the instance ID exposed by the new cloud environment stays identical (a frequent scenario depending on the virtualization platform used), cloud-init finds a state directory already marked as processed for that ID, and simply skips its initialization phase on boot.
The concrete risk: duplicated SSH host keys
The most serious symptom of this trap is duplicated SSH host keys (/etc/ssh/ssh_host_*_key) between the source machine and its clone: two machines sharing the same private SSH host key weaken the authenticity guarantee these keys are supposed to provide, a far more serious security problem than a merely incorrect hostname.
The fix: cleaning state before freezing the image
cloud-init clean removes the recorded execution state, forcing a full rerun on the next boot, the step that belongs on the source image before freezing it as a clone template, never after the fact on a clone that’s already booted.
# Run on the source image BEFORE freezing it
# as a template/golden image for future cloning
cloud-init clean --logs
# Also forces SSH host key regeneration
# on next boot, not just network config
rm -f /etc/ssh/ssh_host_*_key*
Takeaway
cloud-init only runs once per instance, a state tracked in /var/lib/cloud/instances/<instance-id>/ and keyed on the cloud datasource’s instance identifier. Cloning a VM copies that state along with the rest of the disk: if the instance ID doesn’t change in the new environment, cloud-init skips its initialization, with the most serious consequence being duplicated SSH host keys across machines. cloud-init clean must run on the source image before freezing it as a clone template, never after the fact on a clone already booted with already-corrupted state — the same boot-preparation principle already seen with fstab mounts: an initialization state assumed settled once and for all can turn out wrong in an environment different from the one where it was validated.