I have a personal project where I’m working with a lot of IaC (Infrastructure-as-Code). One of my guiding principles is immutability: anything I create should be automatically configured. Manual intervention is forbidden. In addition, if something needs to be changed, the object should be replaced rather than having its state updated (with a few logical exceptions).

I created an image with Packer to serve as the base for a DigitalOcean Droplet. Custom Images must have cloud-init installed. Since my Droplet will never be rebooted or modified after creation I don’t want to keep cloud-init around once the initialization is complete. Now, the only means of controlling the Droplet is providing user data, and adding apt-get remove -y cloud-init there makes initialization fail because cloud-init has more to do after the script runs. Instead, I use systemd:

Bashsudo systemd-run -p After=cloud-final.service -p Type=oneshot --no-block bash -c 'apt-get purge -y cloud-init && rm -rf /var/lib/cloud'

This defines a one-off service that runs after cloud-init’s last stage, removes cloud-init and its paraphernalia, and then exits. There’s no need to wait: the user data script continues immediately and the service is effectively suspended until everything is ready. Since I put this at the end of the script—which has set -euxo pipefail at the beginning—any errors immediately halt execution, so this line is never reached and I don’t have to worry about it removing logs I need for the purpose of debugging.