Catppuccin Mocha campaign template (default) and archive variant, kept in sync in the database; system e-mail header/footer overridden via --static-dir from a ConfigMap. PNG logo for mail clients. Tested in Mailpit (desktop, phone width; 87% CSS support, rest inlined). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
5.2 KiB
Markdown
86 lines
5.2 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. 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
|
|
<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
|