Files
ansible-role-listmonk/README.md
T
claudeandClaude Opus 5.5 22a7ffe676 Initial listmonk role: eom.dev newsletter on Kubernetes
listmonk v6.2.0 with PostgreSQL 17, Dex login for listed admins,
delivery through postfix.eom.dev, Catppuccin public pages, Altcha
captcha and a double opt-in "eom.dev" list. Database-held settings
are converged by an idempotent SQL script, tested against v6.2.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 20:17:30 -05:00

68 lines
3.7 KiB
Markdown

# 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. A password login for the `admin` user is kept as a fallback.
- **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.
## Embedding a signup form
listmonk accepts a plain HTML POST, so any eom.dev page can include a form:
```html
<form method="post" action="https://listmonk.eom.dev/subscription/form">
<input type="hidden" name="nonce">
<input type="email" name="email" required placeholder="you@example.com">
<input type="hidden" name="l" value="LIST-UUID">
<button type="submit">Subscribe</button>
</form>
```
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