Skip to content

Commit 5753a22

Browse files
Keonik1missytake
authored andcommitted
Add installation via docker compose (MVP 1)
- Adds configuration parameters (`change_kernel_settings`, `fs_inotify_max_user_instances_and_watchers`)
1 parent fc7240a commit 5753a22

File tree

16 files changed

+775
-13
lines changed

16 files changed

+775
-13
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,9 @@ cython_debug/
164164
#.idea/
165165

166166
chatmail.zone
167+
168+
# docker
169+
/data/
170+
/custom/
171+
docker-compose.yaml
172+
.env

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@
8080
Provide an "fsreport" CLI for more fine grained analysis of message files.
8181
([#637](https://github.com/chatmail/relay/pull/637))
8282

83+
- Add installation via docker compose (MVP 1). The instructions, known issues and limitations are located in `/docs`
84+
([#614](https://github.com/chatmail/relay/pull/614))
85+
86+
- Add configuration parameters
87+
([#614](https://github.com/chatmail/relay/pull/614)):
88+
- `change_kernel_settings` - Whether to change kernel parameters during installation (default: `True`)
89+
- `fs_inotify_max_user_instances_and_watchers` - Value for kernel parameters `fs.inotify.max_user_instances` and `fs.inotify.max_user_watches` (default: `65535`)
8390

8491
## 1.7.0 2025-09-11
8592

chatmaild/src/chatmaild/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ def __init__(self, inipath, params):
4545
self.mtail_address = params.get("mtail_address")
4646
self.disable_ipv6 = params.get("disable_ipv6", "false").lower() == "true"
4747
self.acme_email = params.get("acme_email", "")
48+
self.change_kernel_settings = (
49+
params.get("change_kernel_settings", "true").lower() == "true"
50+
)
51+
self.fs_inotify_max_user_instances_and_watchers = int(
52+
params["fs_inotify_max_user_instances_and_watchers"]
53+
)
4854
self.imap_rawlog = params.get("imap_rawlog", "false").lower() == "true"
4955
if "iroh_relay" not in params:
5056
self.iroh_relay = "https://" + params["mail_domain"]

chatmaild/src/chatmaild/ini/chatmail.ini.f

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@
6666
# Your email adress, which will be used in acmetool to manage Let's Encrypt SSL certificates
6767
acme_email =
6868

69+
#
70+
# Kernel settings
71+
#
72+
73+
# if you set "True", the kernel settings will be configured according to the values below
74+
change_kernel_settings = True
75+
76+
# change fs.inotify.max_user_instances and fs.inotify.max_user_watches kernel settings
77+
fs_inotify_max_user_instances_and_watchers = 65535
78+
6979
# Defaults to https://iroh.{{mail_domain}} and running `iroh-relay` on the chatmail
7080
# service.
7181
# If you set it to anything else, the service will be disabled

cmdeploy/src/cmdeploy/cmdeploy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ def run_cmd(args, out):
118118
kwargs=dict(command="cat /var/lib/echobot/invite-link.txt"),
119119
)
120120
)
121+
server_deployed_message = f"Chatmail server started: https://{args.config.mail_domain}/"
122+
delimiter_line = "=" * len(server_deployed_message)
123+
out.green(f"{delimiter_line}\n{server_deployed_message}\n{delimiter_line}")
121124
out.green("Deploy completed, call `cmdeploy dns` next.")
122125
elif not remote_data["acme_account_url"]:
123126
out.red("Deploy completed but letsencrypt not configured")

cmdeploy/src/cmdeploy/deployers.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -523,20 +523,21 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool:
523523
present=False,
524524
)
525525

526-
# as per https://doc.dovecot.org/configuration_manual/os/
526+
# as per https://doc.dovecot.org/2.3/configuration_manual/os/
527527
# it is recommended to set the following inotify limits
528-
for name in ("max_user_instances", "max_user_watches"):
529-
key = f"fs.inotify.{name}"
530-
if host.get_fact(Sysctl)[key] > 65535:
531-
# Skip updating limits if already sufficient
532-
# (enables running in incus containers where sysctl readonly)
533-
continue
534-
server.sysctl(
535-
name=f"Change {key}",
536-
key=key,
537-
value=65535,
538-
persist=True,
539-
)
528+
if config.change_kernel_settings:
529+
for name in ("max_user_instances", "max_user_watches"):
530+
key = f"fs.inotify.{name}"
531+
if host.get_fact(Sysctl)[key] > config.fs_inotify_max_user_instances_and_watchers:
532+
# Skip updating limits if already sufficient
533+
# (enables running in incus containers where sysctl readonly)
534+
continue
535+
server.sysctl(
536+
name=f"Change {key}",
537+
key=key,
538+
value=config.fs_inotify_max_user_instances_and_watchers,
539+
persist=True,
540+
)
540541

541542
timezone_env = files.line(
542543
name="Set TZ environment variable",

doc/source/getting_started.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ steps. Please substitute it with your own domain.
7878
configure at your DNS provider (it can take some time until they are
7979
public).
8080

81+
Docker installation
82+
-------------------
83+
84+
We have experimental support for `docker compose <https://github.com/chatmail/relay/blob/docker-rebase/docs/DOCKER_INSTALLATION_EN.md>`_,
85+
but it is not covered by automated tests yet,
86+
so don't expect everything to work.
87+
8188
Other helpful commands
8289
----------------------
8390

docker/chatmail_server.dockerfile

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
FROM jrei/systemd-debian:12 AS base
2+
3+
ENV LANG=en_US.UTF-8
4+
5+
RUN echo 'APT::Install-Recommends "0";' > /etc/apt/apt.conf.d/01norecommend && \
6+
echo 'APT::Install-Suggests "0";' >> /etc/apt/apt.conf.d/01norecommend && \
7+
apt-get update && \
8+
apt-get install -y \
9+
ca-certificates && \
10+
DEBIAN_FRONTEND=noninteractive \
11+
TZ=Europe/London \
12+
apt-get install -y tzdata && \
13+
apt-get install -y locales && \
14+
sed -i -e "s/# $LANG.*/$LANG UTF-8/" /etc/locale.gen && \
15+
dpkg-reconfigure --frontend=noninteractive locales && \
16+
update-locale LANG=$LANG \
17+
&& rm -rf /var/lib/apt/lists/*
18+
19+
RUN apt-get update && \
20+
apt-get install -y \
21+
openssh-client \
22+
openssh-server \
23+
git \
24+
python3 \
25+
python3-venv \
26+
python3-virtualenv \
27+
gcc \
28+
python3-dev \
29+
opendkim \
30+
opendkim-tools \
31+
curl \
32+
rsync \
33+
unbound \
34+
unbound-anchor \
35+
dnsutils \
36+
postfix \
37+
acl \
38+
nginx \
39+
libnginx-mod-stream \
40+
fcgiwrap \
41+
cron \
42+
&& for pkg in core imapd lmtpd; do \
43+
case "$pkg" in \
44+
core) sha256="43f593332e22ac7701c62d58b575d2ca409e0f64857a2803be886c22860f5587" ;; \
45+
imapd) sha256="8d8dc6fc00bbb6cdb25d345844f41ce2f1c53f764b79a838eb2a03103eebfa86" ;; \
46+
lmtpd) sha256="2f69ba5e35363de50962d42cccbfe4ed8495265044e244007d7ccddad77513ab" ;; \
47+
esac; \
48+
url="https://download.delta.chat/dovecot/dovecot-${pkg}_2.3.21%2Bdfsg1-3_amd64.deb"; \
49+
file="/tmp/$(basename "$url")"; \
50+
curl -fsSL "$url" -o "$file"; \
51+
echo "$sha256 $file" | sha256sum -c -; \
52+
apt-get install -y "$file"; \
53+
rm -f "$file"; \
54+
done \
55+
&& rm -rf /var/lib/apt/lists/*
56+
57+
RUN systemctl enable \
58+
ssh \
59+
fcgiwrap
60+
61+
RUN sed -i 's/^#PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config && \
62+
sed -i 's/^#PermitRootLogin .*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config && \
63+
ssh-keygen -P "" -t rsa -b 2048 -f /root/.ssh/id_rsa && \
64+
mkdir -p /root/.ssh && \
65+
cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && \
66+
SSH_USER_CONFIG="/root/.ssh/config" && \
67+
echo "Host localhost" > "$SSH_USER_CONFIG" && \
68+
echo " HostName localhost" >> "$SSH_USER_CONFIG" && \
69+
echo " User root" >> "$SSH_USER_CONFIG" && \
70+
echo " StrictHostKeyChecking no" >> "$SSH_USER_CONFIG" && \
71+
echo " UserKnownHostsFile /dev/null" >> "$SSH_USER_CONFIG"
72+
## TODO: deny access for all insteed root form 127.0.0.1 https://unix.stackexchange.com/a/406264
73+
74+
WORKDIR /opt/chatmail
75+
76+
ARG SETUP_CHATMAIL_SERVICE_PATH=/lib/systemd/system/setup_chatmail.service
77+
COPY ./files/setup_chatmail.service "$SETUP_CHATMAIL_SERVICE_PATH"
78+
RUN ln -sf "$SETUP_CHATMAIL_SERVICE_PATH" "/etc/systemd/system/multi-user.target.wants/setup_chatmail.service"
79+
80+
COPY --chmod=555 ./files/setup_chatmail_docker.sh /setup_chatmail_docker.sh
81+
COPY --chmod=555 ./files/update_ini.sh /update_ini.sh
82+
COPY --chmod=555 ./files/entrypoint.sh /entrypoint.sh
83+
84+
## TODO: add git clone.
85+
## Problem: how correct save only required files inside container....
86+
# RUN git clone https://github.com/chatmail/relay.git -b master . \
87+
# && ./scripts/initenv.sh
88+
89+
# EXPOSE 443 25 587 143 993
90+
91+
VOLUME ["/sys/fs/cgroup", "/home"]
92+
93+
STOPSIGNAL SIGRTMIN+3
94+
95+
ENTRYPOINT ["/entrypoint.sh"]
96+
97+
CMD [ "--default-standard-output=journal+console", \
98+
"--default-standard-error=journal+console" ]
99+
100+
## TODO: Add installation and configuration of chatmaild inside the Dockerfile.
101+
## This is required to ensure repeatable deployment.
102+
## In the current MVP, the chatmaild server is updated on every container restart.

docker/docker-compose-default.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
services:
2+
chatmail:
3+
build:
4+
context: ./docker
5+
dockerfile: chatmail_server.dockerfile
6+
tags:
7+
- chatmail-relay:latest
8+
image: chatmail-relay:latest
9+
restart: unless-stopped
10+
container_name: chatmail
11+
cgroup: host # required for systemd
12+
tty: true # required for logs
13+
tmpfs: # required for systemd
14+
- /tmp
15+
- /run
16+
- /run/lock
17+
logging:
18+
driver: json-file
19+
options:
20+
max-size: "10m"
21+
max-file: "3"
22+
environment:
23+
MAIL_DOMAIN: <your_domain>
24+
CHANGE_KERNEL_SETTINGS: "False"
25+
ACME_EMAIL: <your_email>
26+
27+
MAX_MESSAGE_SIZE: "50M"
28+
# DEBUG_COMMANDS_ENABLED: "true"
29+
# FORCE_REINIT_INI_FILE: "true"
30+
# USE_FOREIGN_CERT_MANAGER: "True"
31+
# ENABLE_CERTS_MONITORING: "true"
32+
# CERTS_MONITORING_TIMEOUT: 10
33+
# IS_DEVELOPMENT_INSTANCE: "True"
34+
ports:
35+
- "25:25"
36+
- "587:587"
37+
- "143:143"
38+
- "993:993"
39+
- "443:443"
40+
volumes:
41+
## system
42+
- /sys/fs/cgroup:/sys/fs/cgroup:rw # required for systemd
43+
- ./:/opt/chatmail
44+
- ./data/acme:/var/lib/acme
45+
46+
## data
47+
- ./data/chatmail:/home
48+
- ./data/chatmail-dkimkeys:/etc/dkimkeys
49+
- ./data/chatmail-echobot:/run/echobot
50+
- ./data/chatmail-acme:/var/lib/acme
51+
52+
## custom resources
53+
# - ./custom/www/src/index.md:/opt/chatmail/www/src/index.md
54+
55+
## debug
56+
# - ./docker/files/setup_chatmail_docker.sh:/setup_chatmail_docker.sh
57+
# - ./docker/files/entrypoint.sh:/entrypoint.sh
58+
# - ./docker/files/update_ini.sh:/update_ini.sh

docker/example.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MAIL_DOMAIN="chat.example.com"
2+
3+
PATH_TO_SSL_CONTAINER="/var/lib/acme/live/${MAIL_DOMAIN}"

0 commit comments

Comments
 (0)