1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# 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
no_log: true
- 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
no_log: true
# vim: ft=yaml.ansible