# ansible-role-listmonk Deploys [listmonk](https://listmonk.app) v6.2.0, the eom.dev newsletter, to Kubernetes. It fits in with the rest of eom.dev: - **Login** goes through Dex (`listmonk` static client) with the same "eom.dev account" button as the other services. Only e-mail addresses listed in `listmonk_oidc_admins` can sign in, so being a member is not enough to get into the admin UI. The admin login page sends you straight to Dex. For the `admin` password login, kept as a fallback for when Dex is down, use `/admin/login?local=1`. - **Mail** goes out through `postfix.eom.dev:587` as the LDAP account `news`, the same way Gitea, Nextcloud and PeerTube send mail. - **Look**: the public pages (subscribe, archive, preferences, unsubscribe) use the Catppuccin Mocha/Latte palette from `dex.eom.dev`, along with the eom.dev logo and favicon. - **Cluster conventions**: `ca-issuer` TLS, `nfs-client` volumes, and anti-affinity that keeps the pods off the ingress-nginx node. ## What it creates | Object | Purpose | | --- | --- | | `Secret/listmonk` | DB password and bootstrap admin credentials | | `StatefulSet/listmonk-db` | PostgreSQL 17 (alpine, uid 70) on `nfs-client` | | `Deployment/listmonk` | Runs `--install --idempotent`, then `--upgrade`, then serves on :9000 | | `PVC/listmonk-uploads` | Media uploaded for campaigns | | `Ingress/listmonk` | `https://listmonk.eom.dev` | listmonk keeps most of its configuration in the `settings` table, not in a config file. The role writes those settings with an idempotent SQL script (`templates/configure.sql.j2`) that it runs in the database pod. The script covers SMTP, Dex, theme, captcha, privacy, the Dex admin users and the lists in `listmonk_lists`. On the first run it also removes listmonk's sample data. listmonk is restarted only when a row actually changes. Settings the role manages are reset on every run. Change them in `defaults/main.yml` or `listmonk_settings_extra`, not in the UI. Everything else, including templates, campaigns and subscribers, stays under the UI's control. ## Required variables (Vault) | Variable | Notes | | --- | --- | | `listmonk_db_password` | 16+ chars. Only used when the volume is first initialised | | `listmonk_admin_password` | 8+ chars, for the `admin` fallback login | | `listmonk_smtp_password` | Password of the `news` LDAP account | | `listmonk_oidc_client_secret` | Must match the `listmonk` client in Dex | The eom.dev playbook maps them from the Vault names `listmonk_postgresql_password`, `listmonk_admin_password`, `listmonk_smtp_password` and `listmonk_dex_client_secret`. ## One-time setup outside this role 1. In LDAP, create the posix account `news` with `mail: news@eom.dev` and add it to `cn=Postfix Users,ou=Postfix,ou=Services,dc=eom,dc=dev`. Its password is `listmonk_smtp_password`. 2. Add the `listmonk` client to Dex (`ericomeehan.dex/playbooks/deploy-eom.yml`) and run that play. ## E-mail design Newsletters and listmonk's system e-mails (opt-in confirmation, password reset and so on) use the Catppuccin Mocha palette and the eom.dev logo, matching www.eom.dev. - `templates/email/campaign.html.j2` produces the **eom.dev** campaign template, which is the default for new campaigns, and an **eom.dev archive** variant without unsubscribe links. Both are kept in sync in the database on every run. - `templates/email/base.html.j2` overrides only the shared system e-mail header and footer. It is mounted from a ConfigMap through `--static-dir`. - The logo is a PNG (`www.eom.dev/static/email-logo.png`), because most mail clients don't show SVG. - Set `listmonk_email_postal_address` to add a mailing address to every footer. US anti-spam law (CAN-SPAM) requires one in commercial e-mail. ## API users | User | Can do | Token | | --- | --- | --- | | `membership` | Read and manage subscribers on `eom.dev` (signup opt-in, profile toggle) | `listmonk_api_token` (Vault); same value as `LISTMONK_API_TOKEN` in the www `membership` Secret | | `newsletter` | Create, edit, test-send and schedule campaigns to `eom.dev`; no subscriber data or settings | Generated by the role, stored in `Secret/listmonk-newsletter-api` (`url`, `username`, `token`) | Neither user can read settings or other lists. To rotate the newsletter token, delete its Secret and run the play again. ## Embedding a signup form listmonk accepts a plain HTML POST, so any eom.dev page can include a form: ```html
``` Copy the list UUID from **Lists → eom.dev → Forms** in the admin UI. The form uses double opt-in, so subscribers have to confirm by e-mail. The page at `https://listmonk.eom.dev/subscription/form` adds an Altcha proof-of-work captcha. ## Testing ```sh ansible-playbook -i tests/inventory tests/render.yml # renders into .render/ ``` This version was tested against `listmonk/listmonk:v6.2.0` and `postgres:17.11-alpine`. The SQL changes 12 settings, 1 user, 1 list and the sample data on the first run, and nothing on the second. ## License MIT-0