mirror of
https://github.com/chatmail/relay.git
synced 2026-05-20 21:08:03 +00:00
docker: move cmdeploy into docker image
This commit is contained in:
@@ -23,7 +23,7 @@ services:
|
|||||||
CHANGE_KERNEL_SETTINGS: "False"
|
CHANGE_KERNEL_SETTINGS: "False"
|
||||||
MAIL_DOMAIN: $MAIL_DOMAIN
|
MAIL_DOMAIN: $MAIL_DOMAIN
|
||||||
ACME_EMAIL: $ACME_EMAIL
|
ACME_EMAIL: $ACME_EMAIL
|
||||||
RECREATE_VENV: $RECREATE_VENV
|
WWW_FOLDER: /opt/chatmail-www
|
||||||
MAX_MESSAGE_SIZE: $MAX_MESSAGE_SIZE
|
MAX_MESSAGE_SIZE: $MAX_MESSAGE_SIZE
|
||||||
DEBUG_COMMANDS_ENABLED: $DEBUG_COMMANDS_ENABLED
|
DEBUG_COMMANDS_ENABLED: $DEBUG_COMMANDS_ENABLED
|
||||||
FORCE_REINIT_INI_FILE: $FORCE_REINIT_INI_FILE
|
FORCE_REINIT_INI_FILE: $FORCE_REINIT_INI_FILE
|
||||||
@@ -36,15 +36,15 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
## system
|
## system
|
||||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw # required for systemd
|
- /sys/fs/cgroup:/sys/fs/cgroup:rw # required for systemd
|
||||||
- ./:/opt/chatmail
|
|
||||||
|
|
||||||
## data
|
## data
|
||||||
- ./data/chatmail:/home
|
- ./data/chatmail:/home
|
||||||
- ./data/chatmail-dkimkeys:/etc/dkimkeys
|
- ./data/chatmail-dkimkeys:/etc/dkimkeys
|
||||||
- ./data/chatmail-acme:/var/lib/acme
|
- ./data/chatmail-acme:/var/lib/acme
|
||||||
|
|
||||||
## custom resources
|
## optional overrides
|
||||||
# - ./custom/www/src/index.md:/opt/chatmail/www/src/index.md
|
# - ./chatmail.ini:/etc/chatmail/chatmail.ini # use your own config
|
||||||
|
# - ./custom/www:/opt/chatmail-www # custom website
|
||||||
|
|
||||||
## debug
|
## debug
|
||||||
# - ./docker/files/setup_chatmail_docker.sh:/setup_chatmail_docker.sh
|
# - ./docker/files/setup_chatmail_docker.sh:/setup_chatmail_docker.sh
|
||||||
|
|||||||
@@ -54,32 +54,30 @@ RUN apt-get update && \
|
|||||||
|
|
||||||
WORKDIR /opt/chatmail
|
WORKDIR /opt/chatmail
|
||||||
|
|
||||||
# --- Build-time install stage ---
|
# --- Build-time: install cmdeploy venv and run install stage ---
|
||||||
# Bake the "install" deployer stage into the image; we can't use
|
# Editable install so importlib.resources reads directly from the source tree.
|
||||||
# scripts/initenv.sh because /opt/chatmail is empty at build time as
|
|
||||||
# source arrives at runtime via volume mount., so we use a throwaway venv.
|
|
||||||
# On container start only "configure,activate" stages run.
|
# On container start only "configure,activate" stages run.
|
||||||
COPY . /tmp/chatmail-src/
|
COPY . /opt/chatmail/
|
||||||
WORKDIR /tmp/chatmail-src
|
WORKDIR /opt/chatmail
|
||||||
|
|
||||||
# Dummy config — deploy_chatmail() needs a parseable ini to instantiate deployers
|
|
||||||
RUN printf '[params]\nmail_domain = build.local\n' > /tmp/chatmail.ini
|
RUN printf '[params]\nmail_domain = build.local\n' > /tmp/chatmail.ini
|
||||||
|
|
||||||
# Do what initenv.sh would do without the docs
|
RUN python3 -m venv /opt/cmdeploy && \
|
||||||
RUN python3 -m venv /tmp/build-venv && \
|
/opt/cmdeploy/bin/pip install --no-cache-dir \
|
||||||
/tmp/build-venv/bin/pip install --no-cache-dir \
|
-e chatmaild/ -e cmdeploy/
|
||||||
-e chatmaild -e cmdeploy
|
|
||||||
|
|
||||||
RUN CMDEPLOY_STAGES=install \
|
RUN CMDEPLOY_STAGES=install \
|
||||||
CHATMAIL_INI=/tmp/chatmail.ini \
|
CHATMAIL_INI=/tmp/chatmail.ini \
|
||||||
CHATMAIL_DOCKER=True \
|
CHATMAIL_DOCKER=True \
|
||||||
/tmp/build-venv/bin/pyinfra @local \
|
/opt/cmdeploy/bin/pyinfra @local \
|
||||||
/tmp/chatmail-src/cmdeploy/src/cmdeploy/run.py -y
|
/opt/chatmail/cmdeploy/src/cmdeploy/run.py -y
|
||||||
|
|
||||||
RUN rm -rf /tmp/chatmail-src /tmp/build-venv /tmp/chatmail.ini
|
RUN cp -a www/ /opt/chatmail-www/
|
||||||
|
|
||||||
WORKDIR /opt/chatmail
|
RUN rm -f /tmp/chatmail.ini
|
||||||
# --- End build-time install stage ---
|
# --- End build-time install ---
|
||||||
|
|
||||||
|
ENV CHATMAIL_INI=/etc/chatmail/chatmail.ini
|
||||||
|
|
||||||
ARG SETUP_CHATMAIL_SERVICE_PATH=/lib/systemd/system/setup_chatmail.service
|
ARG SETUP_CHATMAIL_SERVICE_PATH=/lib/systemd/system/setup_chatmail.service
|
||||||
COPY ./docker/files/setup_chatmail.service "$SETUP_CHATMAIL_SERVICE_PATH"
|
COPY ./docker/files/setup_chatmail.service "$SETUP_CHATMAIL_SERVICE_PATH"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
MAIL_DOMAIN="chat.example.com"
|
MAIL_DOMAIN="chat.example.com"
|
||||||
# ACME_EMAIL=""
|
# ACME_EMAIL=""
|
||||||
# RECREATE_VENV="false"
|
|
||||||
# MAX_MESSAGE_SIZE="50M"
|
# MAX_MESSAGE_SIZE="50M"
|
||||||
# DEBUG_COMMANDS_ENABLED="true"
|
# DEBUG_COMMANDS_ENABLED="true"
|
||||||
# FORCE_REINIT_INI_FILE="true"
|
# FORCE_REINIT_INI_FILE="true"
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
export INI_FILE="${INI_FILE:-chatmail.ini}"
|
export CHATMAIL_INI="${CHATMAIL_INI:-/etc/chatmail/chatmail.ini}"
|
||||||
export ENABLE_CERTS_MONITORING="${ENABLE_CERTS_MONITORING:-true}"
|
export ENABLE_CERTS_MONITORING="${ENABLE_CERTS_MONITORING:-true}"
|
||||||
export CERTS_MONITORING_TIMEOUT="${CERTS_MONITORING_TIMEOUT:-60}"
|
export CERTS_MONITORING_TIMEOUT="${CERTS_MONITORING_TIMEOUT:-60}"
|
||||||
export PATH_TO_SSL="${PATH_TO_SSL:-/var/lib/acme/live/${MAIL_DOMAIN}}"
|
export PATH_TO_SSL="${PATH_TO_SSL:-/var/lib/acme/live/${MAIL_DOMAIN}}"
|
||||||
export CHANGE_KERNEL_SETTINGS=${CHANGE_KERNEL_SETTINGS:-"False"}
|
export CHANGE_KERNEL_SETTINGS=${CHANGE_KERNEL_SETTINGS:-"False"}
|
||||||
export RECREATE_VENV=${RECREATE_VENV:-"false"}
|
|
||||||
|
CMDEPLOY=/opt/cmdeploy/bin/cmdeploy
|
||||||
|
|
||||||
if [ -z "$MAIL_DOMAIN" ]; then
|
if [ -z "$MAIL_DOMAIN" ]; then
|
||||||
echo "ERROR: Environment variable 'MAIL_DOMAIN' must be set!" >&2
|
echo "ERROR: Environment variable 'MAIL_DOMAIN' must be set!" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug_commands() {
|
|
||||||
echo "Executing debug commands"
|
|
||||||
# git config --global --add safe.directory /opt/chatmail
|
|
||||||
# ./scripts/initenv.sh
|
|
||||||
}
|
|
||||||
|
|
||||||
calculate_hash() {
|
calculate_hash() {
|
||||||
find "$PATH_TO_SSL" -type f -exec sha1sum {} \; | sort | sha1sum | awk '{print $1}'
|
find "$PATH_TO_SSL" -type f -exec sha1sum {} \; | sort | sha1sum | awk '{print $1}'
|
||||||
}
|
}
|
||||||
@@ -48,11 +43,7 @@ monitor_certificates() {
|
|||||||
|
|
||||||
### MAIN
|
### MAIN
|
||||||
|
|
||||||
if [ "$DEBUG_COMMANDS_ENABLED" = true ]; then
|
if [ "$FORCE_REINIT_INI_FILE" = true ]; then
|
||||||
debug_commands
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$FORCE_REINIT_INI_FILE" = true ]; then
|
|
||||||
INI_CMD_ARGS=--force
|
INI_CMD_ARGS=--force
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -62,21 +53,13 @@ fi
|
|||||||
chown opendkim:opendkim /etc/dkimkeys/opendkim.private
|
chown opendkim:opendkim /etc/dkimkeys/opendkim.private
|
||||||
chown opendkim:opendkim /etc/dkimkeys/opendkim.txt
|
chown opendkim:opendkim /etc/dkimkeys/opendkim.txt
|
||||||
|
|
||||||
# TODO: Move to debug_commands after git clone is moved to dockerfile.
|
# Create chatmail.ini from env vars (skips if file already exists, e.g. volume-mounted)
|
||||||
git config --global --add safe.directory /opt/chatmail
|
mkdir -p "$(dirname "$CHATMAIL_INI")"
|
||||||
if [ "$RECREATE_VENV" = true ]; then
|
$CMDEPLOY init --config "$CHATMAIL_INI" $INI_CMD_ARGS $MAIL_DOMAIN || true
|
||||||
rm -rf venv
|
INI_FILE="$CHATMAIL_INI" bash /update_ini.sh
|
||||||
fi
|
|
||||||
# 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
|
|
||||||
bash /update_ini.sh
|
|
||||||
|
|
||||||
export CMDEPLOY_STAGES="${CMDEPLOY_STAGES:-configure,activate}"
|
export CMDEPLOY_STAGES="${CMDEPLOY_STAGES:-configure,activate}"
|
||||||
./scripts/cmdeploy run --ssh-host @docker
|
$CMDEPLOY run --ssh-host @docker
|
||||||
|
|
||||||
echo "ForwardToConsole=yes" >> /etc/systemd/journald.conf
|
echo "ForwardToConsole=yes" >> /etc/systemd/journald.conf
|
||||||
systemctl restart systemd-journald
|
systemctl restart systemd-journald
|
||||||
|
|||||||
@@ -29,16 +29,7 @@ Please substitute it with your own domain.
|
|||||||
mta-sts.chat.example.com. 3600 IN CNAME chat.example.com.
|
mta-sts.chat.example.com. 3600 IN CNAME chat.example.com.
|
||||||
```
|
```
|
||||||
|
|
||||||
2. clone the repository on your server.
|
2. Configure kernel parameters because they cannot be changed inside the container, specifically `fs.inotify.max_user_instances` and `fs.inotify.max_user_watches`. Run the following:
|
||||||
|
|
||||||
```shell
|
|
||||||
git clone https://github.com/chatmail/relay
|
|
||||||
cd relay
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. Configure kernel parameters because they cannot be changed inside the container, specifically `fs.inotify.max_user_instances` and `fs.inotify.max_user_watches`. Run the following:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
echo "fs.inotify.max_user_instances=65536" | sudo tee -a /etc/sysctl.d/99-inotify.conf
|
echo "fs.inotify.max_user_instances=65536" | sudo tee -a /etc/sysctl.d/99-inotify.conf
|
||||||
@@ -46,83 +37,117 @@ echo "fs.inotify.max_user_watches=65536" | sudo tee -a /etc/sysctl.d/99-inotify.
|
|||||||
sudo sysctl --system
|
sudo sysctl --system
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Copy `./docker/example.env` and rename it to `.env`. This file stores variables used in `docker-compose.yaml`.
|
## Building the image
|
||||||
|
|
||||||
|
Clone the repository and build the Docker image:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/chatmail/relay
|
||||||
|
cd relay
|
||||||
|
docker compose build chatmail
|
||||||
|
```
|
||||||
|
|
||||||
|
The build bakes all binaries, Python packages, and the install stage into the image. After building, only `docker-compose.yaml` and `.env` are needed to run the container.
|
||||||
|
|
||||||
|
## Running with Docker Compose
|
||||||
|
|
||||||
|
1. Copy `docker-compose.yaml` and `docker/example.env` into a working directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cp docker-compose.yaml /path/to/your/workdir/
|
||||||
|
cp docker/example.env /path/to/your/workdir/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are running from the cloned repo directory, just copy the env file:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cp ./docker/example.env .env
|
cp ./docker/example.env .env
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Configure environment variables in the `.env` file. These variables are used in the `docker-compose.yaml` file to pass repeated values.
|
2. Configure environment variables in the `.env` file.
|
||||||
Below is the list of variables used during deployment:
|
Below is the list of variables used during deployment:
|
||||||
|
|
||||||
- `MAIL_DOMAIN` – The domain name of the future server. (required)
|
- `MAIL_DOMAIN` – The domain name of the future server. (required)
|
||||||
- `DEBUG_COMMANDS_ENABLED` – Run debug commands before installation. (default: `false`)
|
- `DEBUG_COMMANDS_ENABLED` – Run debug commands before installation. (default: `false`)
|
||||||
- `FORCE_REINIT_INI_FILE` – Recreate the ini configuration file on startup. (default: `false`)
|
- `FORCE_REINIT_INI_FILE` – Recreate the ini configuration file on startup. (default: `false`)
|
||||||
- `USE_FOREIGN_CERT_MANAGER` – Use a third-party certificate manager. (default: `false`)
|
- `USE_FOREIGN_CERT_MANAGER` – Use a third-party certificate manager. (default: `false`)
|
||||||
- `RECREATE_VENV` - Recreate the virtual environment (venv). If set to `true`, the environment will be recreated when the container starts, which will increase the startup time of the service but can help avoid certain errors. (default: `false`)
|
|
||||||
- `INI_FILE` – Path to the ini configuration file. (default: `./chatmail.ini`)
|
|
||||||
- `PATH_TO_SSL` – Path to where the certificates are stored. (default: `/var/lib/acme/live/${MAIL_DOMAIN}`)
|
- `PATH_TO_SSL` – Path to where the certificates are stored. (default: `/var/lib/acme/live/${MAIL_DOMAIN}`)
|
||||||
- `ENABLE_CERTS_MONITORING` – Enable certificate monitoring if `USE_FOREIGN_CERT_MANAGER=true`. If certificates change, services will be automatically restarted. (default: `false`)
|
- `ENABLE_CERTS_MONITORING` – Enable certificate monitoring if `USE_FOREIGN_CERT_MANAGER=true`. If certificates change, services will be automatically restarted. (default: `false`)
|
||||||
- `CERTS_MONITORING_TIMEOUT` – Interval in seconds to check if certificates have changed. (default: `'60'`)
|
- `CERTS_MONITORING_TIMEOUT` – Interval in seconds to check if certificates have changed. (default: `60`)
|
||||||
- `CMDEPLOY_STAGES` – Deployment stages to run on container start. (default: `"configure,activate"`). Set to `"install,configure,activate"` to force a full reinstall.
|
- `CMDEPLOY_STAGES` – Deployment stages to run on container start. (default: `"configure,activate"`). Set to `"install,configure,activate"` to force a full reinstall.
|
||||||
|
|
||||||
You can also use any variables from the [ini configuration file](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/ini/chatmail.ini.f); they must be in uppercase.
|
You can also use any variables from the [ini configuration file](https://github.com/chatmail/relay/blob/main/chatmaild/src/chatmaild/ini/chatmail.ini.f); they must be in uppercase.
|
||||||
|
|
||||||
4. Build the Docker image:
|
3. Start the container:
|
||||||
|
|
||||||
```shell
|
|
||||||
docker compose build chatmail
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Start docker compose and wait for the installation to finish:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker compose up -d # start service
|
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
|
4. After installation is complete, you can open `https://<your_domain_name>` in your browser.
|
||||||
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.
|
## Managing the server
|
||||||
|
|
||||||
## Using custom files
|
Use `docker exec` to run cmdeploy commands inside the container:
|
||||||
|
|
||||||
When using Docker, you can apply modified configuration files to make the installation more personalized. This is usually needed for the `www/src` section so that the Chatmail landing page is customized to your taste, but it can be used for any other cases as well.
|
|
||||||
|
|
||||||
To replace files correctly:
|
|
||||||
|
|
||||||
1. Create the `./custom` directory. It is in `.gitignore`, so it won’t cause conflicts when updating.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mkdir -p ./custom
|
# Show required DNS records
|
||||||
|
docker exec chatmail /opt/cmdeploy/bin/cmdeploy dns --ssh-host @docker
|
||||||
|
|
||||||
|
# Check server status
|
||||||
|
docker exec chatmail /opt/cmdeploy/bin/cmdeploy status --ssh-host @docker
|
||||||
|
|
||||||
|
# Run benchmarks (can also run from any machine with cmdeploy installed)
|
||||||
|
docker exec chatmail /opt/cmdeploy/bin/cmdeploy bench chat.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Modify the required file. For example, `index.md`:
|
## Customization
|
||||||
|
|
||||||
|
### Custom website
|
||||||
|
|
||||||
|
You can customize the Chatmail landing page by mounting a directory with your own website source files.
|
||||||
|
|
||||||
|
1. Create a directory with your custom website source:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mkdir -p ./custom/www/src
|
mkdir -p ./custom/www/src
|
||||||
nano ./custom/www/src/index.md
|
nano ./custom/www/src/index.md
|
||||||
```
|
```
|
||||||
|
|
||||||
3. In `docker-compose.yaml`, add the file mount in the `volumes` section:
|
2. In `docker-compose.yaml`, uncomment or add the website volume mount:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
chatmail:
|
chatmail:
|
||||||
volumes:
|
volumes:
|
||||||
...
|
...
|
||||||
## custom resources
|
- ./custom/www:/opt/chatmail-www
|
||||||
- ./custom/www/src/index.md:/opt/chatmail/www/src/index.md
|
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Restart the service:
|
3. Restart the service:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker compose down
|
docker compose down
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Custom chatmail.ini
|
||||||
|
|
||||||
|
Instead of using environment variables, you can mount your own `chatmail.ini` configuration file. This is useful if you prefer managing the full ini file directly or want to share one configuration across environments.
|
||||||
|
|
||||||
|
1. In `docker-compose.yaml`, uncomment or add the ini volume mount:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
chatmail:
|
||||||
|
volumes:
|
||||||
|
...
|
||||||
|
- ./chatmail.ini:/etc/chatmail/chatmail.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Environment variables from `.env` are still applied on top of the mounted file at container start, so you can combine both approaches.
|
||||||
|
|
||||||
## Migrating from a bare-metal install
|
## Migrating from a bare-metal install
|
||||||
|
|
||||||
If you have an existing bare-metal Chatmail installation and want to switch to Docker:
|
If you have an existing bare-metal Chatmail installation and want to switch to Docker:
|
||||||
@@ -144,6 +169,8 @@ systemctl disable postfix dovecot doveauth nginx opendkim unbound acmetool-redir
|
|||||||
python3 docker/cm_ini_to_env.py /usr/local/lib/chatmaild/chatmail.ini .env
|
python3 docker/cm_ini_to_env.py /usr/local/lib/chatmaild/chatmail.ini .env
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or mount it (see above).
|
||||||
|
|
||||||
3. Copy persistent data into the `./data/` subdirectories:
|
3. Copy persistent data into the `./data/` subdirectories:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|||||||
Reference in New Issue
Block a user