How to Force Cloud-Init to Execute Only Once: Preventing Unwanted Re-runs on Reboot


2 views

Many sysadmins encounter situations where cloud-init unexpectedly re-executes during system reboots - even on fully provisioned systems. This behavior stems from cloud-init's default design to process user-data whenever it detects configuration changes. While this makes sense for dynamic cloud environments, it causes headaches in scenarios where:

  • Manual configurations get overwritten (e.g., SSH host keys regeneration)
  • Network settings revert to initial values
  • Duplicate user accounts get created

Cloud-init determines whether to run through several mechanisms:

# Check current execution status
sudo cat /var/lib/cloud/instance/sem/config_scripts_user

The main trigger files reside in /var/lib/cloud/instance/sem/ where completion markers get created after each module runs.

Method 1: The Official Disable Flag

Create a disable file in the cloud-init configuration directory:

sudo touch /etc/cloud/cloud-init.disabled
sudo cloud-init clean --logs

Method 2: Cloud-Config Directive

Add this to /etc/cloud/cloud.cfg:

# Disable cloud-init after first boot
cloud_init_modules:
  - [scripts-user, once]
  - [set-hostname, once]
  - [update-hostname, once]
  
disable_root: true
preserve_hostname: true

Method 3: Systemd Unit Modification

For systems using systemd, mask the service:

sudo systemctl mask cloud-init.service \
cloud-init-local.service \
cloud-config.service \
cloud-final.service

For automated deployments, include this in your user-data:

#cloud-config
runcmd:
  - [touch, /etc/cloud/cloud-init.disabled]
  - [cloud-init, clean, --logs]
  - [systemctl, mask, cloud-init.service]
power_state:
  mode: poweroff
  timeout: 30
  message: Completed first boot configuration

After implementing any solution, verify with:

cloud-init analyze show
cloud-init status --long
systemctl list-unit-files | grep cloud-init

For cloned VMs or golden images, consider these additional measures:

# Clean cloud-init artifacts before templating
sudo cloud-init clean
sudo rm -rf /var/lib/cloud/instance
sudo rm -f /var/lib/cloud/instances/*

Cloud-init's default behavior of reprocessing user-data on configuration changes or reboots can cause operational issues in production environments. The most common pain points include:

  • SSH host key regeneration breaking existing connections
  • Network configuration reverting to template defaults
  • Custom package installations running multiple times

Cloud-init executes through systemd services with these critical components:

cloud-init-local.service
cloud-init.service
cloud-config.service
cloud-final.service

Each stage evaluates the /var/lib/cloud/instance directory's marker files to determine execution status.

Method 1: The Canonical Disable Flag

The most reliable approach creates a disable file:

sudo touch /etc/cloud/cloud-init.disabled

Method 2: Systemd Service Masking

For systemd-based distributions:

sudo systemctl mask cloud-init.service cloud-init-local.service \
    cloud-config.service cloud-final.service

For template images or automated deployments, add this to your cloud-config:

#cloud-config
runcmd:
  - [ sh, -c, "echo 'datasource_list: [ None ]' > /etc/cloud/cloud.cfg.d/90_disable_cloud_init.cfg" ]
  - [ sh, -c, "touch /etc/cloud/cloud-init.disabled" ]
  - [ systemctl, mask, cloud-init.service ]
  - [ rm, -rf, /var/lib/cloud/instance ]

After implementation, verify with:

cloud-init status --wait
cloud-init analyze show

Check for persistent artifacts:

ls -la /var/lib/cloud/instance
journalctl -u cloud-init

When working with cloned VMs or golden images:

sudo cloud-init clean --log
sudo rm -rf /var/lib/cloud/instances/*
sudo truncate -s 0 /etc/machine-id