1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-20 05:11:30 +00:00

[PHP-FPM] use python bootstrapper to start PHP-FPM container

This commit is contained in:
FreddleSpl0it
2025-05-22 13:06:35 +02:00
parent 767d746419
commit f329549c2e
12 changed files with 247 additions and 225 deletions

5
.gitignore vendored
View File

@@ -31,6 +31,11 @@ data/conf/nginx/*.bak
data/conf/nginx/*.conf data/conf/nginx/*.conf
data/conf/nginx/*.custom data/conf/nginx/*.custom
data/conf/phpfpm/sogo-sso/sogo-sso.pass data/conf/phpfpm/sogo-sso/sogo-sso.pass
data/conf/phpfpm/opcache-recommended.ini
data/conf/phpfpm/other.ini
data/conf/phpfpm/pools.conf
data/conf/phpfpm/session_store.ini
data/conf/phpfpm/upload.ini
data/conf/portainer/ data/conf/portainer/
data/conf/postfix/allow_mailcow_local.regexp data/conf/postfix/allow_mailcow_local.regexp
data/conf/postfix/custom_postscreen_whitelist.cidr data/conf/postfix/custom_postscreen_whitelist.cidr

View File

@@ -25,6 +25,8 @@ def main():
from modules.BootstrapClamd import Bootstrap from modules.BootstrapClamd import Bootstrap
elif container_name == "mysql-mailcow": elif container_name == "mysql-mailcow":
from modules.BootstrapMysql import Bootstrap from modules.BootstrapMysql import Bootstrap
elif container_name == "php-fpm-mailcow":
from modules.BootstrapPhpfpm import Bootstrap
else: else:
print(f"No bootstrap handler for container: {container_name}", file=sys.stderr) print(f"No bootstrap handler for container: {container_name}", file=sys.stderr)
sys.exit(1) sys.exit(1)

View File

@@ -0,0 +1,207 @@
from jinja2 import Environment, FileSystemLoader
from modules.BootstrapBase import BootstrapBase
from pathlib import Path
import os
import ipaddress
import sys
import time
import platform
import subprocess
class Bootstrap(BootstrapBase):
def bootstrap(self):
self.connect_mysql()
self.connect_redis()
# Setup Jinja2 Environment and load vars
self.env = Environment(
loader=FileSystemLoader('/php-conf/config_templates'),
keep_trailing_newline=True,
lstrip_blocks=True,
trim_blocks=True
)
extra_vars = {
}
self.env_vars = self.prepare_template_vars('/overwrites.json', extra_vars)
print("Set Timezone")
self.set_timezone()
# Prepare Redis and MySQL Database
# TODO: move to dockerapi
if self.isYes(os.getenv("MASTER", "")):
print("We are master, preparing...")
self.prepare_redis()
self.setup_apikeys(
os.getenv("API_ALLOW_FROM", "").strip(),
os.getenv("API_KEY", "").strip(),
os.getenv("API_KEY_READ_ONLY", "").strip()
)
self.setup_mysql_events()
print("Render config")
self.render_config("opcache-recommended.ini.j2", "/usr/local/etc/php/conf.d/opcache-recommended.ini")
self.render_config("pools.conf.j2", "/usr/local/etc/php-fpm.d/z-pools.conf")
self.render_config("other.ini.j2", "/usr/local/etc/php/conf.d/zzz-other.ini")
self.render_config("upload.ini.j2", "/usr/local/etc/php/conf.d/upload.ini")
self.render_config("session_store.ini.j2", "/usr/local/etc/php/conf.d/session_store.ini")
self.render_config("0081-custom-mailcow.css.j2", "/web/css/build/0081-custom-mailcow.css")
self.copy_file("/usr/local/etc/php/conf.d/opcache-recommended.ini", "/php-conf/opcache-recommended.ini")
self.copy_file("/usr/local/etc/php-fpm.d/z-pools.conf", "/php-conf/pools.conf")
self.copy_file("/usr/local/etc/php/conf.d/zzz-other.ini", "/php-conf/other.ini")
self.copy_file("/usr/local/etc/php/conf.d/upload.ini", "/php-conf/upload.ini")
self.copy_file("/usr/local/etc/php/conf.d/session_store.ini", "/php-conf/session_store.ini")
self.set_owner("/global_sieve", 82, 82, recursive=True)
self.set_owner("/web/templates/cache", 82, 82, recursive=True)
self.remove("/web/templates/cache", wipe_contents=True, exclude=[".gitkeep"])
print("Running DB init...")
self.run_command(["php", "-c", "/usr/local/etc/php", "-f", "/web/inc/init_db.inc.php"], check=False)
def prepare_redis(self):
print("Setting default Redis keys if missing...")
# Q_RELEASE_FORMAT
if self.redis_conn.get("Q_RELEASE_FORMAT") is None:
self.redis_conn.set("Q_RELEASE_FORMAT", "raw")
# Q_MAX_AGE
if self.redis_conn.get("Q_MAX_AGE") is None:
self.redis_conn.set("Q_MAX_AGE", 365)
# PASSWD_POLICY hash defaults
if self.redis_conn.hget("PASSWD_POLICY", "length") is None:
self.redis_conn.hset("PASSWD_POLICY", mapping={
"length": 6,
"chars": 0,
"special_chars": 0,
"lowerupper": 0,
"numbers": 0
})
# DOMAIN_MAP
print("Rebuilding DOMAIN_MAP from MySQL...")
self.redis_conn.delete("DOMAIN_MAP")
domains = set()
try:
cursor = self.mysql_conn.cursor()
cursor.execute("SELECT domain FROM domain")
domains.update(row[0] for row in cursor.fetchall())
cursor.execute("SELECT alias_domain FROM alias_domain")
domains.update(row[0] for row in cursor.fetchall())
cursor.close()
if domains:
for domain in domains:
self.redis_conn.hset("DOMAIN_MAP", domain, 1)
print(f"{len(domains)} domains added to DOMAIN_MAP.")
else:
print("No domains found to insert into DOMAIN_MAP.")
except Exception as e:
print(f"Failed to rebuild DOMAIN_MAP: {e}")
def setup_apikeys(self, api_allow_from, api_key_rw, api_key_ro):
if not api_allow_from or api_allow_from == "invalid":
return
print("Validating API_ALLOW_FROM IPs...")
ip_list = [ip.strip() for ip in api_allow_from.split(",")]
validated_ips = []
for ip in ip_list:
try:
ipaddress.ip_network(ip, strict=False)
validated_ips.append(ip)
except ValueError:
continue
if not validated_ips:
print("No valid IPs found in API_ALLOW_FROM")
return
allow_from_str = ",".join(validated_ips)
cursor = self.mysql_conn.cursor()
try:
if api_key_rw and api_key_rw != "invalid":
print("Setting RW API key...")
cursor.execute("DELETE FROM api WHERE access = 'rw'")
cursor.execute(
"INSERT INTO api (api_key, active, allow_from, access) VALUES (%s, %s, %s, %s)",
(api_key_rw, 1, allow_from_str, "rw")
)
if api_key_ro and api_key_ro != "invalid":
print("Setting RO API key...")
cursor.execute("DELETE FROM api WHERE access = 'ro'")
cursor.execute(
"INSERT INTO api (api_key, active, allow_from, access) VALUES (%s, %s, %s, %s)",
(api_key_ro, 1, allow_from_str, "ro")
)
self.mysql_conn.commit()
print("API key(s) set successfully.")
except Exception as e:
print(f"Failed to configure API keys: {e}")
self.mysql_conn.rollback()
finally:
cursor.close()
def setup_mysql_events(self):
print("Creating scheduled MySQL EVENTS...")
queries = [
"DROP EVENT IF EXISTS clean_spamalias;",
"""
CREATE EVENT clean_spamalias
ON SCHEDULE EVERY 1 DAY
DO
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP();
""",
"DROP EVENT IF EXISTS clean_oauth2;",
"""
CREATE EVENT clean_oauth2
ON SCHEDULE EVERY 1 DAY
DO
BEGIN
DELETE FROM oauth_refresh_tokens WHERE expires < NOW();
DELETE FROM oauth_access_tokens WHERE expires < NOW();
DELETE FROM oauth_authorization_codes WHERE expires < NOW();
END;
""",
"DROP EVENT IF EXISTS clean_sasl_log;",
"""
CREATE EVENT clean_sasl_log
ON SCHEDULE EVERY 1 DAY
DO
BEGIN
DELETE sasl_log.* FROM sasl_log
LEFT JOIN (
SELECT username, service, MAX(datetime) AS lastdate
FROM sasl_log
GROUP BY username, service
) AS last
ON sasl_log.username = last.username AND sasl_log.service = last.service
WHERE datetime < DATE_SUB(NOW(), INTERVAL 31 DAY)
AND datetime < lastdate;
DELETE FROM sasl_log
WHERE username NOT IN (SELECT username FROM mailbox)
AND datetime < DATE_SUB(NOW(), INTERVAL 31 DAY);
END;
"""
]
try:
cursor = self.mysql_conn.cursor()
for query in queries:
cursor.execute(query)
self.mysql_conn.commit()
cursor.close()
print("MySQL EVENTS created successfully.")
except Exception as e:
print(f"Failed to create MySQL EVENTS: {e}")
self.mysql_conn.rollback()

View File

@@ -63,6 +63,7 @@ RUN apk add -U --no-cache autoconf \
samba-client \ samba-client \
zlib-dev \ zlib-dev \
tzdata \ tzdata \
python3 py3-pip \
&& pecl install APCu-${APCU_PECL_VERSION} \ && pecl install APCu-${APCU_PECL_VERSION} \
&& pecl install imagick-${IMAGICK_PECL_VERSION} \ && pecl install imagick-${IMAGICK_PECL_VERSION} \
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \ && pecl install mailparse-${MAILPARSE_PECL_VERSION} \
@@ -107,8 +108,18 @@ RUN apk add -U --no-cache autoconf \
pcre-dev \ pcre-dev \
zlib-dev zlib-dev
COPY ./docker-entrypoint.sh / RUN pip install --break-system-packages \
mysql-connector-python \
jinja2 \
redis \
dnspython
COPY data/Dockerfiles/bootstrap /bootstrap
COPY data/Dockerfiles/phpfpm/docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"] ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["php-fpm"] CMD ["php-fpm"]

View File

@@ -1,219 +1,5 @@
#!/bin/bash #!/bin/bash
function array_by_comma { local IFS=","; echo "$*"; }
# Wait for containers
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for SQL..."
sleep 2
done
# Do not attempt to write to slave
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
REDIS_HOST=$REDIS_SLAVEOF_IP
REDIS_PORT=$REDIS_SLAVEOF_PORT
else
REDIS_HOST="redis"
REDIS_PORT="6379"
fi
REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -a ${REDISPASS} --no-auth-warning"
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
echo "Waiting for Redis..."
sleep 2
done
# Set redis session store
echo -n '
session.save_handler = redis
session.save_path = "tcp://'${REDIS_HOST}':'${REDIS_PORT}'?auth='${REDISPASS}'"
' > /usr/local/etc/php/conf.d/session_store.ini
# Check mysql_upgrade (master and slave)
CONTAINER_ID=
until [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ ^[[:alnum:]]*$ ]]; do
CONTAINER_ID=$(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" 2> /dev/null)
echo "Could not get mysql-mailcow container id... trying again"
sleep 2
done
echo "MySQL @ ${CONTAINER_ID}"
SQL_LOOP_C=0
SQL_CHANGED=0
until [[ ${SQL_UPGRADE_STATUS} == 'success' ]]; do
if [ ${SQL_LOOP_C} -gt 4 ]; then
echo "Tried to upgrade MySQL and failed, giving up after ${SQL_LOOP_C} retries and starting container (oops, not good)"
break
fi
SQL_FULL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json')
SQL_UPGRADE_STATUS=$(echo ${SQL_FULL_UPGRADE_RETURN} | jq -r .type)
SQL_LOOP_C=$((SQL_LOOP_C+1))
echo "SQL upgrade iteration #${SQL_LOOP_C}"
if [[ ${SQL_UPGRADE_STATUS} == 'warning' ]]; then
SQL_CHANGED=1
echo "MySQL applied an upgrade, debug output:"
echo ${SQL_FULL_UPGRADE_RETURN}
sleep 3
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for SQL to return, please wait"
sleep 2
done
continue
elif [[ ${SQL_UPGRADE_STATUS} == 'success' ]]; then
echo "MySQL is up-to-date - debug output:"
echo ${SQL_FULL_UPGRADE_RETURN}
else
echo "No valid reponse for mysql_upgrade was received, debug output:"
echo ${SQL_FULL_UPGRADE_RETURN}
fi
done
# doing post-installation stuff, if SQL was upgraded (master and slave)
if [ ${SQL_CHANGED} -eq 1 ]; then
POSTFIX=$(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"postfix-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" 2> /dev/null)
if [[ -z "${POSTFIX}" ]] || ! [[ "${POSTFIX}" =~ ^[[:alnum:]]*$ ]]; then
echo "Could not determine Postfix container ID, skipping Postfix restart."
else
echo "Restarting Postfix"
curl -X POST --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${POSTFIX}/restart | jq -r '.msg'
echo "Sleeping 5 seconds..."
sleep 5
fi
fi
# Check mysql tz import (master and slave)
TZ_CHECK=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT CONVERT_TZ('2019-11-02 23:33:00','Europe/Berlin','UTC') AS time;" -BN 2> /dev/null)
if [[ -z ${TZ_CHECK} ]] || [[ "${TZ_CHECK}" == "NULL" ]]; then
SQL_FULL_TZINFO_IMPORT_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_tzinfo_to_sql"}' --silent -H 'Content-type: application/json')
echo "MySQL mysql_tzinfo_to_sql - debug output:"
echo ${SQL_FULL_TZINFO_IMPORT_RETURN}
fi
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing..."
# Set a default release format
if [[ -z $(${REDIS_CMDLINE} --raw GET Q_RELEASE_FORMAT) ]]; then
${REDIS_CMDLINE} --raw SET Q_RELEASE_FORMAT raw
fi
# Set max age of q items - if unset
if [[ -z $(${REDIS_CMDLINE} --raw GET Q_MAX_AGE) ]]; then
${REDIS_CMDLINE} --raw SET Q_MAX_AGE 365
fi
# Set default password policy - if unset
if [[ -z $(${REDIS_CMDLINE} --raw HGET PASSWD_POLICY length) ]]; then
${REDIS_CMDLINE} --raw HSET PASSWD_POLICY length 6
${REDIS_CMDLINE} --raw HSET PASSWD_POLICY chars 0
${REDIS_CMDLINE} --raw HSET PASSWD_POLICY special_chars 0
${REDIS_CMDLINE} --raw HSET PASSWD_POLICY lowerupper 0
${REDIS_CMDLINE} --raw HSET PASSWD_POLICY numbers 0
fi
# Trigger db init
echo "Running DB init..."
php -c /usr/local/etc/php -f /web/inc/init_db.inc.php
# Recreating domain map
echo "Rebuilding domain map in Redis..."
declare -a DOMAIN_ARR
${REDIS_CMDLINE} DEL DOMAIN_MAP > /dev/null
while read line
do
DOMAIN_ARR+=("$line")
done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain" -Bs)
while read line
do
DOMAIN_ARR+=("$line")
done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT alias_domain FROM alias_domain" -Bs)
if [[ ! -z ${DOMAIN_ARR} ]]; then
for domain in "${DOMAIN_ARR[@]}"; do
${REDIS_CMDLINE} HSET DOMAIN_MAP ${domain} 1 > /dev/null
done
fi
# Set API options if env vars are not empty
if [[ ${API_ALLOW_FROM} != "invalid" ]] && [[ ! -z ${API_ALLOW_FROM} ]]; then
IFS=',' read -r -a API_ALLOW_FROM_ARR <<< "${API_ALLOW_FROM}"
declare -a VALIDATED_API_ALLOW_FROM_ARR
REGEX_IP6='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$'
REGEX_IP4='^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/([0-9]|[1-2][0-9]|3[0-2]))?$'
for IP in "${API_ALLOW_FROM_ARR[@]}"; do
if [[ ${IP} =~ ${REGEX_IP6} ]] || [[ ${IP} =~ ${REGEX_IP4} ]]; then
VALIDATED_API_ALLOW_FROM_ARR+=("${IP}")
fi
done
VALIDATED_IPS=$(array_by_comma ${VALIDATED_API_ALLOW_FROM_ARR[*]})
if [[ ! -z ${VALIDATED_IPS} ]]; then
if [[ ${API_KEY} != "invalid" ]] && [[ ! -z ${API_KEY} ]]; then
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELETE FROM api WHERE access = 'rw';
INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY}", "1", "${VALIDATED_IPS}", "rw");
EOF
fi
if [[ ${API_KEY_READ_ONLY} != "invalid" ]] && [[ ! -z ${API_KEY_READ_ONLY} ]]; then
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELETE FROM api WHERE access = 'ro';
INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY_READ_ONLY}", "1", "${VALIDATED_IPS}", "ro");
EOF
fi
fi
fi
# Create events (master only, STATUS for event on slave will be SLAVESIDE_DISABLED)
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DROP EVENT IF EXISTS clean_spamalias;
DELIMITER //
CREATE EVENT clean_spamalias
ON SCHEDULE EVERY 1 DAY DO
BEGIN
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP();
END;
//
DELIMITER ;
DROP EVENT IF EXISTS clean_oauth2;
DELIMITER //
CREATE EVENT clean_oauth2
ON SCHEDULE EVERY 1 DAY DO
BEGIN
DELETE FROM oauth_refresh_tokens WHERE expires < NOW();
DELETE FROM oauth_access_tokens WHERE expires < NOW();
DELETE FROM oauth_authorization_codes WHERE expires < NOW();
END;
//
DELIMITER ;
DROP EVENT IF EXISTS clean_sasl_log;
DELIMITER //
CREATE EVENT clean_sasl_log
ON SCHEDULE EVERY 1 DAY DO
BEGIN
DELETE sasl_log.* FROM sasl_log
LEFT JOIN (
SELECT username, service, MAX(datetime) AS lastdate
FROM sasl_log
GROUP BY username, service
) AS last ON sasl_log.username = last.username AND sasl_log.service = last.service
WHERE datetime < DATE_SUB(NOW(), INTERVAL 31 DAY) AND datetime < lastdate;
DELETE FROM sasl_log
WHERE username NOT IN (SELECT username FROM mailbox) AND
datetime < DATE_SUB(NOW(), INTERVAL 31 DAY);
END;
//
DELIMITER ;
EOF
fi
# Create dummy for custom overrides of mailcow style
[[ ! -f /web/css/build/0081-custom-mailcow.css ]] && echo '/* Autogenerated by mailcow */' > /web/css/build/0081-custom-mailcow.css
# Fix permissions for global filters
chown -R 82:82 /global_sieve/*
# Fix permissions on twig cache folder
chown -R 82:82 /web/templates/cache
# Clear cache
find /web/templates/cache/* -not -name '.gitkeep' -delete
# Run hooks # Run hooks
for file in /hooks/*; do for file in /hooks/*; do
if [ -x "${file}" ]; then if [ -x "${file}" ]; then
@@ -222,4 +8,13 @@ for file in /hooks/*; do
fi fi
done done
python3 -u /bootstrap/main.py
BOOTSTRAP_EXIT_CODE=$?
if [ $BOOTSTRAP_EXIT_CODE -ne 0 ]; then
echo "Bootstrap failed with exit code $BOOTSTRAP_EXIT_CODE. Not starting PHP-FPM."
exit $BOOTSTRAP_EXIT_CODE
fi
echo "Bootstrap succeeded. Starting PHP-FPM..."
exec "$@" exec "$@"

View File

@@ -0,0 +1,3 @@
/*
Custom styling for mailcow UI
*/

View File

@@ -0,0 +1,2 @@
session.save_handler = redis
session.save_path = "tcp://{{ REDIS_SLAVEOF_IP or 'redis-mailcow' }}:{{ REDIS_SLAVEOF_PORT or '6379' }}?auth={{ REDISPASS }}"

View File

@@ -131,7 +131,7 @@ services:
- rspamd - rspamd
php-fpm-mailcow: php-fpm-mailcow:
image: ghcr.io/mailcow/phpfpm:1.93 image: ghcr.io/mailcow/phpfpm:nightly-19052025
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on: depends_on:
- redis-mailcow - redis-mailcow
@@ -151,12 +151,8 @@ services:
- mysql-socket-vol-1:/var/run/mysqld/ - mysql-socket-vol-1:/var/run/mysqld/
- ./data/conf/sogo/:/etc/sogo/:z - ./data/conf/sogo/:/etc/sogo/:z
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z
- ./data/conf/phpfpm/crons:/crons:z - ./data/conf/phpfpm:/php-conf:z
- ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/:z - ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/:z
- ./data/conf/phpfpm/php-fpm.d/pools.conf:/usr/local/etc/php-fpm.d/z-pools.conf:Z
- ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini:Z
- ./data/conf/phpfpm/php-conf.d/upload.ini:/usr/local/etc/php/conf.d/upload.ini:Z
- ./data/conf/phpfpm/php-conf.d/other.ini:/usr/local/etc/php/conf.d/zzz-other.ini:Z
- ./data/conf/dovecot/global_sieve_before:/global_sieve/before:z - ./data/conf/dovecot/global_sieve_before:/global_sieve/before:z
- ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z - ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z
- ./data/assets/templates:/tpls:z - ./data/assets/templates:/tpls:z
@@ -164,6 +160,7 @@ services:
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
- CONTAINER_NAME=php-fpm-mailcow
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- REDISPASS=${REDISPASS} - REDISPASS=${REDISPASS}
@@ -204,10 +201,10 @@ services:
ofelia.enabled: "true" ofelia.enabled: "true"
ofelia.job-exec.phpfpm_keycloak_sync.schedule: "@every 1m" ofelia.job-exec.phpfpm_keycloak_sync.schedule: "@every 1m"
ofelia.job-exec.phpfpm_keycloak_sync.no-overlap: "true" ofelia.job-exec.phpfpm_keycloak_sync.no-overlap: "true"
ofelia.job-exec.phpfpm_keycloak_sync.command: "/bin/bash -c \"php /crons/keycloak-sync.php || exit 0\"" ofelia.job-exec.phpfpm_keycloak_sync.command: "/bin/bash -c \"php /php-conf/crons/keycloak-sync.php || exit 0\""
ofelia.job-exec.phpfpm_ldap_sync.schedule: "@every 1m" ofelia.job-exec.phpfpm_ldap_sync.schedule: "@every 1m"
ofelia.job-exec.phpfpm_ldap_sync.no-overlap: "true" ofelia.job-exec.phpfpm_ldap_sync.no-overlap: "true"
ofelia.job-exec.phpfpm_ldap_sync.command: "/bin/bash -c \"php /crons/ldap-sync.php || exit 0\"" ofelia.job-exec.phpfpm_ldap_sync.command: "/bin/bash -c \"php /php-conf/crons/ldap-sync.php || exit 0\""
networks: networks:
mailcow-network: mailcow-network:
aliases: aliases: