Split up main.yml into multiple playbooks
change
commit 7f6c7eb9a10aeb110831ec1826775fba79234fe9
author Alpha Chen <alpha@kejadlen.dev>
date
parent 7690c459
diff --git a/lotus-land-story/caddy.yml b/lotus-land-story/caddy.yml
new file mode 100644
index 0000000..1c9de52
--- /dev/null
+++ b/lotus-land-story/caddy.yml
@@ -0,0 +1,49 @@
+- name: Set up Caddy
+  hosts: lotus-land-story
+  vars_files:
+    - vars.yml
+  tasks:
+
+    - name: Make /mnt/lotus-land-story/caddy
+      ansible.builtin.file:
+        path: /mnt/lotus-land-story/{{ item }}
+        state: directory
+        mode: "0755"
+      loop:
+        - caddy
+        - caddy/data
+
+    - name: Set up Caddyfile
+      ansible.builtin.template:
+        src: templates/Caddyfile
+        dest: /mnt/lotus-land-story/caddy/Caddyfile
+        mode: "0644"
+
+    - name: Run caddy
+      community.docker.docker_compose:
+        project_name: caddy
+        definition:
+          version: "3.3"
+          services:
+            caddy:
+              image: caddy:2
+              container_name: caddy
+              ports:
+                - "80:80"
+                - "443:443"
+                - "2019:2019"
+              volumes:
+                - /mnt/lotus-land-story/caddy/Caddyfile:/etc/caddy/Caddyfile
+                - /mnt/lotus-land-story/caddy/data:/data
+                - caddy-config:/config
+              restart: unless-stopped
+              extra_hosts:
+                - "host.docker.internal:host-gateway"
+          volumes:
+            caddy-config:
+
+  handlers:
+    - name: Import restarts
+      ansible.builtin.import_tasks: restarts.yml
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/docker.yml b/lotus-land-story/docker.yml
new file mode 100644
index 0000000..4000841
--- /dev/null
+++ b/lotus-land-story/docker.yml
@@ -0,0 +1,78 @@
+# https://docs.docker.com/engine/install/debian/#install-using-the-repository
+- name: Set up Docker
+  hosts: lotus-land-story
+  tasks:
+
+    - name: Install Docker requirements
+      ansible.builtin.apt:
+        pkg:
+          - ca-certificates
+          - curl
+          - gnupg
+        state: present
+
+    - name: Make /etc/apt/keyrings
+      ansible.builtin.file:
+        path: /etc/apt/keyrings
+        state: directory
+        mode: "0755"
+
+    - name: Download Docker GPG key
+      ansible.builtin.shell: |
+        set -o pipefail
+        curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
+      args:
+        creates: /etc/apt/keyrings/docker.gpg
+
+    - name: Get architecture
+      ansible.builtin.command: dpkg --print-architecture
+      register: arch
+      changed_when: arch.rc != 0
+
+    - name: Set up Docker repository
+      ansible.builtin.template:
+        src: templates/docker.list
+        dest: /etc/apt/sources.list.d/docker.list
+        mode: "0644"
+
+    - name: Install Docker
+      ansible.builtin.apt:
+        pkg:
+          - docker-ce
+          - docker-ce-cli
+          - containerd.io
+          - docker-buildx-plugin
+          - docker-compose-plugin
+        update_cache: true
+
+    - name: Create Docker volume location
+      ansible.builtin.file:
+        path: /mnt/lotus-land-story/docker
+        state: directory
+        mode: "0755"
+
+    - name: Get docker0 IP address
+      ansible.builtin.shell: ip -4 -o addr show docker0 | awk '{print $4}' # noqa: risky-shell-pipe
+      vars:
+        executable: /usr/bin/bash
+      register: docker_ip
+      changed_when: docker_ip.rc != 0
+
+    - name: Save docker_ip fact
+      ansible.builtin.set_fact:
+        docker_ip:
+          cidr: "{{ docker_ip.stdout }}"
+          address: "{{ docker_ip.stdout | ansible.utils.ipaddr('address') }}"
+
+    - name: Configure Docker daemon
+      ansible.builtin.template:
+        src: templates/daemon.json
+        dest: /etc/docker/daemon.json
+        mode: "0644"
+      notify: Restart docker
+
+  handlers:
+    - name: Import restarts
+      ansible.builtin.import_tasks: restarts.yml
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/grafana.yml b/lotus-land-story/grafana.yml
new file mode 100644
index 0000000..2c6fc5d
--- /dev/null
+++ b/lotus-land-story/grafana.yml
@@ -0,0 +1,62 @@
+- name: Set up Grafana
+  hosts: lotus-land-story
+  tasks:
+
+    - name: Create Grafana dir
+      ansible.builtin.file:
+        path: /mnt/lotus-land-story/grafana/provisioning/{{ item }}
+        state: directory
+        mode: "0755"
+      loop:
+        - datasources
+
+    - name: Configure grafana
+      ansible.builtin.copy:
+        dest: /mnt/lotus-land-story/grafana/grafana.ini
+        content: |
+          [metrics]
+          enabled             = true
+          disable_total_stats = false
+        mode: "0644"
+
+    - name: Provision Grafana
+      ansible.builtin.copy:
+        dest: /mnt/lotus-land-story/grafana/provisioning/datasources/prometheus.yml
+        content: |
+          apiVersion: 1
+
+          datasources:
+            - name: Prometheus
+              type: prometheus
+              # Access mode - proxy (server in the UI) or direct (browser in the UI).
+              access: proxy
+              url: http://host.docker.internal:9090
+              jsonData:
+                httpMethod: POST
+                manageAlerts: true
+                prometheusType: Prometheus
+                prometheusVersion: 2.37.0
+        mode: "0644"
+
+    - name: Run Grafana
+      community.docker.docker_compose:
+        project_name: grafana
+        remove_orphans: true
+        definition:
+          version: "3.3"
+          services:
+            grafana:
+              image: grafana/grafana-oss:latest
+              ports:
+                - "3000:3000"
+              volumes:
+                - /mnt/lotus-land-story/grafana/grafana.ini:/etc/grafana/grafana.ini
+                - /mnt/lotus-land-story/grafana/provisioning:/etc/grafana/provisioning
+                - grafana:/var/lib/grafana
+              restart: unless-stopped
+              extra_hosts:
+                - "host.docker.internal:host-gateway"
+          volumes:
+            grafana:
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/main.yml b/lotus-land-story/main.yml
index fd34eed..6050cfa 100644
--- a/lotus-land-story/main.yml
+++ b/lotus-land-story/main.yml
@@ -1,35 +1,13 @@
-- name: Set up lotus-land-story
+- name: Base setup for lotus-land-story
   hosts: lotus-land-story
   vars_files:
     - vars.yml
   tasks:
-
-    # https://wiki.debian.org/PostgreSql
-    - name: Install postgres
-      ansible.builtin.apt:
-        pkg:
-          - postgresql
-          - postgresql-client
-        state: present
-
     - name: Always mount the lotus-land-story volume
       ansible.builtin.lineinfile:
         dest: /etc/fstab
         line: "{{ linode_volume }} /mnt/lotus-land-story ext4 defaults,noatime,nofail 0 2"
         state: present
-    - name: Make /mnt/lotus-land-story/postgresql
-      ansible.builtin.file:
-        path: /mnt/lotus-land-story/postgresql
-        state: directory
-        owner: postgres
-        mode: "0700"
-    - name: Set data directory to volume
-      ansible.builtin.lineinfile:
-        dest: "/etc/postgresql/13/main/postgresql.conf"
-        regexp: '^#?data_directory ='
-        line: "data_directory = '/mnt/lotus-land-story/postgresql'"
-        state: present
-      notify: Restart postgres
 
     - name: Install ansible requirements
       ansible.builtin.apt:
@@ -40,347 +18,78 @@
           - python3-psycopg2
         state: present
 
-    # https://docs.docker.com/engine/install/debian/#install-using-the-repository
-    - name: Install docker
-      block:
-        - name: Install docker requirements
-          ansible.builtin.apt:
-            pkg:
-              - ca-certificates
-              - curl
-              - gnupg
-            state: present
-        - name: Make /etc/apt/keyrings
-          ansible.builtin.file:
-            path: /etc/apt/keyrings
-            state: directory
-            mode: "0755"
-        - name: Download Docker GPG key
-          ansible.builtin.shell: |
-            set -o pipefail
-            curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
-          args:
-            creates: /etc/apt/keyrings/docker.gpg
-        - name: Get architecture
-          ansible.builtin.command: dpkg --print-architecture
-          register: arch
-          changed_when: arch.rc != 0
-        - name: Set up docker repository
-          ansible.builtin.template:
-            src: templates/docker.list
-            dest: /etc/apt/sources.list.d/docker.list
-            mode: "0644"
-        - name: Install docker
-          ansible.builtin.apt:
-            pkg:
-              - docker-ce
-              - docker-ce-cli
-              - containerd.io
-              - docker-buildx-plugin
-              - docker-compose-plugin
-            update_cache: true
-        - name: Get docker0 IP address
-          ansible.builtin.shell: ip -4 -o addr show docker0 | awk '{print $4}' # noqa: risky-shell-pipe
-          vars:
-            executable: /usr/bin/bash
-          register: docker_ip
-          changed_when: docker_ip.rc != 0
-        - name: Set docker ip fact
-          ansible.builtin.set_fact:
-            docker_ip:
-              cidr: "{{ docker_ip.stdout }}"
-              address: "{{ docker_ip.stdout | ansible.utils.ipaddr('address') }}"
-        - name: Create Docker volume location
-          ansible.builtin.file:
-            path: /mnt/lotus-land-story/docker
-            state: directory
-            mode: "0755"
-        - name: Docker config
-          ansible.builtin.template:
-            src: templates/daemon.json
-            dest: /etc/docker/daemon.json
-            mode: "0644"
-          notify: Restart docker
-
-
-    - name: Set up postgres
-      become: true
-      become_user: postgres
-      block:
-
-        - name: Listen on docker0 interface
-          ansible.builtin.lineinfile:
-            dest: "/etc/postgresql/13/main/conf.d/listen.conf"
-            regexp: '^#?listen_addresses='
-            line: "listen_addresses='localhost,{{ docker_ip.address }}'"
-            state: present
-            create: true
-            mode: "0644"
-          notify: Restart postgres
-
-    - name: Set up postgres for miniflux
-      become: true
-      become_user: postgres
-      block:
-
-        # https://miniflux.app/docs/installation.html#docker
-        - name: Create a miniflux db
-          community.postgresql.postgresql_db:
-            name: miniflux
-          notify: Restart postgres
-        - name: Create a miniflux db user
-          community.postgresql.postgresql_user:
-            db: miniflux
-            name: miniflux
-            password: "{{ miniflux_db_password }}"
-          notify: Restart postgres
-        - name: Grant miniflux access
-          community.postgresql.postgresql_pg_hba:
-            dest: /etc/postgresql/13/main/pg_hba.conf
-            contype: host
-            users: miniflux
-            source: samenet
-            databases: miniflux
-            create: true
-          notify: Restart postgres
-        - name: Install hstore
-          community.postgresql.postgresql_ext:
-            name: hstore
-            db: miniflux
-          notify: Restart postgres
-
-        - name: Set up postgres-exporter
-          block:
-            - name: Get postgres roles
-              community.postgresql.postgresql_info:
-                filter: roles
-              register: postgres_info
-            - name: Add postgres permissions for postgres-exporter
-              community.postgresql.postgresql_query:
-                query: |
-                  CREATE USER prometheus;
-                  ALTER USER prometheus SET SEARCH_PATH TO prometheus,pg_catalog;
-
-                  CREATE SCHEMA prometheus AUTHORIZATION prometheus;
-
-                  CREATE FUNCTION prometheus.f_select_pg_stat_activity()
-                  RETURNS setof pg_catalog.pg_stat_activity
-                  LANGUAGE sql
-                  SECURITY DEFINER
-                  AS $$
-                    SELECT * from pg_catalog.pg_stat_activity;
-                  $$;
-
-                  CREATE FUNCTION prometheus.f_select_pg_stat_replication()
-                  RETURNS setof pg_catalog.pg_stat_replication
-                  LANGUAGE sql
-                  SECURITY DEFINER
-                  AS $$
-                    SELECT * from pg_catalog.pg_stat_replication;
-                  $$;
-
-                  CREATE VIEW prometheus.pg_stat_replication
-                  AS
-                    SELECT * FROM prometheus.f_select_pg_stat_replication();
-
-                  CREATE VIEW prometheus.pg_stat_activity
-                  AS
-                    SELECT * FROM prometheus.f_select_pg_stat_activity();
-
-                  GRANT SELECT ON prometheus.pg_stat_replication TO prometheus;
-                  GRANT SELECT ON prometheus.pg_stat_activity TO prometheus;
-              when: "'prometheus' not in postgres_info.roles"
+- import_playbook: postgres.yml # noqa: name[play]
+- import_playbook: docker.yml # noqa: name[play]
 
-    - name: Run miniflux
-      community.docker.docker_compose:
-        project_name: miniflux
-        definition:
-          version: "3.3"
-          services:
-            miniflux:
-              image: miniflux/miniflux:latest
-              ports:
-                - "8080:8080"
-              environment:
-                - DATABASE_URL=postgres://miniflux:{{ miniflux_db_password }}@host.docker.internal/miniflux
-                - RUN_MIGRATIONS=1
-                - CREATE_ADMIN=1
-                - ADMIN_USERNAME=alpha
-                - ADMIN_PASSWORD={{ miniflux_password }}
-                - BASE_URL=https://rss.{{ domain }}
-              restart: unless-stopped
-              extra_hosts:
-                - "host.docker.internal:host-gateway"
+- name: Set up postgres
+  hosts: lotus-land-story
+  tasks:
 
-    - name: Install host exporters
-      ansible.builtin.apt:
-        pkg:
-          - prometheus-node-exporter
-          - prometheus-postgres-exporter
-        state: present
-    # /usr/share/doc/prometheus-postgres-exporter/README.Debian
-    - name: Configure postgres-exporter
+    - name: Set up postgres to listen on docker0 interface
       ansible.builtin.lineinfile:
-        dest: /etc/default/prometheus-postgres-exporter
-        regexp: '^DATA_SOURCE_NAME='
-        line: "DATA_SOURCE_NAME='user=prometheus host=/run/postgresql dbname=postgres'"
+        dest: "/etc/postgresql/13/main/conf.d/listen.conf"
+        regexp: '^#?listen_addresses='
+        line: "listen_addresses='localhost,{{ docker_ip.address }}'"
         state: present
-    - name: Create Prometheus dir
-      ansible.builtin.file:
-        path: /mnt/lotus-land-story/prometheus
-        state: directory
-        mode: "0755"
-    - name: Prometheus config
-      ansible.builtin.copy:
-        dest: /mnt/lotus-land-story/prometheus/prometheus.yml
-        content: |
-          global:
-            # Attach these labels to any time series or alerts when communicating with
-            # external systems (federation, remote storage, Alertmanager).
-            external_labels:
-              monitor: 'codelab-monitor'
-
-          scrape_configs:
-            - job_name: 'prometheus'
-              static_configs:
-                - targets: ['localhost:9090']
-
-            - job_name: node
-              static_configs:
-                - targets: ['host.docker.internal:9100']
-
-            - job_name: 'docker'
-              static_configs:
-                - targets: ['host.docker.internal:9323']
-
-            - job_name: caddy
-              static_configs:
-                - targets: ['host.docker.internal:2019']
-
-            - job_name: 'grafana'
-              static_configs:
-                - targets: ['host.docker.internal:3000']
-
-            - job_name: 'postgres'
-              static_configs:
-                - targets: ['host.docker.internal:9187']
-        mode: "0644"
-    - name: Run Prometheus
-      community.docker.docker_compose:
-        project_name: prometheus
-        remove_orphans: true
-        definition:
-          version: "3.3"
-          services:
-            prometheus:
-              image: prom/prometheus:latest
-              ports:
-                - "9090:9090"
-              volumes:
-                - /mnt/lotus-land-story/prometheus:/etc/prometheus
-              restart: unless-stopped
-              extra_hosts:
-                - "host.docker.internal:host-gateway"
-
-    - name: Create Grafana dir
-      ansible.builtin.file:
-        path: /mnt/lotus-land-story/grafana/provisioning/{{ item }}
-        state: directory
-        mode: "0755"
-      loop:
-        - datasources
-    - name: Configure grafana
-      ansible.builtin.copy:
-        dest: /mnt/lotus-land-story/grafana/grafana.ini
-        content: |
-          [metrics]
-          enabled             = true
-          disable_total_stats = false
+        create: true
         mode: "0644"
-    - name: Provision Grafana
-      ansible.builtin.copy:
-        dest: /mnt/lotus-land-story/grafana/provisioning/datasources/prometheus.yml
-        content: |
-          apiVersion: 1
+      notify: Restart postgres
 
-          datasources:
-            - name: Prometheus
-              type: prometheus
-              # Access mode - proxy (server in the UI) or direct (browser in the UI).
-              access: proxy
-              url: http://host.docker.internal:9090
-              jsonData:
-                httpMethod: POST
-                manageAlerts: true
-                prometheusType: Prometheus
-                prometheusVersion: 2.37.0
-        mode: "0644"
-    - name: Run Grafana
-      community.docker.docker_compose:
-        project_name: grafana
-        remove_orphans: true
-        definition:
-          version: "3.3"
-          services:
-            grafana:
-              image: grafana/grafana-oss:latest
-              ports:
-                - "3000:3000"
-              volumes:
-                - /mnt/lotus-land-story/grafana/grafana.ini:/etc/grafana/grafana.ini
-                - /mnt/lotus-land-story/grafana/provisioning:/etc/grafana/provisioning
-                - grafana:/var/lib/grafana
-              restart: unless-stopped
-              extra_hosts:
-                - "host.docker.internal:host-gateway"
-          volumes:
-            grafana:
+  handlers:
+    - name: Import restarts
+      ansible.builtin.import_tasks: restarts.yml
 
-    - name: Make /mnt/lotus-land-story/caddy
-      ansible.builtin.file:
-        path: /mnt/lotus-land-story/{{ item }}
-        state: directory
-        mode: "0755"
-      loop:
-        - caddy
-        - caddy/data
-    - name: Set up Caddyfile
-      ansible.builtin.template:
-        src: templates/Caddyfile
-        dest: /mnt/lotus-land-story/caddy/Caddyfile
-        mode: "0644"
-    - name: Run caddy
-      community.docker.docker_compose:
-        project_name: caddy
-        definition:
-          version: "3.3"
-          services:
-            caddy:
-              image: caddy:2
-              container_name: caddy
-              ports:
-                - "80:80"
-                - "443:443"
-                - "2019:2019"
-              volumes:
-                - /mnt/lotus-land-story/caddy/Caddyfile:/etc/caddy/Caddyfile
-                - /mnt/lotus-land-story/caddy/data:/data
-                - caddy-config:/config
-              restart: unless-stopped
-              extra_hosts:
-                - "host.docker.internal:host-gateway"
-          volumes:
-            caddy-config:
+- import_playbook: prometheus.yml # noqa: name[play]
 
-  handlers:
-    - name: Restart postgres
-      ansible.builtin.service:
-        name: postgresql
-        state: restarted
+- name: Set up prometheus user in postgres
+  hosts: lotus-land-story
+  become: true
+  become_user: postgres
+  tasks:
 
-    - name: Restart docker
-      ansible.builtin.service:
-        name: docker
-        state: restarted
+    - name: Get postgres roles
+      community.postgresql.postgresql_info:
+        filter: roles
+      register: postgres_info
+
+    - name: Add postgres permissions for postgres-exporter
+      community.postgresql.postgresql_query:
+        query: |
+          CREATE USER prometheus;
+          ALTER USER prometheus SET SEARCH_PATH TO prometheus,pg_catalog;
+
+          CREATE SCHEMA prometheus AUTHORIZATION prometheus;
+
+          CREATE FUNCTION prometheus.f_select_pg_stat_activity()
+          RETURNS setof pg_catalog.pg_stat_activity
+          LANGUAGE sql
+          SECURITY DEFINER
+          AS $$
+            SELECT * from pg_catalog.pg_stat_activity;
+          $$;
+
+          CREATE FUNCTION prometheus.f_select_pg_stat_replication()
+          RETURNS setof pg_catalog.pg_stat_replication
+          LANGUAGE sql
+          SECURITY DEFINER
+          AS $$
+            SELECT * from pg_catalog.pg_stat_replication;
+          $$;
+
+          CREATE VIEW prometheus.pg_stat_replication
+          AS
+            SELECT * FROM prometheus.f_select_pg_stat_replication();
+
+          CREATE VIEW prometheus.pg_stat_activity
+          AS
+            SELECT * FROM prometheus.f_select_pg_stat_activity();
+
+          GRANT SELECT ON prometheus.pg_stat_replication TO prometheus;
+          GRANT SELECT ON prometheus.pg_stat_activity TO prometheus;
+      when: "'prometheus' not in postgres_info.roles"
+
+- import_playbook: grafana.yml    # noqa: name[play]
+- import_playbook: miniflux.yml   # noqa: name[play]
+- import_playbook: caddy.yml      # noqa: name[play]
 
 # vim: ft=yaml.ansible
diff --git a/lotus-land-story/miniflux.yml b/lotus-land-story/miniflux.yml
new file mode 100644
index 0000000..8413ec5
--- /dev/null
+++ b/lotus-land-story/miniflux.yml
@@ -0,0 +1,70 @@
+# https://miniflux.app/docs/installation.html#docker
+
+- name: Set up the Miniflux db
+  hosts: lotus-land-story
+  become: true
+  become_user: postgres
+  vars_files:
+    - vars.yml
+  tasks:
+
+    - name: Create the Miniflux db
+      community.postgresql.postgresql_db:
+        name: miniflux
+      notify: Restart postgres
+
+    - name: Create the Miniflux db user
+      community.postgresql.postgresql_user:
+        db: miniflux
+        name: miniflux
+        password: "{{ miniflux_db_password }}"
+      notify: Restart postgres
+
+    - name: Grant Miniflux access to the db
+      community.postgresql.postgresql_pg_hba:
+        dest: /etc/postgresql/13/main/pg_hba.conf
+        contype: host
+        users: miniflux
+        source: samenet # TODO Can this be restricted to docker_ip?
+        databases: miniflux
+        create: true
+      notify: Restart postgres
+
+    - name: Install hstore
+      community.postgresql.postgresql_ext:
+        name: hstore
+        db: miniflux
+      notify: Restart postgres
+
+  handlers:
+    - name: Import restarts
+      ansible.builtin.import_tasks: restarts.yml
+
+- name: Set up the Miniflux db
+  hosts: lotus-land-story
+  vars_files:
+    - vars.yml
+  tasks:
+
+    - name: Run miniflux
+      community.docker.docker_compose:
+        project_name: miniflux
+        definition:
+          version: "3.3"
+          services:
+            miniflux:
+              image: miniflux/miniflux:latest
+              ports:
+                - "8080:8080"
+              environment:
+                - DATABASE_URL=postgres://miniflux:{{ miniflux_db_password }}@host.docker.internal/miniflux
+                - RUN_MIGRATIONS=1
+                - CREATE_ADMIN=1
+                - ADMIN_USERNAME=alpha
+                - ADMIN_PASSWORD={{ miniflux_password }}
+                - BASE_URL=https://rss.{{ domain }}
+              restart: unless-stopped
+              extra_hosts:
+                - "host.docker.internal:host-gateway"
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/postgres.yml b/lotus-land-story/postgres.yml
new file mode 100644
index 0000000..c5b0f58
--- /dev/null
+++ b/lotus-land-story/postgres.yml
@@ -0,0 +1,32 @@
+# https://wiki.debian.org/PostgreSql
+- name: Set up postgres
+  hosts: lotus-land-story
+  tasks:
+
+    - name: Install postgres
+      ansible.builtin.apt:
+        pkg:
+          - postgresql
+          - postgresql-client
+        state: present
+
+    - name: Make data directory
+      ansible.builtin.file:
+        path: /mnt/lotus-land-story/postgresql
+        state: directory
+        owner: postgres
+        mode: "0700"
+
+    - name: Set data directory
+      ansible.builtin.lineinfile:
+        dest: "/etc/postgresql/13/main/postgresql.conf"
+        regexp: '^#?data_directory ='
+        line: "data_directory = '/mnt/lotus-land-story/postgresql'"
+        state: present
+      notify: Restart postgres
+
+  handlers:
+    - name: Import restarts
+      ansible.builtin.import_tasks: restarts.yml
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/prometheus.yml b/lotus-land-story/prometheus.yml
new file mode 100644
index 0000000..d6d5670
--- /dev/null
+++ b/lotus-land-story/prometheus.yml
@@ -0,0 +1,79 @@
+- name: Set up Prometheus
+  hosts: lotus-land-story
+  tasks:
+
+    - name: Install host exporters
+      ansible.builtin.apt:
+        pkg:
+          - prometheus-node-exporter
+          - prometheus-postgres-exporter
+        state: present
+
+    # /usr/share/doc/prometheus-postgres-exporter/README.Debian
+    - name: Configure postgres-exporter
+      ansible.builtin.lineinfile:
+        dest: /etc/default/prometheus-postgres-exporter
+        regexp: '^DATA_SOURCE_NAME='
+        line: "DATA_SOURCE_NAME='user=prometheus host=/run/postgresql dbname=postgres'"
+        state: present
+
+    - name: Create Prometheus dir
+      ansible.builtin.file:
+        path: /mnt/lotus-land-story/prometheus
+        state: directory
+        mode: "0755"
+
+    - name: Prometheus config
+      ansible.builtin.copy:
+        dest: /mnt/lotus-land-story/prometheus/prometheus.yml
+        content: |
+          global:
+            # Attach these labels to any time series or alerts when communicating with
+            # external systems (federation, remote storage, Alertmanager).
+            external_labels:
+              monitor: 'codelab-monitor'
+
+          scrape_configs:
+            - job_name: 'prometheus'
+              static_configs:
+                - targets: ['localhost:9090']
+
+            - job_name: node
+              static_configs:
+                - targets: ['host.docker.internal:9100']
+
+            - job_name: 'docker'
+              static_configs:
+                - targets: ['host.docker.internal:9323']
+
+            - job_name: caddy
+              static_configs:
+                - targets: ['host.docker.internal:2019']
+
+            - job_name: 'grafana'
+              static_configs:
+                - targets: ['host.docker.internal:3000']
+
+            - job_name: 'postgres'
+              static_configs:
+                - targets: ['host.docker.internal:9187']
+        mode: "0644"
+
+    - name: Run Prometheus
+      community.docker.docker_compose:
+        project_name: prometheus
+        remove_orphans: true
+        definition:
+          version: "3.3"
+          services:
+            prometheus:
+              image: prom/prometheus:latest
+              ports:
+                - "9090:9090"
+              volumes:
+                - /mnt/lotus-land-story/prometheus:/etc/prometheus
+              restart: unless-stopped
+              extra_hosts:
+                - "host.docker.internal:host-gateway"
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/restarts.yml b/lotus-land-story/restarts.yml
new file mode 100644
index 0000000..27b44ff
--- /dev/null
+++ b/lotus-land-story/restarts.yml
@@ -0,0 +1,11 @@
+- name: Restart postgres
+  ansible.builtin.service:
+    name: postgresql
+    state: restarted
+
+- name: Restart docker
+  ansible.builtin.service:
+    name: docker
+    state: restarted
+
+# vim: ft=yaml.ansible