legacy-of-lunatic-kingdom: switch from docker to podman
The purge playbook targets only this host since lotus-land-story still runs on Docker.
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/legacy-of-lunatic-kingdom/main.yml b/legacy-of-lunatic-kingdom/main.yml
index f0e61c3..5b8636a 100644
--- a/legacy-of-lunatic-kingdom/main.yml
+++ b/legacy-of-lunatic-kingdom/main.yml
@@ -1,3 +1,7 @@
+# Imported first because a leftover Docker source breaks every apt task that
+# follows it. Drop this once it has run against the host.
+- import_playbook: ../playbooks/docker-purge.yml # noqa: name[play]
+
- name: Basic requirements
hosts: legacy-of-lunatic-kingdom
become: true
@@ -25,7 +29,7 @@
- --collector.systemd
- --collector.systemd.unit-include=apt-daily.*\.(timer|service)
- >-
- --collector.filesystem.mount-points-exclude=^/(dev|proc|run|sys|media|var/lib/docker/.+|var/lib/containers/storage/.+)($|/)
+ --collector.filesystem.mount-points-exclude=^/(dev|proc|run|sys|media|var/lib/containers/storage/.+)($|/)
ansible.builtin.lineinfile:
path: /etc/default/prometheus-node-exporter
regexp: '^ARGS='
@@ -80,7 +84,7 @@
# TODO disallow network access from non-tailscale interfaces
- import_playbook: ../playbooks/tailscale.yml # noqa: name[play]
-- import_playbook: ../playbooks/docker.yml # noqa: name[play]
+- import_playbook: ../playbooks/podman.yml # noqa: name[play]
- import_playbook: ../playbooks/syncthing.yml # noqa: name[play]
- import_playbook: ../playbooks/unattended-upgrades.yml # noqa: name[play]
diff --git a/playbooks/docker-purge.yml b/playbooks/docker-purge.yml
new file mode 100644
index 0000000..85986d5
--- /dev/null
+++ b/playbooks/docker-purge.yml
@@ -0,0 +1,55 @@
+# Removes Docker from a host that has finished moving to podman. The reverse of
+# playbooks/docker.yml.
+- name: Purge Docker
+ tags: docker-purge
+ hosts: legacy-of-lunatic-kingdom
+ become: true
+ tasks:
+
+ # Both formats have been used here over time, so match on the URI rather
+ # than a filename. read_whole_file switches contains from re.match to
+ # re.search; without it the pattern would have to match from column zero.
+ - name: Find Docker apt sources
+ ansible.builtin.find:
+ paths: /etc/apt/sources.list.d
+ contains: download\.docker\.com
+ read_whole_file: true
+ register: docker_sources
+
+ - name: Remove Docker apt sources
+ ansible.builtin.file:
+ path: "{{ item.path }}"
+ state: absent
+ loop: "{{ docker_sources.files }}"
+ loop_control:
+ label: "{{ item.path }}"
+
+ # Every apt run fails while a source references a keyring that is gone, so
+ # this only happens once the sources above are removed.
+ - name: Remove Docker signing key
+ ansible.builtin.file:
+ path: /etc/apt/keyrings/docker.asc
+ state: absent
+
+ - name: Purge Docker packages
+ ansible.builtin.apt:
+ name:
+ - containerd.io
+ - docker-buildx-plugin
+ - docker-ce
+ - docker-ce-cli
+ - docker-compose-plugin
+ state: absent
+ purge: true
+ autoremove: true
+
+ # apt purge leaves image and container storage behind.
+ - name: Remove Docker state
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: absent
+ loop:
+ - /var/lib/containerd
+ - /var/lib/docker
+
+# vim: ft=yaml.ansible
diff --git a/playbooks/podman.yml b/playbooks/podman.yml
new file mode 100644
index 0000000..bef65c9
--- /dev/null
+++ b/playbooks/podman.yml
@@ -0,0 +1,40 @@
+# https://podman.io/docs/installation#debian
+- name: Install Podman
+ tags: podman
+ hosts: all
+ become: true
+ tasks:
+
+ - name: Assert distribution is Debian
+ ansible.builtin.assert:
+ that:
+ - ansible_facts["distribution"] == "Debian"
+ fail_msg: "This playbook only supports Debian distributions"
+
+ # Quadlet arrived in podman 4.4 and bookworm ships 4.3, so there is no
+ # supported path to unit-generated containers before trixie.
+ - name: Assert Podman is new enough for Quadlet
+ ansible.builtin.assert:
+ that:
+ - ansible_facts["distribution_major_version"] | int >= 13
+ fail_msg: >-
+ Quadlet needs podman >= 4.4, which Debian first ships in trixie;
+ this host is on {{ ansible_facts["distribution_release"] }}.
+
+ # podman-docker is deliberately absent: it aliases docker(1) to podman,
+ # which makes the two runtimes indistinguishable on hosts running both.
+ - name: Install Podman
+ ansible.builtin.apt:
+ name: podman
+ state: present
+ update_cache: true
+
+ # Quadlet reads unit files from here and generates systemd services at
+ # daemon-reload. Rootful only; rootless units live under ~/.config.
+ - name: Create Quadlet unit directory
+ ansible.builtin.file:
+ path: /etc/containers/systemd
+ state: directory
+ mode: "0755"
+
+# vim: ft=yaml.ansible