mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
docker: make compose work with cgroups (v2), conversion scripts/docs
This commit is contained in:
@@ -3,12 +3,12 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: docker/chatmail_relay.dockerfile
|
dockerfile: docker/chatmail_relay.dockerfile
|
||||||
tags:
|
|
||||||
- chatmail-relay:latest
|
|
||||||
image: chatmail-relay:latest
|
image: chatmail-relay:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
container_name: chatmail
|
container_name: chatmail
|
||||||
cgroup: host # required for systemd
|
# Required for systemd — use only one of the following:
|
||||||
|
cgroup: host # compose v2 only
|
||||||
|
# privileged: true # compose v1 (not tested)
|
||||||
tty: true # required for logs
|
tty: true # required for logs
|
||||||
tmpfs: # required for systemd
|
tmpfs: # required for systemd
|
||||||
- /tmp
|
- /tmp
|
||||||
|
|||||||
84
docker/cm_ini_to_env.py
Executable file
84
docker/cm_ini_to_env.py
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Convert a chatmail.ini to a Docker .env file.
|
||||||
|
|
||||||
|
Usage: python docker/cm_ini_to_env.py [chatmail.ini] [.env]
|
||||||
|
|
||||||
|
Reads the ini file, extracts all non-default key=value pairs,
|
||||||
|
and writes them as UPPER_CASE env vars suitable for docker-compose.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Keys that only make sense for bare-metal deploys or are handled
|
||||||
|
# separately by the Docker setup and should not appear in .env.
|
||||||
|
SKIP_KEYS = set()
|
||||||
|
|
||||||
|
# Keys that exist in .env but have a different name than the ini key.
|
||||||
|
# ini_key -> env_key
|
||||||
|
RENAMES = {}
|
||||||
|
|
||||||
|
|
||||||
|
def read_ini(path):
|
||||||
|
"""Return dict of key=value from [params] section."""
|
||||||
|
cp = configparser.ConfigParser()
|
||||||
|
cp.read(path)
|
||||||
|
if not cp.has_section("params"):
|
||||||
|
sys.exit(f"Error: {path} has no [params] section")
|
||||||
|
return dict(cp.items("params"))
|
||||||
|
|
||||||
|
|
||||||
|
def read_defaults():
|
||||||
|
"""Return dict of default values from the ini template."""
|
||||||
|
template = Path(__file__).resolve().parent.parent / "chatmaild/src/chatmaild/ini/chatmail.ini.f"
|
||||||
|
if not template.exists():
|
||||||
|
return {}
|
||||||
|
cp = configparser.ConfigParser()
|
||||||
|
cp.read(template)
|
||||||
|
if not cp.has_section("params"):
|
||||||
|
return {}
|
||||||
|
defaults = {}
|
||||||
|
for key, value in cp.items("params"):
|
||||||
|
# Template placeholders like {mail_domain} aren't real defaults.
|
||||||
|
if "{" not in value:
|
||||||
|
defaults[key] = value
|
||||||
|
return defaults
|
||||||
|
|
||||||
|
|
||||||
|
def ini_to_env(ini_path, only_non_default=True):
|
||||||
|
"""Yield (ENV_KEY, value) pairs from an ini file."""
|
||||||
|
params = read_ini(ini_path)
|
||||||
|
defaults = read_defaults() if only_non_default else {}
|
||||||
|
|
||||||
|
for key, value in sorted(params.items()):
|
||||||
|
if key in SKIP_KEYS:
|
||||||
|
continue
|
||||||
|
if only_non_default and key in defaults and value.strip() == defaults[key].strip():
|
||||||
|
continue
|
||||||
|
env_key = RENAMES.get(key, key.upper())
|
||||||
|
yield env_key, value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ini_path = sys.argv[1] if len(sys.argv) > 1 else "chatmail.ini"
|
||||||
|
env_path = sys.argv[2] if len(sys.argv) > 2 else None
|
||||||
|
|
||||||
|
if not Path(ini_path).exists():
|
||||||
|
sys.exit(f"Error: {ini_path} not found")
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for env_key, value in ini_to_env(ini_path):
|
||||||
|
lines.append(f'{env_key}="{value}"')
|
||||||
|
|
||||||
|
output = "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
if env_path:
|
||||||
|
Path(env_path).write_text(output)
|
||||||
|
print(f"Wrote {len(lines)} variables to {env_path}")
|
||||||
|
else:
|
||||||
|
print(output, end="")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -67,7 +67,10 @@ git config --global --add safe.directory /opt/chatmail
|
|||||||
if [ "$RECREATE_VENV" = true ]; then
|
if [ "$RECREATE_VENV" = true ]; then
|
||||||
rm -rf venv
|
rm -rf venv
|
||||||
fi
|
fi
|
||||||
./scripts/initenv.sh
|
# Skip venv creation if it already exists
|
||||||
|
if [ ! -x venv/bin/python ] || [ ! -x venv/bin/cmdeploy ]; then
|
||||||
|
./scripts/initenv.sh
|
||||||
|
fi
|
||||||
|
|
||||||
./scripts/cmdeploy init --config "${INI_FILE}" $INI_CMD_ARGS $MAIL_DOMAIN || true
|
./scripts/cmdeploy init --config "${INI_FILE}" $INI_CMD_ARGS $MAIL_DOMAIN || true
|
||||||
bash /update_ini.sh
|
bash /update_ini.sh
|
||||||
|
|||||||
@@ -5,7 +5,13 @@
|
|||||||
- The Docker image is only suitable for amd64. If you need to run it on a different architecture, try modifying the Dockerfile (specifically the part responsible for installing dovecot).
|
- The Docker image is only suitable for amd64. If you need to run it on a different architecture, try modifying the Dockerfile (specifically the part responsible for installing dovecot).
|
||||||
|
|
||||||
# Docker installation
|
# Docker installation
|
||||||
This section provides instructions for installing Chatmail using docker-compose.
|
This section provides instructions for installing Chatmail using Docker Compose.
|
||||||
|
|
||||||
|
**Note:** Docker Compose v2 is required (`docker compose`, not `docker-compose`) for its support of the `cgroup: host` option in `docker-compose.yaml` is only supported by Compose v2.
|
||||||
|
[see documentation](https://docs.docker.com/engine/install/debian/#install-using-the-repository)
|
||||||
|
```shell
|
||||||
|
apt install docker-ce docker-compose-plugin docker.io- docker-compose-
|
||||||
|
```
|
||||||
|
|
||||||
## Preliminary setup
|
## Preliminary setup
|
||||||
We use `chat.example.org` as the Chatmail domain in the following steps.
|
We use `chat.example.org` as the Chatmail domain in the following steps.
|
||||||
@@ -75,6 +81,9 @@ docker compose up -d # start service
|
|||||||
docker compose logs -f chatmail # view container logs, press CTRL+C to exit
|
docker compose logs -f chatmail # view container logs, press CTRL+C to exit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### venv creation
|
||||||
|
The first container start takes longer because it creates the cmdeploy Python virtualenv at `/opt/chatmail/venv` (persisted on the host via volume mount). Subsequent starts reuse the existing venv. Set `RECREATE_VENV=true` in `.env` to force a rebuild if needed.
|
||||||
|
|
||||||
6. After installation is complete, you can open `https://<your_domain_name>` in your browser.
|
6. After installation is complete, you can open `https://<your_domain_name>` in your browser.
|
||||||
|
|
||||||
## Using custom files
|
## Using custom files
|
||||||
@@ -114,6 +123,50 @@ docker compose down
|
|||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Migrating from a bare-metal install
|
||||||
|
|
||||||
|
If you have an existing bare-metal Chatmail installation and want to switch to Docker:
|
||||||
|
|
||||||
|
1. Stop all existing services:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
systemctl stop postfix dovecot doveauth nginx opendkim unbound acmetool-redirector \
|
||||||
|
filtermail filtermail-incoming chatmail-turn iroh-relay chatmail-metadata \
|
||||||
|
lastlogin mtail
|
||||||
|
systemctl disable postfix dovecot doveauth nginx opendkim unbound acmetool-redirector \
|
||||||
|
filtermail filtermail-incoming chatmail-turn iroh-relay chatmail-metadata \
|
||||||
|
lastlogin mtail
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Convert your existing `chatmail.ini` to the Docker `.env` format:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 docker/cm_ini_to_env.py /usr/local/lib/chatmaild/chatmail.ini .env
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Copy persistent data into the `./data/` subdirectories:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mkdir -p data/chatmail-dkimkeys data/chatmail-acme data/chatmail
|
||||||
|
|
||||||
|
# DKIM keys
|
||||||
|
cp -a /etc/dkimkeys/* data/chatmail-dkimkeys/
|
||||||
|
|
||||||
|
# ACME certificates and account
|
||||||
|
rsync -a /var/lib/acme/ data/chatmail-acme/
|
||||||
|
|
||||||
|
# Mail data
|
||||||
|
rsync -a /home/ data/chatmail/
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can mount `/home/vmail` directly by changing the volume in `docker-compose.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- /home/vmail:/home/vmail
|
||||||
|
```
|
||||||
|
|
||||||
|
The three `./data/` subdirectories cover all persistent state. Everything else is regenerated by the `configure` and `activate` stages on container start.
|
||||||
|
|
||||||
## Forcing a full reinstall
|
## Forcing a full reinstall
|
||||||
|
|
||||||
The Docker image bakes the install stage (binary downloads, package setup, chatmaild venv) into the image at build time. On container start, only the `configure` and `activate` stages run by default.
|
The Docker image bakes the install stage (binary downloads, package setup, chatmaild venv) into the image at build time. On container start, only the `configure` and `activate` stages run by default.
|
||||||
|
|||||||
Reference in New Issue
Block a user