How I set up Docker, with persistence for volumes and images, and a service that starts if Qubes service is enabled

This is how I prepare a template to run Docker. It includes persistence for configuration and volumes and images for /etc/docker/, /var/lib/containerd/ and /var/lib/docker/ using bind-dirs on the /rw volume, and integrates with the way Qubes does services, i.e. Docker service will start only if it’s listed in the AppVM settings. Since I do it on a dedicated template I guess this part is optional, but why not :person_shrugging:
I have it in /srv/user_salt/templates/docker.sls and apply to a dedicated template to not mix Docker (and the extra software you’ll see) with my “daily” template, and apply it with

sudo qubesctl --show-output --skip-dom0 --targets=fedora-41-docker state.apply templates.docker saltenv=user
# cat /srv/user_salt/templates/docker.sls 
# -*- coding: utf-8 -*-
# vim: set syntax=yaml ts=2 sw=2 sts=2 et :

{% set gui_user = salt['cmd.shell']('groupmems -l -g qubes') %}

hashicorp stable repo:
  pkgrepo.managed:
    - name: hashicorp
    - enabled: True
    - humanname: "Hashicorp Stable - $basearch"
    - baseurl: "https://rpm.releases.hashicorp.com/fedora/$releasever/$basearch/stable"
    - gpgcheck: 1
    - gpgkey: https://rpm.releases.hashicorp.com/gpg
    - require_in:
      - pkg: install work packages

{% for type in ['stable', 'test', 'nightly'] %}
docker {{ type }} repo:
  pkgrepo.managed:
    - name: "docker-ce-{{ type }}"
    - humanname: "Docker CE {{ type | capitalize }} - $basearch"
    - enabled: {{ type == 'stable' }}
    - baseurl: "https://download.docker.com/linux/fedora/$releasever/$basearch/{{ type }}"
    - gpgcheck: 1
    - gpgkey: https://download.docker.com/linux/fedora/gpg
    - require_in:
      - pkg: install work packages

{% for component in ['debuginfo', 'source'] %}
docker {{ type }} {{ component}} repo:
  pkgrepo.managed:
    - name: "docker-ce-{{ type }}-{{ component }}"
    - humanname: "Docker CE {{ type | capitalize }} - {{ component | capitalize }}{% if component == 'debuginfo' %} $basearch{% endif %}"
    - enabled: False
    - baseurl: "https://download.docker.com/linux/fedora/$releasever/{{ component }}{% if component == 'debuginfo' %}-$basearch{% endif %}/{{ type }}"
    - gpgcheck: 1
    - gpgkey: https://download.docker.com/linux/fedora/gpg
    - require_in:
      - pkg: install work packages
{% endfor %}
{% endfor %}

hashicorp test repo:
  pkgrepo.managed:
    - name: hashicorp-test
    - enabled: False
    - humanname: "Hashicorp Test - $basearch"
    - baseurl: "https://rpm.releases.hashicorp.com/fedora/$releasever/$basearch/test"
    - gpgcheck: 1
    - gpgkey: https://rpm.releases.hashicorp.com/gpg
    - require_in:
      - pkg: install work packages

install work packages:
  pkg.installed:
    - pkgs:
      - ansible
      - python3-ara
      - vault
      - docker-ce
      - docker-ce-cli
      - containerd.io
      - docker-compose-plugin
    - pkg_verify: True

prepare docker for use:
  file.managed:
    - name: /etc/qubes-bind-dirs.d/35_containers.conf
    - makedirs: True
    - contents:
      - "binds+=( '/var/lib/containerd/' )"
      - "binds+=( '/var/lib/docker/' )"
      - "binds+=( '/etc/docker/' )"
    - require:
      - pkg: install work packages
  service.enabled:
    - name: docker.socket
    - require:
      - pkg: install work packages
  group.present:
    - name: docker
    - addusers:
      -  user
    - require:
      - pkg: install work packages

{% for service in ['socket', 'service'] %}
allow docker {{ service }} to be started by qubes settings:
  file.managed:
    - name: /etc/systemd/system/docker.{{ service }}.d/30_qubes.conf
    - makedirs: True
    - contents:
      - "[Unit]"
      - "ConditionPathExists=/var/run/qubes-service/docker"
    - user: root
    - group: root
    - mode: 0644
    - require:
      - pkg: install work packages
      - file: prepare docker for use 
{% endfor %}