diff --git a/roles/base/tasks/apt.yml b/roles/base/tasks/apt.yml
index 48307a13da3f503d94de18b0005b928d25ea01e6..19038e0e647b74d57bee0e595d04c91cb616fb6b 100644
--- a/roles/base/tasks/apt.yml
+++ b/roles/base/tasks/apt.yml
@@ -65,7 +65,6 @@
       - rsync
       - git
       - python-pip
-      - python-docker
       - ntp
       - openssl
       - curl
@@ -77,16 +76,6 @@
       - restic
       - runcron
 
-# With the default credentials handler 'docker login' stops working with:
-# error getting credentials - err: exit status 1, out: Cannot autolaunch
-# D-Bus without X11 $DISPLAY
-# See also https://bugs.debian.org/910823
-- name: Fix docker login on Buster
-  file:
-    state: absent
-    path: /usr/bin/docker-credential-secretservice
-  when: float_debian_dist == 'buster'
-
 - name: Install Stretch packages
   apt:
     name: "{{ packages }}"
@@ -95,6 +84,7 @@
     packages:
       - apt-transport-https
       - liblz4-tool
+      - python-docker
   when: float_debian_dist == 'stretch'
 
 - name: Install Buster packages
diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml
index bfb3b49a0a39ba5d0c5a0382cf336e6eb23c79ec..85db028455e544162ccdadf7ff472a9a808a5c68 100644
--- a/roles/docker/defaults/main.yml
+++ b/roles/docker/defaults/main.yml
@@ -3,6 +3,8 @@
 docker_registry_url: ""
 docker_registry_username: ""
 docker_registry_password: ""
+
+# The following options are not used on >= Buster
 docker_daemon_config:
   log-driver: "local"
   log-opts:
diff --git a/roles/docker/files/registries.conf b/roles/docker/files/registries.conf
new file mode 100644
index 0000000000000000000000000000000000000000..4496b5d11ea6f099a8826b2f415a654b89bbd8ab
--- /dev/null
+++ b/roles/docker/files/registries.conf
@@ -0,0 +1,2 @@
+# Dummy file to mute podman's warning
+# WARN[0000] unable to find /etc/containers/registries.conf. some podman (image shortnames) commands may be limited
diff --git a/roles/docker/tasks/buster_upgrade.yml b/roles/docker/tasks/buster_upgrade.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0207ec14b1bf7e08ee6be2770f0e6193679db2db
--- /dev/null
+++ b/roles/docker/tasks/buster_upgrade.yml
@@ -0,0 +1,28 @@
+- name: Remove docker.com GPG key
+  apt_key:
+    id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
+    url: https://download.docker.com/linux/debian/gpg
+    state: absent
+
+- name: Remove docker.com package repository
+  apt_repository:
+    repo: "deb [arch=amd64] {% if apt_proxy is defined %}http://{{ apt_proxy }}/HTTPS/{% else %}https:{% endif %}//download.docker.com/linux/debian {{ ansible_distribution_release }} stable"
+    state: absent
+
+- name: Remove docker packages
+  apt:
+    name: "{{ packages }}"
+    state: absent
+  vars:
+    packages:
+      - docker-ce
+      - systemd-docker
+
+- name: Remove docker files
+  file:
+    dest: "{{ item }}"
+    state: absent
+  with_items:
+    - /etc/docker
+    - /usr/local/bin/docker-cleanup
+    - /etc/cron.d/docker-cleanup
diff --git a/roles/docker/tasks/docker.yml b/roles/docker/tasks/docker.yml
new file mode 100644
index 0000000000000000000000000000000000000000..baa9ad7d61886ee0a4e6774d86860464f44f6bb0
--- /dev/null
+++ b/roles/docker/tasks/docker.yml
@@ -0,0 +1,32 @@
+# TODO: switch to keyserver once the apt_key --no-tty bug in Ansible is fixed.
+- name: Install docker.com GPG key
+  apt_key:
+    id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
+    url: https://download.docker.com/linux/debian/gpg
+    state: present
+
+- name: Install docker.com package repository
+  apt_repository:
+    repo: "deb [arch=amd64] {% if apt_proxy is defined %}http://{{ apt_proxy }}/HTTPS/{% else %}https:{% endif %}//download.docker.com/linux/debian {{ ansible_distribution_release }} stable"
+    state: present
+
+- file:
+    path: "/etc/docker"
+    state: directory
+
+- name: Configure docker daemon
+  template:
+    src: daemon.json.j2
+    dest: /etc/docker/daemon.json
+  notify:
+    - restart docker
+
+- name: Install docker packages
+  apt:
+    name: "{{ packages }}"
+    state: present
+  vars:
+    packages:
+      - docker-ce
+      - systemd-docker
+
diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml
index dada82b94982eed8b893b93db55d177a534e6b22..c56866a52b8ca8c68fc288fe441cf0e77cb92b63 100644
--- a/roles/docker/tasks/main.yml
+++ b/roles/docker/tasks/main.yml
@@ -1,36 +1,20 @@
 ---
 
-# TODO: switch to keyserver once the apt_key --no-tty bug in Ansible is fixed.
-- name: Install docker.com GPG key
-  apt_key:
-    id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
-    url: https://download.docker.com/linux/debian/gpg
-    state: present
-
-- name: Install docker.com package repository
-  apt_repository:
-    repo: "deb [arch=amd64] {% if apt_proxy is defined %}http://{{ apt_proxy }}/HTTPS/{% else %}https:{% endif %}//download.docker.com/linux/debian {{ ansible_distribution_release }} stable"
-    state: present
+- set_fact:
+    container_runtime: "podman"
 
-- file:
-    path: "/etc/docker"
-    state: directory
+- set_fact:
+    container_runtime: "docker"
+  when: "float_debian_dist == 'stretch'"
 
-- name: Configure docker daemon
-  template:
-    src: daemon.json.j2
-    dest: /etc/docker/daemon.json
-  notify:
-    - restart docker
-
-- name: Install docker packages
-  apt:
-    name: "{{ packages }}"
-    state: present
-  vars:
-    packages:
-      - docker-ce
-      - systemd-docker
+- include_tasks: buster_upgrade.yml
+  when: "float_debian_dist == 'buster'"
+
+- include_tasks: docker.yml
+  when: "container_runtime == 'docker'"
+
+- include_tasks: podman.yml
+  when: "container_runtime == 'podman'"
 
 - name: Login to the Docker registry
   command: docker login -u "{{ docker_registry_username }}" -p "{{ docker_registry_password }}" "{{ docker_registry_url }}"
diff --git a/roles/docker/tasks/podman.yml b/roles/docker/tasks/podman.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0d682d0b262723314aeafa7a34edf7d90dc530dd
--- /dev/null
+++ b/roles/docker/tasks/podman.yml
@@ -0,0 +1,32 @@
+---
+
+# TODO: switch to keyserver once the apt_key --no-tty bug in Ansible is fixed.
+- name: Install project atomic PPA GPG key
+  apt_key:
+    id: 018BA5AD9DF57A4448F0E6CF8BECF1637AD8C79D
+    url: 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x8becf1637ad8c79d'
+    state: present
+
+- name: Install project atomic PPA repository
+  apt_repository:
+    repo: "deb http://ppa.launchpad.net/projectatomic/ppa/ubuntu disco main"
+    state: present
+
+- name: Install podman packages
+  apt:
+    name: "{{ packages }}"
+    state: present
+  vars:
+    packages:
+      - podman
+
+- name: Symlink podman to docker
+  file:
+    src: /usr/bin/podman
+    dest: /usr/bin/docker
+    state: link
+
+- name: Install podman registries.conf
+  copy:
+    dest: /etc/containers/registries.conf
+    src: registries.conf
diff --git a/roles/docker/tasks/start.yml b/roles/docker/tasks/start.yml
index dce9d140f3f47a8237daecc6eaceeedaed1cd1cd..fe7660f6a3834949e894e6056d2fd99f9a1cf0b4 100644
--- a/roles/docker/tasks/start.yml
+++ b/roles/docker/tasks/start.yml
@@ -59,6 +59,13 @@
         name: "{{ container.image }}"
         force: true
       register: docker_image
+      when: "container_runtime == 'docker'"
+
+    - name: Pull the Docker image {{ container.image }}
+      # Ansible >= 2.8 ships with podman_image
+      command: "podman pull {{ container.image }}"
+      register: podman_image
+      when: "container_runtime == 'podman'"
 
     - name: Start the systemd unit {{ systemd_service }}
       systemd:
diff --git a/roles/docker/templates/run.sh.j2 b/roles/docker/templates/run.sh.j2
index 3dd7f225e2265a6505375c880b5bf7e9890dae48..77c8825ecb125a12242bf4b30f8cb20c29def0ed 100644
--- a/roles/docker/templates/run.sh.j2
+++ b/roles/docker/templates/run.sh.j2
@@ -55,7 +55,29 @@ for gid in $(id -G {{ container_user }}); do
 done
 {% endif %}
 
+{% if container_runtime == 'podman' %}
+# Remove 'created' (but never started) and 'exited' containers to avoid name conflicts
+podman ps --quiet --all \
+  --filter status=created \
+  --filter name={{ service.name }}-{{ container.name }} \
+  | xargs --no-run-if-empty podman rm
+
+podman ps --quiet --all \
+  --filter status=exited \
+  --filter name={{ service.name }}-{{ container.name }} \
+  | xargs --no-run-if-empty podman rm
+
+exec /usr/bin/podman run --env-host \
+  --cgroup-manager=cgroupfs \
+  --cgroup-parent /system.slice/{{ systemd_service }} \
+  --rm --name {{ service.name }}-{{ container.name }} \
+  $opts \
+  {{ container.image }} {{ container.get('args', '') }}
+{% endif %}
+
+{% if container_runtime == 'docker' %}
 exec /usr/bin/systemd-docker --env run \
   --rm --name {{ service.name }}-{{ container.name }} \
   $opts \
   {{ container.image }} {{ container.get('args', '') }}
+{% endif %}
diff --git a/roles/docker/templates/systemd.j2 b/roles/docker/templates/systemd.j2
index 3a64bd0f069e78b96347f8d8b821ae19b6aec03d..7ba81e14d2a802afa624cd70d7f636ea9c8818d6 100644
--- a/roles/docker/templates/systemd.j2
+++ b/roles/docker/templates/systemd.j2
@@ -1,7 +1,9 @@
 [Unit]
 Description={{ service.name }}/{{ container.name }}
+{% if container_runtime == 'docker' %}
 After=docker.service
 Requires=docker.service
+{% endif %}
 
 [Service]
 EnvironmentFile=-/etc/default/{{ service.name }}-{{ container.name }}
@@ -22,4 +24,5 @@ CPUQuota={{ 100 * container.resources.cpu }}%
 LimitNOFILE=65535
 
 [Install]
-WantedBy=multi-user.target docker.service
+WantedBy=multi-user.target {{ 'docker.service' if container_runtime == 'docker' else '' }}
+Alias={{ service.name }}-{{ container.name }}
diff --git a/test/integration-test-docker.yml b/test/integration-test-docker.yml
index bf0a394459a533e4e8abbeb251b3719337e9553c..39d2038a90a92abb733f103d502bfba02637c6d1 100644
--- a/test/integration-test-docker.yml
+++ b/test/integration-test-docker.yml
@@ -13,6 +13,11 @@
       docker_image:
         name: registry.git.autistici.org/ai3/float:integration-test
         force: true
+      when: "float_debian_dist == 'stretch'"
+
+    - name: Setup test Docker image
+      command: "podman pull registry.git.autistici.org/ai3/float:integration-test"
+      when: "float_debian_dist != 'stretch'"
 
     - name: Run tests
-      command: docker run --mount type=bind,source=/tmp/test-config.yml,destination=/test-config.yml registry.git.autistici.org/ai3/float:integration-test
+      command: docker run --net host --mount type=bind,source=/tmp/test-config.yml,destination=/test-config.yml registry.git.autistici.org/ai3/float:integration-test