This release adds Ubuntu 26.04 LTS support, automatic swap, automatic hostnames, and full compatibility with Ansible 2.21. It also cleans up a number of long-standing issues in the dynamic inventory.
🤩 What's New
Ubuntu 26.04 LTS support
You can now provision and manage Ubuntu 26.04 servers. The example hardware profiles for Hetzner, Vultr, and DigitalOcean default to 26.04 images.
Automatic swap
Servers get a right-sized swap file by default. Sizing follows Red Hat's recommendations and the Ubuntu SwapFaq, and swap you created yourself is never touched.
| RAM | Swap size |
|---|---|
| 2 GB or less | 2x RAM |
| 2 to 8 GB | Matches RAM |
| More than 8 GB | Half of RAM (min 4 GB) |
Capped at 8 GB by default. Configurable with swap_enabled, swap_size_mb, swap_max_size_mb, swap_force, swap_file_path, and swap_swappiness.
Automatic hostnames
The OS hostname is now set from server_name in .spin.yml, and cloud-init is configured to preserve it across reboots. If you use your own inventory instead of .spin.yml, you can opt in:
vars:
server_hostname: "{{ inventory_hostname }}"Chrony for time sync
The ntp and ntpsec packages are replaced with chrony (required on Ubuntu 26.04). Ubuntu's default NTP sources are used out of the box. Add your own with chrony_ntp_servers.
💪 Improvements
- No more deprecation warnings on Ansible 2.21. All fact usage was migrated to
ansible_facts, and the Docker repo now uses thedeb822_repositoryformat. The legacy.listfile is cleaned up automatically on existing servers. - Playbook output now shows hosts by server name instead of IP address.
- Each server's environment is available as the
spin_environmenthost variable for use in your own playbooks and templates. - Provisioning with
targetonly creates servers in the targeted environment, and you get a clear error when the target doesn't match anything. - Server names are validated for hostname compatibility with helpful error messages.
- CI now tests Ubuntu 26.04 and runs a VM-based swap integration test.
🐛 Bug Fixes
- The dynamic inventory no longer leaks a
hostsvariable into host and group vars. This triggered reserved-name warnings on Ansible 2.21. environmentis no longer exposed as a host variable (it is a reserved name in Ansible). Usespin_environmentinstead.- The OS validation bypass variable name was inconsistent. It is now
skip_os_version_validationeverywhere.
⏫ Upgrade Notes (why this is 3.0)
Typical setups don't need any changes to .spin.yml. Review the list below if you've customized things:
| If you... | Then... |
|---|---|
Reference hosts by IP in --limit or target |
Hosts are now named by server_name, so use that instead |
Use hostvars[x].environment in your own playbooks |
Rename to spin_environment |
Override docker_apt_repository |
Removed. Use docker_repo_url, docker_apt_release_channel, and docker_apt_arch |
Use skip_os_version_check |
Renamed to skip_os_version_validation |
Have underscores or special characters in server_name |
Names must now be hostname-safe (letters, numbers, dashes) |
| Set custom NTP servers | The old ntp config is gone. Use chrony_ntp_servers |
Things you'll notice on your first run after upgrading:
- Existing servers will have their hostname set from
server_name. This is a one-time visible change. - A swap file is created on servers without existing swap. Set
swap_enabled: falseto opt out. python3-debianis installed on targets (needed for the new Docker repo format).- Requires Ansible core 2.15+ (unchanged).