From 8cf5640757a6a728844f47ea21d15190771fb5db Mon Sep 17 00:00:00 2001 From: Samantha Atkins Date: Sat, 11 Apr 2026 18:07:35 -0400 Subject: [PATCH] Add Listmonk, Mattermost manifests; Ghost SMTP and device verification fix - Listmonk: newsletter/mailing list manager with PostgreSQL backend, NodePort 32375, Postmark SMTP. Replaces Ghost's broken Mailgun-only newsletter sending via n8n automation pipeline. - Mattermost: team messaging manifest, NodePort 32374, PostgreSQL backend. - Ghost: added Postmark SMTP config for transactional email, disabled staffDeviceVerification on all three instances (Ghost has no TOTP, only email-based verification which requires working SMTP). Co-Authored-By: Claude Opus 4.6 (1M context) --- k3s/ghost/ghost.yaml | 26 +++++ k3s/listmonk/listmonk-db-init.yaml | 45 +++++++++ k3s/listmonk/listmonk.yaml | 134 +++++++++++++++++++++++++ k3s/mattermost/mattermost-db-init.yaml | 43 ++++++++ k3s/mattermost/mattermost.yaml | 98 ++++++++++++++++++ 5 files changed, 346 insertions(+) create mode 100644 k3s/listmonk/listmonk-db-init.yaml create mode 100644 k3s/listmonk/listmonk.yaml create mode 100644 k3s/mattermost/mattermost-db-init.yaml create mode 100644 k3s/mattermost/mattermost.yaml diff --git a/k3s/ghost/ghost.yaml b/k3s/ghost/ghost.yaml index a612a29..f6ef1f6 100644 --- a/k3s/ghost/ghost.yaml +++ b/k3s/ghost/ghost.yaml @@ -64,6 +64,28 @@ spec: value: ghost1_db - name: url value: https://blog.the-fulfillment.org + - name: security__staffDeviceVerification + value: "false" + - name: mail__transport + value: SMTP + - name: mail__from + value: "Sister Sam " + - name: mail__options__host + value: smtp.postmarkapp.com + - name: mail__options__port + value: "587" + - name: mail__options__auth__user + valueFrom: + secretKeyRef: + name: ghost-secrets + key: postmark-token + - name: mail__options__auth__pass + valueFrom: + secretKeyRef: + name: ghost-secrets + key: postmark-token + - name: bulk_email__provider + value: smtp ports: - containerPort: 2368 volumeMounts: @@ -139,6 +161,8 @@ spec: value: ghost2_db - name: url value: https://blog.privacy-practice.com + - name: security__staffDeviceVerification + value: "false" - name: server__port value: "2369" ports: @@ -216,6 +240,8 @@ spec: value: ghost3_db - name: url value: https://blog.sjasoft.com + - name: security__staffDeviceVerification + value: "false" - name: server__port value: "2370" ports: diff --git a/k3s/listmonk/listmonk-db-init.yaml b/k3s/listmonk/listmonk-db-init.yaml new file mode 100644 index 0000000..f3ebadb --- /dev/null +++ b/k3s/listmonk/listmonk-db-init.yaml @@ -0,0 +1,45 @@ +# Listmonk DB Init Job +# Creates listmonk database and user in PostgreSQL. +# Run once before deploying Listmonk. +# +# Deploy: +# kubectl create secret generic listmonk-secret \ +# --from-literal=db-password='' \ +# --from-literal=admin-password='' \ +# --from-literal=postmark-token='' +# kubectl apply -f listmonk-db-init.yaml +# +# Watch completion: +# kubectl get jobs -w +# kubectl logs job/listmonk-db-init + +apiVersion: batch/v1 +kind: Job +metadata: + name: listmonk-db-init +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: listmonk-db-init + image: postgres:16 + env: + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: postgres-secret + key: password + - name: LISTMONK_DB_PASSWORD + valueFrom: + secretKeyRef: + name: listmonk-secret + key: db-password + command: + - /bin/sh + - -c + - | + psql -h postgres -U postgres <' \ +# --from-literal=admin-password='' \ +# --from-literal=postmark-token='' +# kubectl apply -f listmonk-db-init.yaml +# kubectl get jobs -w # wait for completion +# kubectl apply -f listmonk.yaml +# +# First run: Listmonk auto-runs DB migrations on startup. +# Admin UI: http://:32375 +# +# n8n integration: Ghost webhook -> n8n -> Listmonk API +# POST /api/campaigns to create campaign from Ghost post +# PUT /api/campaigns/{id}/status to trigger send + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: listmonk-uploads-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 5Gi + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: listmonk-config +data: + config.toml: | + [app] + address = "0.0.0.0:9000" + admin_username = "admin" + admin_password = "" + + [db] + host = "postgres" + port = 5432 + user = "listmonk_user" + password = "" + database = "listmonk_db" + ssl_mode = "disable" + max_open = 25 + max_idle = 25 + max_lifetime = "300s" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: listmonk +spec: + replicas: 1 + selector: + matchLabels: + app: listmonk + template: + metadata: + labels: + app: listmonk + spec: + containers: + - name: listmonk + image: listmonk/listmonk:latest + command: ["./listmonk", "--config", "/etc/listmonk/config.toml"] + env: + - name: LISTMONK_app__admin_password + valueFrom: + secretKeyRef: + name: listmonk-secret + key: admin-password + - name: LISTMONK_db__password + valueFrom: + secretKeyRef: + name: listmonk-secret + key: db-password + ports: + - containerPort: 9000 + volumeMounts: + - name: config + mountPath: /etc/listmonk + - name: uploads + mountPath: /listmonk/uploads + initContainers: + - name: listmonk-install + image: listmonk/listmonk:latest + command: ["./listmonk", "--install", "--idempotent", "--yes", "--config", "/etc/listmonk/config.toml"] + env: + - name: LISTMONK_app__admin_password + valueFrom: + secretKeyRef: + name: listmonk-secret + key: admin-password + - name: LISTMONK_db__password + valueFrom: + secretKeyRef: + name: listmonk-secret + key: db-password + volumeMounts: + - name: config + mountPath: /etc/listmonk + volumes: + - name: config + configMap: + name: listmonk-config + - name: uploads + persistentVolumeClaim: + claimName: listmonk-uploads-pvc + +--- +apiVersion: v1 +kind: Service +metadata: + name: listmonk +spec: + selector: + app: listmonk + ports: + - port: 9000 + targetPort: 9000 + nodePort: 32375 + type: NodePort diff --git a/k3s/mattermost/mattermost-db-init.yaml b/k3s/mattermost/mattermost-db-init.yaml new file mode 100644 index 0000000..9fdf8fc --- /dev/null +++ b/k3s/mattermost/mattermost-db-init.yaml @@ -0,0 +1,43 @@ +# Mattermost DB Init Job +# Creates mattermost database and user in PostgreSQL. +# Run once before deploying Mattermost. +# +# Deploy: +# kubectl create secret generic mattermost-secret \ +# --from-literal=db-password='' +# kubectl apply -f mattermost-db-init.yaml +# +# Watch completion: +# kubectl get jobs -w +# kubectl logs job/mattermost-db-init + +apiVersion: batch/v1 +kind: Job +metadata: + name: mattermost-db-init +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: mattermost-db-init + image: postgres:16 + env: + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: postgres-secret + key: password + - name: MATTERMOST_DB_PASSWORD + valueFrom: + secretKeyRef: + name: mattermost-secret + key: db-password + command: + - /bin/sh + - -c + - | + psql -h postgres -U postgres <' +# kubectl apply -f mattermost-db-init.yaml +# kubectl get jobs -w # wait for completion +# kubectl apply -f mattermost.yaml + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mattermost-data-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 10Gi + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mattermost-plugins-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 2Gi + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mattermost +spec: + replicas: 1 + selector: + matchLabels: + app: mattermost + template: + metadata: + labels: + app: mattermost + spec: + containers: + - name: mattermost + image: mattermost/mattermost-team-edition:10 + env: + - name: MM_DB_PASSWORD + valueFrom: + secretKeyRef: + name: mattermost-secret + key: db-password + - name: MM_SQLSETTINGS_DRIVERNAME + value: postgres + - name: MM_SQLSETTINGS_DATASOURCE + value: "postgres://mattermost_user:$(MM_DB_PASSWORD)@postgres:5432/mattermost_db?sslmode=disable&connect_timeout=10" + - name: MM_SERVICESETTINGS_SITEURL + value: https://chat.the-fulfillment.org + - name: MM_SERVICESETTINGS_LISTENADDRESS + value: ":8065" + ports: + - containerPort: 8065 + volumeMounts: + - name: mattermost-data + mountPath: /mattermost/data + - name: mattermost-plugins + mountPath: /mattermost/plugins + volumes: + - name: mattermost-data + persistentVolumeClaim: + claimName: mattermost-data-pvc + - name: mattermost-plugins + persistentVolumeClaim: + claimName: mattermost-plugins-pvc + +--- +apiVersion: v1 +kind: Service +metadata: + name: mattermost +spec: + selector: + app: mattermost + ports: + - port: 8065 + targetPort: 8065 + nodePort: 32374 + type: NodePort