lotus-land-story: run monica
Moved as-is from ramble-hard, whose running build had drifted to 4.1.2
from the 3.7 its history pins.
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/host_vars/lotus-land-story.sops.yml b/host_vars/lotus-land-story.sops.yml
index 843c681..f9814be 100644
--- a/host_vars/lotus-land-story.sops.yml
+++ b/host_vars/lotus-land-story.sops.yml
@@ -35,6 +35,10 @@ miniflux:
minio:
cluster_token: ENC[AES256_GCM,data:Huof3MtMLigijMwQbB6aNn+12lF5poE9QwqYk5LFzbhu9k49Vof/GnhTEe1v4Qm9S/3/6XwGfzmurK4J9q9oaAaBafVHbDrpPtKytxBcvxtAPqEmnjBpeKT55fmlWmkkc29EPamJHKqw5DXFcn/xwK3zfi2fJdyeORuhS5a6GQ67tyLyGn3rui5+pWMa+WyzavjmjQ0OdENkcbOfLZUfqsBYzBP17C69e7wliWXyAVJouC76lAM+ufZrj+Bsgzw=,iv:LtSvVZ/X48H5nykMdYrjrihnAJod4cZxVXHKvrQNhwk=,tag:Fw4Xxic7Qzy/RFnMKuYzHw==,type:str]
bucket_token: ENC[AES256_GCM,data:aY0bJf8i0jfSomJHsxQtdMmt6mtTLLU5B1HjZtuMcdquEDEe6S/xyQIQd201IR9xUKfvElUm4kYW8FPK7EvtUoR+fFCjB1hk9lK2k1r72LUifeYud7pXUbv04l7YCL45qcCkefk/MqsQlRPyQaP9Lj3NrGgZ95WW+l3mki6KoHa3nzNhYZNd2rFNWkLGgssO2f3JPAylHB7fBioQXZsilmozO8admWz915Czy88v/FCALsMNMDu5kyln7580dpk=,iv:rQh3hJyLnUg9PNo5+h3+oH8zrXSERdZLT0MwDwCCuM4=,tag:7PqTzKmIkMxECsK3sNGohw==,type:str]
+monica:
+ app_key: ENC[AES256_GCM,data:IZw4NhZfdECZJjpIOsTTzN7hplbJIw1QK7LuZiCSyueAuVT2mW7Lym+LZHtIbqnL2lwa,iv:LhaECQzdDdXiziYXhjNTtUNiIYC4eunUHwJbznb8jnY=,tag:HiZigEU8/acbq+cBP2u9nw==,type:str]
+ db_password: ENC[AES256_GCM,data:jGFSSj44b6aiN+k3om6JV6wYdhO0g7NeM7WTHRM6lI0=,iv:KT6AQRSfd3USSdSz4D7xn6VC935rPCfgVoIFsneGiXk=,tag:1+L6+YVbL98uemphc3E6gw==,type:str]
+ mail_password: ENC[AES256_GCM,data:xL4rNXq8QOiOT/16JAqqO9bNBlHRkytHsXM8c9I0dKqOzvT4geMm//2COU/vzOChYcYg+aOcgX+b9xj/bzlnvBT0cVpl,iv:G6KSTUaiL3AVY+EKrBfGsOmIlrK3QwpIpEPGpssSii8=,tag:pMU6XRfJ1UdU1djTZ/WcVw==,type:str]
paperless_ngx:
secret: ENC[AES256_GCM,data:p9q584YTKqLybDtxmwOSi8SwC+kG6IZ+i091ws7Z3p0iqKTU67qhFZwnvzIpUFYhtFH6kgLjldMj4YGtkvTmItvan8xnB8NTZWXLi2SJTpWg82n/o78=,iv:dI+MrhqCb4fj3+uFV5ZI6EQaIoVjqDGcYxK5wmxCSSQ=,tag:8mAd613aoTHALAY68Sjq9g==,type:str]
quire:
@@ -65,7 +69,7 @@ sops:
LEedZQPpxvyODlXv/10aLZhO2DZBOtjw9W2ctfg9WoyX7OxZvdbFnA==
-----END AGE ENCRYPTED FILE-----
recipient: age15ars55ulrp9hz6r0utvluayrglt40wv0t997njez0y9l3e43cersn6kgh7
- lastmodified: "2026-09-13T15:34:26Z"
- mac: ENC[AES256_GCM,data:7ssjK98SU6ekf94ydXezaN6uj0TteDtQaPAD7r2zXO8/MYoZyWblJ30tSDT+HulRjGpOravmCJqsKyow/TCCYXpaCjG1+HGbHOAMogv/p230y/EfjxEvd879k0Ru1VxQahQ6h6eThCspjqwQfpZzyjR8QSfch6Mg6GEHmdFLhbU=,iv:MXIgfkwPM8hm13pmO1/XYXsteBX28FxPoPrBBlUlbgY=,tag:5CpuX+AtM4RYealtG/YLSw==,type:str]
+ lastmodified: "2026-09-15T02:50:40Z"
+ mac: ENC[AES256_GCM,data:0NYFZqexlz6MELdolQNyPKlPHhhWchsmR1ytpSzbAM2SWINHOtu1wi0V1qj5gWeWJk8vC2Tyg57M+KXR2m4zxOD+OYGDEscasbHcuG8reemHhmicVjF6Z19qIGgeHaAXdhaUBcq3hOr+GVPywVPm/z4m31VSgEScvFDxfRtgS8I=,iv:JWuhs5qko4lrlsxFareYy0EpOr8RJtDiLNmvdQTP6jc=,tag:Pzv+ICJ9KwSqWBSlOp02Mg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3
diff --git a/host_vars/lotus-land-story.yml b/host_vars/lotus-land-story.yml
index 74d54e7..636d774 100644
--- a/host_vars/lotus-land-story.yml
+++ b/host_vars/lotus-land-story.yml
@@ -13,6 +13,8 @@ versions:
homebox: 0.25.0
ketchup: 20260525-e75f249
miniflux: 2.3.3
+ monica: 4.1.2
+ monica_mysql: "5.7"
paperless_ngx: 3.1.2
quire: latest # TODO Pin the version once we're not in active development anymore
soju: 0.10.1 # remember to update the docker-soju image to bump this
diff --git a/lotus-land-story/main.yml b/lotus-land-story/main.yml
index 77d8bd3..ff43bee 100644
--- a/lotus-land-story/main.yml
+++ b/lotus-land-story/main.yml
@@ -61,6 +61,7 @@
- import_playbook: grafana.yml # noqa: name[play]
- import_playbook: miniflux.yml # noqa: name[play]
+- import_playbook: monica.yml # noqa: name[play]
- import_playbook: gotosocial.yml # noqa: name[play]
- import_playbook: ketchup.yml # noqa: name[play]
- import_playbook: caddy.yml # noqa: name[play]
diff --git a/lotus-land-story/monica.yml b/lotus-land-story/monica.yml
new file mode 100644
index 0000000..cba47b3
--- /dev/null
+++ b/lotus-land-story/monica.yml
@@ -0,0 +1,158 @@
+# Carried over unchanged from ramble-hard's docker-compose setup: the stock
+# apache image with supervisord added so apache, cron, and the queue share one
+# container.
+
+- name: Set up Monica
+ hosts: lotus-land-story
+ tasks:
+
+ - name: Check for the copied Monica datadir
+ ansible.builtin.stat:
+ path: /mnt/lotus-land-story/monica/db/mysql
+ register: monica_datadir
+
+ # Otherwise MySQL initializes an empty datadir the data copy then lands on.
+ - name: Warn that Monica is skipped until its data is copied
+ ansible.builtin.debug:
+ msg: >-
+ Skipping Monica: copy ramble-hard's /usr/share/monica/{app,db} first;
+ see docs/lotus-land-story/2026-09-14-monica-migration.md.
+ when: not monica_datadir.stat.exists
+
+ - name: Skip Monica until its data is copied
+ ansible.builtin.meta: end_host
+ when: not monica_datadir.stat.exists
+
+ # app/ and db/ are left to the data copy, which preserves the ownership
+ # mysql and www-data expect.
+ - name: Create Monica directories
+ ansible.builtin.file:
+ path: /mnt/lotus-land-story/monica/{{ item }}
+ state: directory
+ mode: "0755"
+ loop:
+ - build
+ - php
+
+ - name: Write Monica Dockerfile
+ ansible.builtin.copy:
+ dest: /mnt/lotus-land-story/monica/build/Dockerfile
+ mode: "0644"
+ content: |
+ FROM docker.io/library/monica:{{ versions.monica }}-apache
+
+ # Use the default production configuration
+ RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
+
+ # supervisord dependencies
+ RUN set -ex; \
+ \
+ apt-get update; \
+ apt-get install -y --no-install-recommends \
+ supervisor \
+ ; \
+ rm -rf /var/lib/apt/lists/*
+
+ COPY supervisord.conf /etc/supervisord.conf
+
+ CMD ["supervisord", "-c", "/etc/supervisord.conf"]
+ register: monica_dockerfile
+
+ - name: Write Monica supervisord config
+ ansible.builtin.copy:
+ dest: /mnt/lotus-land-story/monica/build/supervisord.conf
+ mode: "0644"
+ content: |
+ [supervisord]
+ nodaemon=true
+ user=root
+
+ [program:cron]
+ command=cron.sh
+ autostart=true
+ autorestart=true
+
+ [program:queue]
+ process_name=%(program_name)s_%(process_num)02d
+ command=queue.sh
+ numprocs=1
+ stdout_logfile=/proc/1/fd/1
+ stdout_logfile_maxbytes=0
+ stderr_logfile=/proc/1/fd/2
+ stderr_logfile_maxbytes=0
+ autostart=true
+ autorestart=true
+ startretries=0
+
+ [program:httpd]
+ process_name=%(program_name)s_%(process_num)02d
+ command=entrypoint.sh apache2-foreground
+ stdout_logfile=/proc/1/fd/1
+ stdout_logfile_maxbytes=0
+ stderr_logfile=/proc/1/fd/2
+ stderr_logfile_maxbytes=0
+ autostart=true
+ autorestart=true
+ register: monica_supervisord
+
+ - name: Write Monica PHP upload limit
+ ansible.builtin.copy:
+ dest: /mnt/lotus-land-story/monica/php/uploads.ini
+ mode: "0644"
+ content: |
+ [www]
+ upload_max_filesize = 10M
+
+ - name: Build Monica
+ community.docker.docker_image:
+ name: monica-supervisord
+ tag: "{{ versions.monica }}"
+ source: build
+ build:
+ path: /mnt/lotus-land-story/monica/build
+ pull: true
+ force_source: "{{ monica_dockerfile is changed or monica_supervisord is changed }}"
+
+ # The MYSQL_* env only seeds an empty datadir; the copied one ignores it.
+ - name: Run Monica db
+ community.docker.docker_container:
+ name: monica-db
+ image: docker.io/library/mysql:{{ versions.monica_mysql }}
+ restart_policy: unless-stopped
+ env:
+ MYSQL_RANDOM_ROOT_PASSWORD: "true"
+ MYSQL_DATABASE: monica
+ MYSQL_USER: monica
+ MYSQL_PASSWORD: "{{ monica.db_password }}"
+ volumes:
+ - /mnt/lotus-land-story/monica/db:/var/lib/mysql
+ networks:
+ - name: lotus_land_story
+
+ - name: Run Monica
+ community.docker.docker_container:
+ name: monica
+ image: monica-supervisord:{{ versions.monica }}
+ restart_policy: unless-stopped
+ env:
+ APP_ENV: production
+ APP_KEY: "{{ monica.app_key }}"
+ DB_HOST: monica-db
+ DB_USERNAME: monica
+ DB_PASSWORD: "{{ monica.db_password }}"
+ MAIL_MAILER: smtp
+ MAIL_HOST: smtp.sendgrid.net
+ MAIL_PORT: "25"
+ MAIL_USERNAME: apikey
+ MAIL_PASSWORD: "{{ monica.mail_password }}"
+ MAIL_ENCRYPTION: tls
+ MAIL_FROM_ADDRESS: admin@kejadlen.dev
+ MAIL_FROM_NAME: Monica
+ APP_EMAIL_NEW_USERS_NOTIFICATION: alpha+monica@kejadlen.dev
+ volumes:
+ - /mnt/lotus-land-story/monica/app:/var/www/html/storage
+ - /mnt/lotus-land-story/monica/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
+ networks:
+ - name: lotus_land_story
+
+# vim: ft=yaml.ansible
diff --git a/lotus-land-story/templates/Caddyfile b/lotus-land-story/templates/Caddyfile
index 112bd90..b652c36 100644
--- a/lotus-land-story/templates/Caddyfile
+++ b/lotus-land-story/templates/Caddyfile
@@ -246,6 +246,12 @@ hey.kejadlen.dev {
}
}
+monica.kejadlen.dev {
+ log
+
+ reverse_proxy monica:80
+}
+
quire.kejadlen.dev {
reverse_proxy quire:3000
}