[mountain-of-faith]
change lumpxvyqnvwzzvzwwrkyolzoqzqkqupw
commit e087c166937b7db8759f767b5b338083929bfbb2
author Alpha Chen <alpha@kejadlen.dev>
date
parent tkxywrnx
diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl
index 4c87ad3..968b360 100644
--- a/.terraform.lock.hcl
+++ b/.terraform.lock.hcl
@@ -2,45 +2,47 @@
 # Manual edits may be lost in future updates.
 
 provider "registry.terraform.io/linode/linode" {
-  version     = "2.34.0"
+  version     = "2.41.1"
   constraints = "~> 2.18"
   hashes = [
-    "h1:iiVqqMGJt7pk9TlpuMa6DKK3ffOh2lKgYAkUquzf4A0=",
-    "zh:46c7b398e6d7b833e8884e0636942216263c41202415a6edff4851ac2d8c20aa",
-    "zh:4858f7ab37315d480fcbb0a5a4abfa85b951ed3f54680e2f41a4a5b08055f824",
-    "zh:48740c443ae0f1f46298a407caede642f6912b869ae13b41cda9cfafa9537b19",
-    "zh:4c7158c29d1c9f4fa7d6955b230e9ea735ec14a099256888788f5257bfc47ee0",
-    "zh:68f002b7dccac0c704ca8eefb1475a5af12f1b9e9656b90bffde8e401244671b",
-    "zh:7da03cbbaa33dff4b1cd47eb2ff28d4fb43444b32f8fa3936111e83c043edadf",
-    "zh:9cc73fdbdf230f2663e35b7493237e64e5596d8520233842942303230e1ea234",
-    "zh:aa9e0166691523f5e19d76ad5d74a17a0b0c419489adfe0a657b565ba641b629",
-    "zh:b33af04cc9740c762a3c55c3753b3f293873349c233f8d7d0a734b8eb9291c32",
-    "zh:cf246a8e4653685f1b02ec2f6c01a88927cc2c1557a4f8f1e3b54a162a96e512",
-    "zh:dcdf67170d07b544f98ffae54c3c8e0e6154d4bbb27bfef33f0746a327cece4b",
-    "zh:f3a34513e650c6df61c2da0cf160f6d83e493e8d210a39d63510405d13c27391",
-    "zh:f5ae88e47ed08fb7d4ebc48d84fedb9daba372c1c634508698d8fbf4c4948231",
-    "zh:f8c7d3c7e7865c221d15cbb6a12f9e60bea95b0a91d573833c3490d24e3b9227",
+    "h1:gDA5DkRDuSgXdWoChx+ttbDeuB3Jv3VMV6ekS4GgWK8=",
+    "zh:0b76e77089c098171d09711bab3a13ffb65f8247876ef9f613b249004f04bddf",
+    "zh:0f060eb2558c28cce8d3cbc2add2028ba211c9d887606ca457b78b76819e0013",
+    "zh:29d74b53aa7899b6c31a629a37d2ccab166a21f25829f1dc13e5ecc240048df9",
+    "zh:2be5fb85c1d386dd370cf9c5ebf570ebf5841b8283592e0f94f2bb42090bf092",
+    "zh:893850f36c8ab5ccaec99164b3e51e54791e27cac7995d8cf9dce85156c5ec08",
+    "zh:9ed8515c25b6c2ed32c33c1b2d7631e530382fa44e950c694e55e599fa554a09",
+    "zh:a3e8a3b0cff767292fed8c8db6c071231b2565e64a8a48fb85b636bcdef74643",
+    "zh:a5a303ed5712502c8bdb7173ca3a9ab080a2a589a56a938b59e07c98086f6d97",
+    "zh:d13e06f2b34105d5755bacf3613710d9a05f1b81865dd8c62ec35ecee5895a0b",
+    "zh:d3c60ef1782a71c0eb6f64a57e398aa8536942277c42dca7ee9993e68c58e808",
+    "zh:f1a9fcf96bc428272345556b520d631220e7443efaa41929cf465c6f8944c2ba",
+    "zh:f3ef16df5913dbc73fb9a6debdb58e2fb85e7063d0d23d8c2be2562c1a0a7f3c",
+    "zh:fdc3c13f05f2d7563af84cd4ff20d07b8b3e8593110294357840d9f0e295fe97",
+    "zh:febe350bb0f7ff4755658def77552d78991783f247aeaa2e919cd3a558aaad7b",
   ]
 }
 
 provider "registry.terraform.io/ryanwholey/pihole" {
-  version     = "0.2.0"
-  constraints = "~> 0.2"
+  version     = "2.0.0-beta.1"
+  constraints = "2.0.0-beta.1"
   hashes = [
-    "h1:bnOfTUShJbhaEnqAD+hfWECOQRriukCNxVrH478leC4=",
-    "zh:340bf25085869ef0292ca4c4b916f509e7b3af9bf9c5b4dee9279fdf74287281",
-    "zh:4dbed1af421a2b9379e99e9d896898b6597ca874ff071e874c5116ba62f7706c",
-    "zh:532dc4ff9500c2f0225f12e2df53a2fc4da46713fc01756d1575783933dc51e0",
-    "zh:837a7030b30d5651c46307b687bc6c082aff191e2ea31f36914d585bb46ffc0d",
-    "zh:a678331326f465a4ca930d26e10d75e6e62e05ae9220ee406b34d4d1d77e0595",
-    "zh:a6e23c128d71061ab89e78c45ef7a878420a8201551136d3ecbd10ae620676e1",
-    "zh:b5133016ddd29f08f3719da3f7ee41680ed05a6d8f4c3f6b0bb89a6aa5f187da",
-    "zh:b59ce1807603f5cd95e8988f8203c9b03a7754f75dbf613909fee5abe687ecf2",
-    "zh:bd3fb3ea5d72468a08491c27ec4f32b1836533d4036f88f56728508644a9bcb2",
-    "zh:cf69c5127d89bdd8c094f169cdf3c93be2977abc48030c001616129f74f124ca",
-    "zh:e82a08e0be7437ef19b5ced837e76fd5014c490b803c2b479738a8db3a6ad933",
-    "zh:eef96671eba8c9e7d52586a0ef2adc0f92b13f46faf893b553d83366c01cc8ff",
-    "zh:f63758ad86ab442f29041f7d0de417efa594a0f0b2ade0651977ec8831ad9378",
+    "h1:u1I9C4k4xTnM2ngeqnDuNxrUQaqWqujP3hwhPrAo9sw=",
+    "zh:02b61dfbdf0729b8fc4078cedd883334ae5a5360999e6c74255a3a55d5e7b1fd",
+    "zh:0e7502067463236fd68ea558b6a68d5b37e2d3eea4e52355e2db46de78896402",
+    "zh:1335a746f4b9507103c067fb72854db42426cd1503ff18f803c040555d64c172",
+    "zh:1ae696498f377bab1f959881580ef98af7f711c59a1c5d17044cc8288dafc1aa",
+    "zh:3ba0a0b2b7127dbd38b4b1ac65a6b1568332bb4dbad5cb5bc27b07d85ec5f369",
+    "zh:4b25d770e7664b971ed667f4c4fd4aea812704dc490c1f35e85da7bc476e6d5c",
+    "zh:4b459c231ae51d5f206ec34233933d41668c199df19cbdcbbb7aa5ef09701df9",
+    "zh:5e7f6b6caa272d34432956648b5f12b107957f9cb5e4bb8ca2a054c27350de8f",
+    "zh:6b6dc50b623074d880c0dff315ae717a320c9638f52b50303262953c1733ef0d",
+    "zh:775bd9cb0411c11bcb40646a60354e8346678c00e297bd0e1305c6dec834815f",
+    "zh:887267938b4b993da80119587ddee46355f9c51e8244d68de6fb32ddedbd9546",
+    "zh:8b99a65842b414930bf44ffb6d729d6fdfd35fcfe639e11c9c564dfaf10fb68a",
+    "zh:8be0072af4e07d91ee6a4fa0cbe4f56e3325ec5d213f76be9e395ca0eac116fe",
+    "zh:9efbfde422b6dd226ae59ec0c286953977f8ad04690c85e4f681f171dc5a8088",
+    "zh:d26570107355722e5e4c7d1d4731c7171f1f11a6425f92481f77fd3f832a2ea6",
   ]
 }
 
diff --git a/hosts.yml b/hosts.yml
index c2d311f..6274816 100644
--- a/hosts.yml
+++ b/hosts.yml
@@ -8,6 +8,7 @@ all:
       ansible_python_interpreter: /usr/bin/python3
     on-fire-within:
     lotus-land-story:
+    mountain-of-faith:
 local:
   hosts:
     localhost:
diff --git a/lotus-land-story/caddy.yml b/lotus-land-story/caddy.yml
index 58820f2..38efb96 100644
--- a/lotus-land-story/caddy.yml
+++ b/lotus-land-story/caddy.yml
@@ -51,7 +51,7 @@
         volumes:
           - /mnt/lotus-land-story/caddy/Caddyfile:/etc/caddy/Caddyfile
           - /mnt/lotus-land-story/caddy/data:/data
-          - caddy-config:/config
+          - caddy-config:/config # TODO probably put this somewhere else???
         restart_policy: unless-stopped
         networks:
           - name: lotus_land_story
diff --git a/lotus-land-story/templates/backup.sh b/lotus-land-story/templates/backup.sh
index 4fdc8c4..deda03c 100644
--- a/lotus-land-story/templates/backup.sh
+++ b/lotus-land-story/templates/backup.sh
@@ -2,7 +2,7 @@
 
 set -eu pipefail
 
-export RESTIC_REPOSITORY=sftp:restic@on-fire-within:/mnt/mushu/restic
+export RESTIC_REPOSITORY=sftp:restic@mountain-of-faith:/mnt/mountain-of-faith/restic
 export RESTIC_PASSWORD={{ restic.password }}
 
 restic --verbose backup \
diff --git a/main.tf b/main.tf
index 5c2b72f..a82adfd 100644
--- a/main.tf
+++ b/main.tf
@@ -14,7 +14,9 @@ terraform {
 
     pihole = {
       source  = "ryanwholey/pihole"
-      version = "~> 0.2"
+
+      # https://github.com/ryanwholey/terraform-provider-pihole/issues/86#issuecomment-2828469631
+      version = "2.0.0-beta.1"
     }
 
     tailscale = {
@@ -29,7 +31,8 @@ provider "linode" {
 }
 
 provider "pihole" {
-  url = "https://pihole.kejadlen.dev"
+  # url = "https://pihole.kejadlen.dev" # on-fire-within
+  url = "http://mountain-of-faith"
   # api_token = # PIHOLE_API_TOKEN
 }
 
@@ -67,6 +70,10 @@ data "tailscale_device" "lotus_land_story" {
   hostname = "lotus-land-story"
 }
 
+data "tailscale_device" "mountain_of_faith" {
+  hostname = "mountain-of-faith"
+}
+
 data "tailscale_device" "on_fire_within" {
   hostname = "on-fire-within"
 }
@@ -88,9 +95,17 @@ resource "linode_domain" "chislan_family" {
   soa_email = var.soa_email
 }
 
-resource "pihole_dns_record" "rss" {
+resource "pihole_dns_record" "git_kejadlen_dev" {
   for_each = toset(["git.kejadlen.dev"])
 
   domain = each.key
   ip     = data.tailscale_device.lotus_land_story.addresses.0
 }
+
+# doesn't seem to work
+# resource "pihole_dns_record" "pi_hole" {
+#   for_each = toset(["pi.hole"])
+#
+#   domain = each.key
+#   ip     = data.tailscale_device.mountain_of_faith.addresses.0
+# }
diff --git a/mountain-of-faith/README.md b/mountain-of-faith/README.md
new file mode 100644
index 0000000..283c3e9
--- /dev/null
+++ b/mountain-of-faith/README.md
@@ -0,0 +1,18 @@
+# mountain of faith
+
+- Set the IP manually either in `~/.ssh/config.local` or `hosts.yml`
+
+```sh
+ansible-playbook -l mountain-of-faith mountain-of-faith/main.yml
+```
+
+- Log into Tailscale: `sudo tailscale up`
+- Disable the key expiry in the [Tailscale console](https://login.tailscale.com/admin/machines)
+- Remove the manual IP now that the host is on the tailnet
+
+## minio
+
+```sh
+mc alias set minio http://mountain-of-faith:9000 root $MINIO_PASSWORD
+mc admin accesskey create minio root --policy mountain-of-faith/minio/arq.json
+```
diff --git a/mountain-of-faith/caddy.yml b/mountain-of-faith/caddy.yml
new file mode 100644
index 0000000..1e4c445
--- /dev/null
+++ b/mountain-of-faith/caddy.yml
@@ -0,0 +1,52 @@
+- name: Caddy
+  tags: caddy
+  hosts: mountain-of-faith
+  become: true
+  vars_files:
+    - vars.private
+  vars:
+    # don't forget to update docker-caddy-tailscale first!
+    version: 2.10.0
+  tasks:
+
+    - name: Make /mnt/mountain-of-faith/caddy
+      ansible.builtin.file:
+        path: /mnt/mountain-of-faith/{{ item }}
+        state: directory
+        mode: "0755"
+      loop:
+        - caddy
+        - caddy/config
+        - caddy/data
+
+    # TODO Reload Caddy when this changes:
+    #      docker exec -w /etc/caddy $caddy_container_id caddy reload
+    - name: Set up Caddyfile
+      ansible.builtin.template:
+        src: templates/Caddyfile
+        dest: /mnt/mountain-of-faith/caddy/Caddyfile
+        mode: "0644"
+
+    - name: Run Caddy
+      community.docker.docker_container:
+        name: caddy
+        image: ghcr.io/kejadlen/caddy-tailscale:{{ version }}
+        restart: true
+        env:
+          TS_AUTHKEY: "{{ tailscale.auth_key }}"
+        ports:
+          - "80:80"
+          - "443:443"
+          - "443:443/udp"
+          - "2019:2019"
+        volumes:
+          - /mnt/mountain-of-faith/caddy/Caddyfile:/etc/caddy/Caddyfile
+          - /mnt/mountain-of-faith/caddy/data:/data
+          - /mnt/mountain-of-faith/caddy/config:/config
+        restart_policy: unless-stopped
+        networks:
+          - name: mountain-of-faith
+        etc_hosts:
+          host.docker.internal: host-gateway
+
+# vim: ft=yaml.ansible
diff --git a/mountain-of-faith/main.yml b/mountain-of-faith/main.yml
new file mode 100644
index 0000000..a969517
--- /dev/null
+++ b/mountain-of-faith/main.yml
@@ -0,0 +1,125 @@
+- name: Update system things
+  hosts: mountain-of-faith
+  become: true
+  tasks:
+
+    - name: Remove big packages
+      ansible.builtin.apt:
+        name: "{{ item }}"
+        state: absent
+        update_cache: true
+        autoremove: true
+      loop:
+        - chromium
+
+    - name: Update all packages to their latest version
+      ansible.builtin.apt:
+        name: "*"
+        state: latest
+        update_cache: true
+        autoremove: true
+
+    - name: Upgrade the OS (apt-get dist-upgrade)
+      ansible.builtin.apt:
+        upgrade: dist
+
+- import_playbook: ../playbooks/tailscale.yml
+- import_playbook: ../playbooks/docker.yml
+
+- name: Mount USB drive
+  hosts: mountain-of-faith
+  become: true
+  tasks:
+
+    # Use `lsblk -f` to find the device
+    - name: Mount /dev/sda to /mnt/mountain-of-faith
+      ansible.posix.mount:
+        path: /mnt/mountain-of-faith
+        src: /dev/sda
+        fstype: ext4
+        state: mounted
+
+- name: Set up Restic
+  tags: restic
+  hosts: mountain-of-faith
+  become: true
+  tasks:
+
+    - name: Create Restic user
+      ansible.builtin.user:
+        name: restic
+        system: true
+        state: present
+
+    - name: Add SSH key for restic user
+      ansible.posix.authorized_key:
+        user: restic
+        key: "{{ item }}"
+      loop:
+        - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrlVay1GlpCuhYSUup/K/gRYmltycViPBAdZ8t9OKtu # lotus-land-story
+        - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPgX0tA28NO3djYaV++JxggR0t47xou+R0HQVwVjzj8s # 1password
+
+    - name: Create Restic directory
+      ansible.builtin.file:
+        path: /mnt/mountain-of-faith/restic
+        state: directory
+        owner: restic
+        group: restic
+        mode: "0755"
+
+
+- name: Create Docker network
+  hosts: mountain-of-faith
+  become: true
+  tasks:
+    - name: Create IPv6 Docker network
+      community.docker.docker_network:
+        name: mountain-of-faith
+        enable_ipv6: true
+        state: present
+
+- name: Run Pi-Hole
+  tags: pihole
+  hosts: mountain-of-faith
+  vars_files:
+    - vars.private
+  vars:
+    version: 2025.06.2
+  tasks:
+
+    - name: Run the Pi-Hole container
+      become: true
+      community.docker.docker_container:
+        name: pihole
+        image: pihole/pihole:{{ version }}
+        ports:
+          - "53:53/tcp"
+          - "53:53/udp"
+          - "80:80/tcp"
+          - "443:443/tcp"
+          #- "67:67/udp" # DHCP
+          #- "123:123/udp" # NTP
+        networks:
+          - name: mountain-of-faith
+        env:
+          TZ: "America/Los_Angeles"
+          FTLCONF_webserver_api_password: "{{ pihole.password }}"
+          FTLCONF_dns_listeningMode: "all"
+          FTLCONF_dns_upstreams: "1.1.1.1;1.0.0.1;2606:4700:4700::1111;2606:4700:4700::1001" # cloudflare
+        volumes:
+          - "./etc-pihole:/etc/pihole"
+        capabilities:
+          - NET_ADMIN
+          - SYS_TIME
+          - SYS_NICE
+        restart_policy: unless-stopped
+        state: started
+
+- import_playbook: minio.yml
+
+# TODO smokeping
+# TODO syncthing
+
+- import_playbook: caddy.yml
+
+# vim: ft=yaml.ansible
diff --git a/mountain-of-faith/minio.yml b/mountain-of-faith/minio.yml
new file mode 100644
index 0000000..947c612
--- /dev/null
+++ b/mountain-of-faith/minio.yml
@@ -0,0 +1,138 @@
+# https://min.io/docs/minio/linux/operations/install-deploy-manage/deploy-minio-single-node-single-drive.html
+
+- name: "Deploy MinIO: Single Node Single Drive"
+  tags: minio
+  hosts: mountain-of-faith
+  become: true
+  vars_files:
+    - vars.private
+  vars:
+    minio_version: "20250613113347.0.0"
+    minio_data_dir: /mnt/data
+  tasks:
+
+    - name: Assert distribution is Debian
+      ansible.builtin.assert:
+        that:
+          - ansible_distribution == "Debian"
+        fail_msg: "This playbook only supports Debian distributions"
+
+    # wget https://dl.min.io/server/minio/release/linux-arm64/archive/minio_20250613113347.0.0_arm64.deb -O minio.deb
+    # sudo dpkg -i minio.deb
+    - name: Install MinIO DEB package
+      ansible.builtin.apt:
+        deb: https://dl.min.io/server/minio/release/linux-arm64/archive/minio_{{ minio_version }}_arm64.deb
+        state: present
+
+    - name: Create MinIO systemd service file
+      ansible.builtin.copy:
+        content: |
+          [Unit]
+          Description=MinIO
+          Documentation=https://min.io/docs/minio/linux/index.html
+          Wants=network-online.target
+          After=network-online.target
+          AssertFileIsExecutable=/usr/local/bin/minio
+
+          [Service]
+          WorkingDirectory=/usr/local
+
+          User=minio
+          Group=minio
+          ProtectProc=invisible
+
+          EnvironmentFile=-/etc/default/minio
+          ExecStartPre=/bin/bash -c "if [ -z \"${MINIO_VOLUMES}\" ]; then echo \"Variable MINIO_VOLUMES not set in /etc/default/minio\"; exit 1; fi"
+          ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
+
+          # MinIO RELEASE.2023-05-04T21-44-30Z adds support for Type=notify (https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=)
+          # This may improve systemctl setups where other services use `After=minio.server`
+          # Uncomment the line to enable the functionality
+          # Type=notify
+
+          # Let systemd restart this service always
+          Restart=always
+
+          # Specifies the maximum file descriptor number that can be opened by this process
+          LimitNOFILE=65536
+
+          # Specifies the maximum number of threads this process can create
+          TasksMax=infinity
+
+          # Disable timeout logic and wait until process is stopped
+          TimeoutStopSec=infinity
+          SendSIGKILL=no
+
+          [Install]
+          WantedBy=multi-user.target
+
+          # Built for ${project.name}-${project.version} (${project.name})
+        dest: /usr/lib/systemd/system/minio.service
+        owner: root
+        group: root
+        mode: "0644"
+      notify: restart minio
+
+    # groupadd -r minio-user
+    - name: Create MinIO group
+      ansible.builtin.group:
+        name: minio
+        system: true
+        state: present
+
+    # useradd -M -r -g minio-user minio-user
+    - name: Create MinIO user
+      ansible.builtin.user:
+        name: minio
+        group: minio
+        system: true
+        shell: /sbin/nologin
+
+    # chown minio-user:minio-user /mnt/data
+    - name: Create MinIO data directory
+      ansible.builtin.file:
+        path: /mnt/mountain-of-faith/minio
+        state: directory
+        owner: minio
+        group: minio
+        mode: "0755"
+
+    - name: Create MinIO environment file
+      ansible.builtin.copy:
+        content: |
+          # MINIO_ROOT_USER and MINIO_ROOT_PASSWORD sets the root account for the MinIO server.
+          # This user has unrestricted permissions to perform S3 and administrative API operations on any resource in the deployment.
+          # Omit to use the default values 'minioadmin:minioadmin'.
+          # MinIO recommends setting non-default values as a best practice, regardless of environment
+
+          MINIO_ROOT_USER=root
+          MINIO_ROOT_PASSWORD="{{ minio.password }}"
+
+          # MINIO_VOLUMES sets the storage volume or path to use for the MinIO server.
+
+          MINIO_VOLUMES="/mnt/mountain-of-faith/minio"
+
+          # MINIO_OPTS sets any additional commandline options to pass to the MinIO server.
+          # For example, `--console-address :9001` sets the MinIO Console listen port
+          MINIO_OPTS="--console-address :9001"
+        dest: /etc/default/minio
+        owner: root
+        group: root
+        mode: "0644"
+      notify: restart minio
+
+    - name: Enable and start MinIO service
+      ansible.builtin.systemd:
+        name: minio
+        enabled: true
+        state: started
+        daemon_reload: true
+
+  handlers:
+    - name: restart minio
+      ansible.builtin.systemd:
+        name: minio
+        state: restarted
+        daemon_reload: true
+
+# vim: ft=yaml.ansible
diff --git a/mountain-of-faith/minio/arq.json b/mountain-of-faith/minio/arq.json
new file mode 100644
index 0000000..378f0a2
--- /dev/null
+++ b/mountain-of-faith/minio/arq.json
@@ -0,0 +1,15 @@
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "s3:*"
+      ],
+      "Resource": [
+        "arn:aws:s3:::arq",
+        "arn:aws:s3:::arq/*"
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/playbooks/docker.yml b/playbooks/docker.yml
new file mode 100644
index 0000000..c89c09f
--- /dev/null
+++ b/playbooks/docker.yml
@@ -0,0 +1,58 @@
+# https://docs.docker.com/engine/install/debian/
+- name: Install Docker
+  tags: docker
+  hosts: all
+  become: true
+  tasks:
+
+    - name: Assert distribution is Debian
+      ansible.builtin.assert:
+        that:
+          - ansible_distribution == "Debian"
+        fail_msg: "This playbook only supports Debian distributions"
+
+    # sudo apt-get update
+    # sudo apt-get install ca-certificates curl
+    - name: Install required packages
+      ansible.builtin.apt:
+        name:
+          - ca-certificates
+          - curl
+        state: present
+        update_cache: true
+
+    # sudo install -m 0755 -d /etc/apt/keyrings
+    # sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
+    # sudo chmod a+r /etc/apt/keyrings/docker.asc
+    - name: Download Docker GPG key
+      ansible.builtin.get_url:
+        url: https://download.docker.com/linux/debian/gpg
+        dest: /usr/share/keyrings/docker.asc
+        mode: "0644"
+
+    - name: Get system architecture
+      ansible.builtin.command: dpkg --print-architecture
+      register: dpkg_arch
+      changed_when: false
+
+    # echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+    - name: Add Docker repository
+      ansible.builtin.apt_repository:
+        repo: deb [arch={{ dpkg_arch.stdout }} signed-by=/usr/share/keyrings/docker.asc] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable
+        state: present
+
+    # sudo apt-get update
+    # sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+    - name: Install Docker
+      ansible.builtin.apt:
+        name: "{{ item }}"
+        state: present
+        update_cache: true
+      loop:
+        - docker-ce
+        - docker-ce-cli
+        - containerd.io
+        - docker-buildx-plugin
+        - docker-compose-plugin
+
+# vim: ft=yaml.ansible
diff --git a/playbooks/tailscale.yml b/playbooks/tailscale.yml
index 0dfb9b6..9b59aad 100644
--- a/playbooks/tailscale.yml
+++ b/playbooks/tailscale.yml
@@ -1,53 +1,57 @@
-# https://tailscale.com/download/linux/debian-bullseye
+# https://tailscale.com/download/linux/debian-bookworm
 - name: Install Tailscale
+  tags: tailscale
   hosts: all
   become: true
   tasks:
 
-    # curl -fsSL https://pkgs.tailscale.com/stable/debian/bullseye.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
+    - name: Assert distribution is Debian
+      ansible.builtin.assert:
+        that:
+          - ansible_distribution == "Debian"
+        fail_msg: "This playbook only supports Debian distributions"
+
+    # curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
     - name: Download Tailscale package signing key
       ansible.builtin.get_url:
-        url: https://pkgs.tailscale.com/stable/debian/bullseye.noarmor.gpg
+        url: https://pkgs.tailscale.com/stable/debian/{{ ansible_distribution_release }}.noarmor.gpg
         dest: /usr/share/keyrings/tailscale-archive-keyring.gpg
         mode: "0644"
 
-    # curl -fsSL https://pkgs.tailscale.com/stable/debian/bullseye.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
+    # curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
     - name: Add Tailscale repository
       ansible.builtin.apt_repository:
-        repo: deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/debian bullseye main
+        repo: deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/debian {{ ansible_distribution_release }} main
         state: present
 
     # sudo apt-get update
-    - name: Update apt-get
-      ansible.builtin.apt:
-        update_cache: true
-
     # sudo apt-get install tailscale
     - name: Install Tailscale
-      ansible.builtin.package:
+      ansible.builtin.apt:
         name: tailscale
         state: present
+        update_cache: true
 
-- name: Restrict tailscaled logging
-  hosts: all
-  become: true
-  tasks:
-
-    - name: Create systemd override dir for tailscaled
-      ansible.builtin.file:
-        path: /etc/systemd/system/tailscaled.service.d
-        state: directory
-        mode: "0644"
-
-    - name: Create systemd override
-      ansible.builtin.copy:
-        content: |
-          [Service]
-          LogLevelMax=notice
-        dest: /etc/systemd/system/tailscaled.service.d/override.conf
-        mode: "0644"
-      notify:
-        - Restart Tailscale
+# - name: Restrict tailscaled logging
+#   hosts: all
+#   become: true
+#   tasks:
+#
+#     - name: Create systemd override dir for tailscaled
+#       ansible.builtin.file:
+#         path: /etc/systemd/system/tailscaled.service.d
+#         state: directory
+#         mode: "0644"
+#
+#     - name: Create systemd override
+#       ansible.builtin.copy:
+#         content: |
+#           [Service]
+#           LogLevelMax=notice
+#         dest: /etc/systemd/system/tailscaled.service.d/override.conf
+#         mode: "0644"
+#       notify:
+#         - Restart Tailscale
 
   handlers: