From f1d1c175f900b432aede252d50b7a5e2f9e27f7b Mon Sep 17 00:00:00 2001 From: Leonard Jonathan Oh Date: Mon, 13 Mar 2023 15:20:31 +0000 Subject: [PATCH] Enhancement: Add Docker Hub image --- .docker/release/Dockerfile | 139 +++++++++++++----- .docker/release/files/entrypoint.sh | 77 +++++----- .../release/files/etc/logrotate.d/snappymail | 5 - .docker/release/files/etc/nginx/nginx.conf | 2 +- .docker/release/files/logrotate-loop.sh | 113 -------------- .docker/release/files/snappymail/include.php | 3 + .docker/release/files/supervisor.conf | 34 +---- .../usr/local/etc/php-fpm.d/php-fpm.conf | 7 +- .github/workflows/docker.yml | 78 ++++++++++ README.md | 4 + examples/README.md | 123 ++++++++++++++++ examples/docker-compose.mysql.yml | 45 ++++++ examples/docker-compose.postgres.yml | 44 ++++++ examples/docker-compose.simple.yml | 18 +++ examples/docker-compose.traefik.yml | 74 ++++++++++ 15 files changed, 538 insertions(+), 228 deletions(-) mode change 100644 => 100755 .docker/release/files/entrypoint.sh delete mode 100644 .docker/release/files/etc/logrotate.d/snappymail delete mode 100644 .docker/release/files/logrotate-loop.sh create mode 100644 .docker/release/files/snappymail/include.php create mode 100644 .github/workflows/docker.yml create mode 100644 examples/README.md create mode 100644 examples/docker-compose.mysql.yml create mode 100644 examples/docker-compose.postgres.yml create mode 100644 examples/docker-compose.simple.yml create mode 100644 examples/docker-compose.traefik.yml diff --git a/.docker/release/Dockerfile b/.docker/release/Dockerfile index 97757bb44..31b431e34 100644 --- a/.docker/release/Dockerfile +++ b/.docker/release/Dockerfile @@ -1,51 +1,110 @@ +FROM alpine:3.15 AS builder +RUN apk add --no-cache npm +RUN npm install -g gulp yarn +RUN apk add --no-cache php7 php7-json php-phar php-zip +COPY . /source +WORKDIR /source +RUN yarn install +# Patch release.php with hotfix from: https://github.com/xgbstar1/snappymail-docker/blob/main/Dockerfile, so that release.php doesn't fail with error +RUN sed -i 's_^if.*rename.*snappymail.v.0.0.0.*$_if (!!system("mv snappymail/v/0.0.0 snappymail/v/{$package->version}")) {_' cli/release.php || true +RUN php release.php +RUN set -eux; \ + VERSION=$( ls build/dist/releases/webmail ); \ + ls -al build/dist/releases/webmail/$VERSION/snappymail-$VERSION.tar.gz; \ + mkdir -p /snappymail; \ + tar -zxvf build/dist/releases/webmail/$VERSION/snappymail-$VERSION.tar.gz -C /snappymail; \ + find /snappymail -type d -exec chmod 550 {} \; ; \ + find /snappymail -type f -exec chmod 440 {} \; ; \ + find /snappymail/data -type d -exec chmod 750 {} \; ; \ + # Remove unneeded files + rm -v /snappymail/README.md /snappymail/_include.php + # Inspired by the original Rainloop dockerfile from youtous on GitLab -FROM php:8.1-fpm-bullseye +FROM php:8.1-fpm-alpine AS final -ARG FILES_ZIP - - - -LABEL org.label-schema.description="SnappyMail webmail client image using nginx, php-fpm based on Debian Buster" - -ENV UID=991 GID=991 UPLOAD_MAX_SIZE=25M LOG_TO_STDERR=true MEMORY_LIMIT=128M SECURE_COOKIES=true -ENV fpm.pool.clear_env=false +LABEL org.label-schema.description="SnappyMail webmail client image using nginx, php-fpm on Alpine" # Install dependencies such as nginx -RUN mkdir -p /usr/share/man/man1/ /usr/share/man/man3/ /usr/share/man/man7/ && \ - apt-get update -q --fix-missing && \ - apt-get -y upgrade && \ - apt-get install --no-install-recommends -y \ - apt-transport-https gnupg openssl wget curl ca-certificates nginx supervisor sudo \ - unzip libzip-dev libxml2-dev libldb-dev libldap2-dev \ - sqlite3 libsqlite3-dev libsqlite3-0 libpq-dev postgresql-client mariadb-client logrotate \ - zip mlocate libpcre3-dev libicu-dev \ - build-essential chrpath libssl-dev \ - libxft-dev libfreetype6 libfreetype6-dev \ - libpng-dev libjpeg62-turbo-dev \ - libfontconfig1 libfontconfig1-dev \ - && \ - rm -rf /var/lib/apt/lists/* +RUN apk add --no-cache ca-certificates nginx supervisor bash # Install PHP extensions -RUN php -m && \ - docker-php-ext-configure ldap --with-libdir=lib/$(uname -m)-linux-gnu/ && \ - docker-php-ext-configure intl && \ - docker-php-ext-configure gd --with-freetype --with-jpeg && \ - docker-php-ext-install ldap opcache pdo_mysql pdo_pgsql zip intl gd && \ - php -m +# gd +RUN set -eux; \ + apk add --no-cache freetype libjpeg-turbo libpng; \ + apk add --no-cache --virtual .deps freetype-dev libjpeg-turbo-dev libpng-dev; \ + docker-php-ext-configure gd --with-freetype --with-jpeg; \ + docker-php-ext-install gd; \ + apk del .deps +# gnupg +RUN set -eux; \ + apk add --no-cache gnupg gpgme; \ + apk add --no-cache --virtual .deps gpgme-dev; \ + apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \ + pecl install gnupg; \ + docker-php-ext-enable gnupg; \ + docker-php-source delete; \ + apk del .build-dependencies; \ + apk del .deps +# intl +RUN set -eux; \ + apk add --no-cache icu-libs; \ + apk add --no-cache --virtual .deps icu-dev; \ + docker-php-ext-configure intl; \ + docker-php-ext-install intl; \ + apk del .deps +# ldap +RUN set -eux; \ + apk add --no-cache libldap; \ + apk add --no-cache --virtual .deps openldap-dev; \ + docker-php-ext-configure ldap; \ + docker-php-ext-install ldap; \ + apk del .deps +# mysql +RUN docker-php-ext-install pdo_mysql +# opcache +RUN docker-php-ext-install opcache +# postgres +RUN set -eux; \ + apk add --no-cache postgresql-libs; \ + apk add --no-cache --virtual .deps postgresql-dev; \ + docker-php-ext-install pdo_pgsql; \ + apk del .deps +# redis +RUN set -eux; \ + apk add --no-cache liblzf zstd-libs; \ + apk add --no-cache --virtual .deps zstd-dev; \ + apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \ + pecl install igbinary; \ + docker-php-ext-enable igbinary; \ + pecl install --configureoptions 'enable-redis-igbinary="yes" enable-redis-lzf="yes" enable-redis-zstd="yes"' redis; \ + docker-php-ext-enable redis; \ + docker-php-source delete; \ + apk del .build-dependencies; \ + apk del .deps +# zip +RUN set -eux; \ + apk add --no-cache libzip; \ + apk add --no-cache --virtual .deps libzip-dev; \ + docker-php-ext-install zip; \ + apk del .deps # Install snappymail -WORKDIR /tmp -COPY ${FILES_ZIP} . -RUN mkdir /snappymail && \ - unzip -q ${FILES_ZIP} -d /snappymail && \ - find /snappymail -type d -exec chmod 755 {} \; && \ - find /snappymail -type f -exec chmod 644 {} \; && \ - rm -rf ${FILES_ZIP} +# The 'www-data' user/group in alpine is 82:82. The 'nginx' user/group in alpine is 101:101, and is part of www-data group +COPY --chown=www-data:www-data --from=builder /snappymail /snappymail +# Use a custom snappymail data folder +RUN mv -v /snappymail/data /var/lib/snappymail; +# Setup configs +COPY --chown=root:root .docker/release/files / +RUN set -eux; \ + chown www-data:www-data /snappymail/include.php; \ + chmod 440 /snappymail/include.php; \ + chmod +x /entrypoint.sh; \ + # Disable the built-in php-fpm configs, since we're using our own config + mv -v /usr/local/etc/php-fpm.d/docker.conf /usr/local/etc/php-fpm.d/docker.conf.disabled; \ + mv -v /usr/local/etc/php-fpm.d/www.conf /usr/local/etc/php-fpm.d/www.conf.disabled; \ + mv -v /usr/local/etc/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf.disabled; -# Install other content -COPY files / -RUN chmod +x /entrypoint.sh && chmod +x /logrotate-loop.sh -VOLUME /snappymail/data +WORKDIR /snappymail +VOLUME /var/lib/snappymail EXPOSE 8888 CMD ["/entrypoint.sh"] diff --git a/.docker/release/files/entrypoint.sh b/.docker/release/files/entrypoint.sh old mode 100644 new mode 100755 index eed82bf5b..567889e57 --- a/.docker/release/files/entrypoint.sh +++ b/.docker/release/files/entrypoint.sh @@ -1,23 +1,16 @@ #!/bin/sh +set -eu -# Create not root user -groupadd --gid "$GID" php-cli -f -adduser --uid "$UID" --disabled-password --gid "$GID" --shell /bin/bash --home /home/php-cli php-cli --force --gecos "" - +UPLOAD_MAX_SIZE=${UPLOAD_MAX_SIZE:-25M} +MEMORY_LIMIT=${MEMORY_LIMIT:-128M} +SECURE_COOKIES=${SECURE_COOKIES:-true} # Set attachment size limit sed -i "s//$UPLOAD_MAX_SIZE/g" /usr/local/etc/php-fpm.d/php-fpm.conf /etc/nginx/nginx.conf sed -i "s//$MEMORY_LIMIT/g" /usr/local/etc/php-fpm.d/php-fpm.conf -# Set log output to STDERR if wanted (LOG_TO_STDERR=true) -if [ "$LOG_TO_STDERR" = true ]; then - echo "[INFO] Logging to stderr activated" - sed -i "s/.*error_log.*$/error_log \/dev\/stderr warn;/" /etc/nginx/nginx.conf - sed -i "s/.*error_log.*$/php_admin_value[error_log] = \/dev\/stderr/" /usr/local/etc/php-fpm.d/php-fpm.conf -fi - # Secure cookies -if [ "${SECURE_COOKIES}" = true ]; then +if [ "${SECURE_COOKIES}" = 'true' ]; then echo "[INFO] Secure cookies activated" { echo 'session.cookie_httponly = On'; @@ -26,43 +19,45 @@ if [ "${SECURE_COOKIES}" = true ]; then } > /usr/local/etc/php/conf.d/cookies.ini; fi -# Copy snappymail default config if absent -SNAPPYMAIL_CONFIG_FILE=/snappymail/data/_data_/_default_/configs/application.ini +# Set permissions on snappymail data +echo "[INFO] Setting permissions on /var/lib/snappymail" +chown -R www-data:www-data /var/lib/snappymail/ +chmod 550 /var/lib/snappymail/ +find /var/lib/snappymail/ -type d -exec chmod 750 {} \; + +# Create snappymail default config if absent +SNAPPYMAIL_CONFIG_FILE=/var/lib/snappymail/_data_/_default_/configs/application.ini if [ ! -f "$SNAPPYMAIL_CONFIG_FILE" ]; then - echo "[INFO] Creating default Snappymail configuration" - mkdir -p $(dirname $SNAPPYMAIL_CONFIG_FILE) - cp /usr/local/include/application.ini $SNAPPYMAIL_CONFIG_FILE + echo "[INFO] Creating default Snappymail configuration: $SNAPPYMAIL_CONFIG_FILE" + # Run snappymail and exit. This populates the snappymail data directory + su - www-data -s /bin/sh -c 'php /snappymail/index.php' > /dev/null fi # Enable output of snappymail logs -if [ "${LOG_TO_STDERR}" = true ]; then - sed -z 's/\; Enable logging\nenable = Off/\; Enable logging\nenable = On/' -i $SNAPPYMAIL_CONFIG_FILE - sed 's/^filename = .*/filename = "errors.log"/' -i $SNAPPYMAIL_CONFIG_FILE - sed 's/^write_on_error_only = .*/write_on_error_only = Off/' -i $SNAPPYMAIL_CONFIG_FILE - sed 's/^write_on_php_error_only = .*/write_on_php_error_only = On/' -i $SNAPPYMAIL_CONFIG_FILE -else - sed -z 's/\; Enable logging\nenable = On/\; Enable logging\nenable = Off/' -i $SNAPPYMAIL_CONFIG_FILE -fi +sed '/^\; Enable logging/{ +N +s/enable = Off/enable = On/ +}' -i $SNAPPYMAIL_CONFIG_FILE +# Redirect snappymail logs to stderr /stdout +sed 's/^filename = .*/filename = "stderr"/' -i $SNAPPYMAIL_CONFIG_FILE +sed 's/^write_on_error_only = .*/write_on_error_only = Off/' -i $SNAPPYMAIL_CONFIG_FILE +sed 's/^write_on_php_error_only = .*/write_on_php_error_only = On/' -i $SNAPPYMAIL_CONFIG_FILE # Always enable snappymail Auth logging sed 's/^auth_logging = .*/auth_logging = On/' -i $SNAPPYMAIL_CONFIG_FILE sed 's/^auth_logging_filename = .*/auth_logging_filename = "auth.log"/' -i $SNAPPYMAIL_CONFIG_FILE sed 's/^auth_logging_format = .*/auth_logging_format = "[{date:Y-m-d H:i:s}] Auth failed: ip={request:ip} user={imap:login} host={imap:host} port={imap:port}"/' -i $SNAPPYMAIL_CONFIG_FILE -# Redirect snappymail logs to stderr /stdout -mkdir -p /snappymail/data/_data_/_default_/logs/ -# empty logs -cp /dev/null /snappymail/data/_data_/_default_/logs/errors.log -cp /dev/null /snappymail/data/_data_/_default_/logs/auth.log -chown -R php-cli:php-cli /snappymail/data/ +sed 's/^auth_syslog = .*/auth_syslog = Off/' -i $SNAPPYMAIL_CONFIG_FILE -# Fix permissions -chown -R $UID:$GID /snappymail/data /var/log /var/lib/nginx -chmod o+w /dev/stdout -chmod o+w /dev/stderr - - -# Touch supervisord PID file in order to fix permissions -touch /run/supervisord.pid -chown php-cli:php-cli /run/supervisord.pid +# Create snappymail admin password if absent +SNAPPYMAIL_ADMIN_PASSWORD_FILE=/var/lib/snappymail/_data_/_default_/admin_password.txt +if [ ! -f "$SNAPPYMAIL_ADMIN_PASSWORD_FILE" ]; then + ( + while ! nc -vz -w 1 localhost 8888 > /dev/null 2>&1; do echo "[INFO] Checking whether Snappymail is alive"; sleep 1; done + echo "[INFO] Creating Snappymail admin password file: $SNAPPYMAIL_ADMIN_PASSWORD_FILE" + wget -qO- 'http://localhost:8888/?/AdminAppData/0/12345/' > /dev/null + echo "[INFO] Snappymail Admin Panel ready at http://localhost:8888/?admin. Login using password in $SNAPPYMAIL_ADMIN_PASSWORD_FILE" + ) & +fi # RUN ! -exec sudo -u php-cli -g php-cli /usr/bin/supervisord -c '/supervisor.conf' --pidfile '/run/supervisord.pid' +exec /usr/bin/supervisord -c /supervisor.conf --pidfile /run/supervisord.pid diff --git a/.docker/release/files/etc/logrotate.d/snappymail b/.docker/release/files/etc/logrotate.d/snappymail deleted file mode 100644 index 701631d98..000000000 --- a/.docker/release/files/etc/logrotate.d/snappymail +++ /dev/null @@ -1,5 +0,0 @@ -/snappymail/data/_data_/_default_/logs/* { - size 10M - rotate 0 - missingok -} \ No newline at end of file diff --git a/.docker/release/files/etc/nginx/nginx.conf b/.docker/release/files/etc/nginx/nginx.conf index 1bb643f23..a024932e8 100644 --- a/.docker/release/files/etc/nginx/nginx.conf +++ b/.docker/release/files/etc/nginx/nginx.conf @@ -11,7 +11,7 @@ http { default_type application/octet-stream; access_log off; - error_log /tmp/ngx_error.log error; + error_log /dev/stderr error; sendfile on; keepalive_timeout 15; diff --git a/.docker/release/files/logrotate-loop.sh b/.docker/release/files/logrotate-loop.sh deleted file mode 100644 index ba1d42469..000000000 --- a/.docker/release/files/logrotate-loop.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------- -# Simple script to invoke logrotate at regular intervals -# ---------------------------------------------------------------------- - -# from https://github.com/misho-kr/docker-appliances/blob/master/nginx-nodejs/logrotate-loop.sh - -LOGROTATE_BIN="logrotate" - -STATE="$HOME/logrotate.state" -CONF="/etc/logrotate.d/snappymail" - -export LOGROTATE_BIN STATE CONF - -RUN_INTERVAL="3600" # every hour - -# helper functions for logging -export FMT="%a %b %d %Y %H:%M:%S GMT%z (%Z)" - -function log_date() { - echo "$(date +"$FMT"): $*" -} - -# ---------------------------------------------------------------------- -# Main loop of the logrotate service: -# -# while True: -# sleep N seconds -# run logrotate -# -# ---------------------------------------------------------------------- - -function logrotate_loop() { - - trap on_terminate TERM INT - - local interval="${1}" - - log_date "====================================================" - log_date - log_date "logrotate service starting (pid=$$)" - log_date "logrotate process will run every ${interval} seconds" - - while true; do - - current_time=$(date "+%s") - next_run_time=$(( current_time + interval )) - - while (( current_time < next_run_time )) - do - logrotate_sleep $(( next_run_time - current_time )) - current_time=$(date "+%s") - done - - logrotate_run - done -} - -# helper function to execute logrotate and pass it the right parameters -function logrotate_run() { - - log_date "logrotate will run now" - ${LOGROTATE_BIN} -s ${STATE} ${CONF} -} - -# ---------------------------------------------------------------------- -# Procedure to idle the execution for a number of seconds -# -# There are two requirements: -# -# - export the PID of the sleep command so that it can be terminated -# in case the logrotate service is being shutdown -# - keep this (bash) process responsive to SIGTERM while in sleep -# mode (normally the signal will be masked and will not be delivered -# until the subprocess completes) -# ---------------------------------------------------------------------- - -proc_sleep_pid="" - -function logrotate_sleep() { - - local sleep_interval=${1} - - log_date "logrotate will sleep for ${sleep_interval} seconds" - - ( exec -a "logrotate: sleep" sleep ${sleep_interval} )& - - proc_sleep_pid=$! - wait ${proc_sleep_pid} -} - -# ---------------------------------------------------------------------- -# Signal handler for logrotate service to make sure the process exits: -# -# - properly by terminating the sleep process that is used to idle -# the service -# - gracefully by writing a message in the log -# ---------------------------------------------------------------------- - -function on_terminate() { - - log_date "logrotate will terminate" - log_date - - kill -TERM ${proc_sleep_pid} - exit 0 -} - -# ---------------------------------------------------------------------- -# main -# ---------------------------------------------------------------------- - -logrotate_loop ${1:-RUN_INTERVAL} diff --git a/.docker/release/files/snappymail/include.php b/.docker/release/files/snappymail/include.php new file mode 100644 index 000000000..53ba30722 --- /dev/null +++ b/.docker/release/files/snappymail/include.php @@ -0,0 +1,3 @@ + diff --git a/.docker/release/files/supervisor.conf b/.docker/release/files/supervisor.conf index da6c6aefe..13a12a290 100644 --- a/.docker/release/files/supervisor.conf +++ b/.docker/release/files/supervisor.conf @@ -1,10 +1,13 @@ [supervisord] nodaemon=true +user=root +logfile=/dev/null +logfile_maxbytes=0 [program:nginx] command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' process_name=%(program_name)s_%(process_num)02d -user=php-cli +user=root numprocs=1 autostart=true autorestart=false @@ -17,7 +20,7 @@ stderr_logfile_maxbytes=0 [program:php-fpm] command=php-fpm -F process_name=%(program_name)s_%(process_num)02d -user=php-cli +user=root numprocs=1 autostart=true autorestart=false @@ -27,34 +30,11 @@ stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 -; reads snappymail logs -[program:snappymail-auth] -command=tail -f /snappymail/data/_data_/_default_/logs/auth.log -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:snappymail-errors] -command=tail -f /snappymail/data/_data_/_default_/logs/errors.log -# everything is an error -stdout_logfile=/dev/stderr -stdout_logfile_maxbytes=0 -redirect_stderr=true - -[program:logrotate] -command=/logrotate-loop.sh -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - [eventlistener:subprocess-stopped] command=php /listener.php process_name=%(program_name)s_%(process_num)02d -user=php-cli +user=root numprocs=1 events=PROCESS_STATE_EXITED,PROCESS_STATE_STOPPED,PROCESS_STATE_FATAL autostart=true -autorestart=unexpected \ No newline at end of file +autorestart=unexpected diff --git a/.docker/release/files/usr/local/etc/php-fpm.d/php-fpm.conf b/.docker/release/files/usr/local/etc/php-fpm.d/php-fpm.conf index 456fdb798..e7870fdf0 100644 --- a/.docker/release/files/usr/local/etc/php-fpm.d/php-fpm.conf +++ b/.docker/release/files/usr/local/etc/php-fpm.d/php-fpm.conf @@ -1,15 +1,20 @@ [global] daemonize = no +error_log = /dev/stderr +log_buffering = no [default] listen = /tmp/php-fpm.sock +user = www-data +listen.owner = www-data +listen.group = www-data pm = ondemand pm.max_children = 30 pm.process_idle_timeout = 10s pm.max_requests = 500 catch_workers_output = yes +decorate_workers_output = no chdir = / -php_admin_value[error_log] = /tmp/php_error.log php_admin_value[log_errors] = On php_admin_value[expose_php] = Off php_admin_value[display_errors] = Off diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..af675cd75 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,78 @@ +name: docker +on: + push: + branches: + - master + tags: + - '**' + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + # This step generates the docker tags + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ github.repository }} + # type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123' + # type=ref,event=branch generates tag(s) on branch only. E.g. 'master', 'master-abc0123' + # type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0', 'v0.0.0-abc0123', and 'latest' + tags: | + type=ref,event=pr + type=ref,suffix=-{{sha}},event=pr + type=ref,event=branch + type=ref,suffix=-{{sha}},event=branch + type=ref,event=tag + type=ref,suffix=-{{sha}},event=tag + + # See: https://github.com/docker/build-push-action/blob/v2.6.1/docs/advanced/cache.md#github-cache + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to Docker Hub registry + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + id: build-and-push + uses: docker/build-push-action@v3 + with: + context: '.' + file: ./.docker/release/Dockerfile + # TODO: Add more arches? + # platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x + platforms: linux/386,linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/README.md b/README.md index 9c5c432c5..a8f5ff0f6 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@

SnappyMail


+ +[![github-actions](https://github.com/the-djmaze/snappymail/workflows/ci/badge.svg)](https://github.com/the-djmaze/snappymail/actions) +[![docker-image-size](https://img.shields.io/docker/image-size/the-djmaze/snappymail/master)](https://hub.docker.com/r/the-djmaze/snappymail) +

Simple, modern, lightweight & fast web-based email client.

diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..68b5baa57 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,123 @@ +# Examples + +Here are some [`docker-compose`] examples. + +## `docker-compose.simple.yml` + +This runs `snappymail`, using [SQLite](https://www.sqlite.org/index.html) as the database. + +Start the stack: + +```sh +docker-compose -f docker-compose.simple.yml up +``` + +Get the Admin Panel password: + +```sh +docker exec -it $( docker-compose -f docker-compose.simple.yml ps -q snappymail ) cat /var/lib/snappymail/_data_/_default_/admin_password.txt +``` + +Now, login to [http://localhost:8888/?admin](http://localhost:8888/?admin) with user `admin` and the admin password. + +## `docker-compose.mysql.yml` + +This runs `snappymail`, using [MariaDB](https://mariadb.org/) (a fork of [MYSQL](https://www.mysql.com/)) as the database. + +Start `snappymail` and `mysql`: + +```sh +docker-compose -f docker-compose.mysql.yml up +``` + +Get the Admin Panel password: + +```sh +docker exec -it $( docker-compose -f docker-compose.mysql.yml ps -q snappymail ) cat /var/lib/snappymail/_data_/_default_/admin_password.txt +``` + +Now, login to [http://localhost:8888/?admin](http://localhost:8888/?admin) with user `admin` and the admin password. + +To setup MySQL as the DB, in Admin Panel, click `Contacts`, check `Enable contacts` and , and under `Storage (PDO)` choose the following: + +- Type: `MySQL` +- Data Source Name (DSN): `host=mysql;port=3306;dbname=snappymail` +- User `snappymail` +- Password `snappymail` + +Click the `Test` button. If it turns green, MySQL is ready to be used for contacts. + +To setup Redis for caching, in Admin Panel, click `Config`, update the following configuration options: + +- `cache > enable`: yes +- `cache > fast_cache_driver`: `redis` +- `labs > fast_cache_redis_host`: `redis` +- `labs > fast_cache_redis_port`: `6379` + +Redis caching is now enabled. + +## `docker-compose.postgres.yml` + +This runs `snappymail`, using [PostgreSQL](https://hub.docker.com/_/postgres) as the database. + +Start `snappymail` and `postgres`: + +```sh +docker-compose -f docker-compose.postgres.yml up +``` + +Get the Admin Panel password: + +```sh +docker exec -it $( docker-compose -f docker-compose.postgres.yml ps -q snappymail ) cat /var/lib/snappymail/_data_/_default_/admin_password.txt +``` + +Now, login to [http://localhost:8888/?admin](http://localhost:8888/?admin) with user `admin` and the admin password. + +To use PostgreSQL as the DB, in Admin Panel, click `Contacts`, check `Enable contacts` and , and under `Storage (PDO)` choose the following: + +- Type: `PostgresSQL` +- Data Source Name (DSN): `host=postgres;port=5432;dbname=snappymail` +- User `snappymail` +- Password `snappymail` + +Click the `Test` button. If it turns green, PostgreSQL is ready to be used for contacts. + +To setup Redis for caching, in Admin Panel, click `Config`, update the following configuration options: + +- `cache > enable`: yes +- `cache > fast_cache_driver`: `redis` +- `labs > fast_cache_redis_host`: `redis` +- `labs > fast_cache_redis_port`: `6379` + +Redis caching is now enabled. + +## `docker-compose.traefik.yml` + +This runs `snappymail`, using [SQLite](https://www.sqlite.org/index.html) as the database, with `traefik` as the TLS reverse proxy and loadbalancer. + +In this example, it is assumed the domain name is `snappymail.example.com`. It is assumed you have an [OVHcloud](https://www.ovh.com/auth/) account to obtain LetsEncrypt TLS certs via `ACME` using DNS challenge for the domain `snappymail.example.com`. If you are using another DNS provider, see [here](https://doc.traefik.io/traefik/https/acme/#providers). + +To begin, edit the `OVH_*` environment variables in [`docker-compose.traefik.yml`](docker-compose.traefik.yml) accordingly: + +```sh +nano docker-compose.traefik.yml +``` + +Start `snappymail` and `traefik`: + +```sh +docker-compose -f docker-compose.traefik.yml up +``` + +`traefik` should now begin requesting a TLS cert for `snappymail.example.com`. The process may take a few minutes. If all goes well, https://snappymail.example.com should now be ready. + +> You may still visit https://snappymail.example.com while waiting for `traefik` to be issued a TLS certificate. `traefik` simply serves a self-signed TLS cert. + +Get the Admin Panel password: + +```sh +docker exec -it $( docker-compose -f docker-compose.traefik.yml ps -q snappymail ) cat /var/lib/snappymail/_data_/_default_/admin_password.txt +``` + +Now, login to [https://snappymail.example.com/?admin](https://snappymail.example.com/?admin) with user `admin` and the admin password. diff --git a/examples/docker-compose.mysql.yml b/examples/docker-compose.mysql.yml new file mode 100644 index 000000000..92ffed1e0 --- /dev/null +++ b/examples/docker-compose.mysql.yml @@ -0,0 +1,45 @@ +version: '2' +services: + snappymail: + image: leojonathanoh/snappymail:merge + ports: + - 8888:8888 + volumes: + - snappymail:/var/lib/snappymail + networks: + - default + - db-network + - redis-network + restart: unless-stopped + + mysql: + image: mariadb:10 + environment: + - MYSQL_ROOT_PASSWORD=root + - MYSQL_USER=snappymail + - MYSQL_PASSWORD=snappymail + - MYSQL_DATABASE=snappymail + volumes: + - mysql:/var/lib/mysql + networks: + - db-network + restart: unless-stopped + + redis: + image: redis:7-alpine + networks: + - redis-network + restart: unless-stopped + +networks: + default: + db-network: + internal: true + redis-network: + internal: true + +volumes: + mysql: + driver: local + snappymail: + driver: local diff --git a/examples/docker-compose.postgres.yml b/examples/docker-compose.postgres.yml new file mode 100644 index 000000000..37c2e37e8 --- /dev/null +++ b/examples/docker-compose.postgres.yml @@ -0,0 +1,44 @@ +version: '2' +services: + snappymail: + image: leojonathanoh/snappymail:merge + ports: + - 8888:8888 + volumes: + - snappymail:/var/lib/snappymail + networks: + - default + - db-network + - redis-network + restart: unless-stopped + + db: + image: postgres:15-alpine + environment: + - POSTGRES_USER=snappymail + - POSTGRES_PASSWORD=snappymail + - POSTGRES_DB=snappymail + volumes: + - postgres:/var/lib/postgresql/data + networks: + - db-network + restart: unless-stopped + + redis: + image: redis:7-alpine + networks: + - redis-network + restart: unless-stopped + +networks: + default: + db-network: + internal: true + redis-network: + internal: true + +volumes: + postgres: + driver: local + snappymail: + driver: local diff --git a/examples/docker-compose.simple.yml b/examples/docker-compose.simple.yml new file mode 100644 index 000000000..e80f79f25 --- /dev/null +++ b/examples/docker-compose.simple.yml @@ -0,0 +1,18 @@ +version: '2' +services: + snappymail: + image: leojonathanoh/snappymail:merge + ports: + - 8888:8888 + volumes: + - snappymail:/var/lib/snappymail + networks: + - default + restart: unless-stopped + +networks: + default: + +volumes: + snappymail: + driver: local diff --git a/examples/docker-compose.traefik.yml b/examples/docker-compose.traefik.yml new file mode 100644 index 000000000..111597a1d --- /dev/null +++ b/examples/docker-compose.traefik.yml @@ -0,0 +1,74 @@ +version: '2' +services: + # The reverse proxy for our web containers + # See https://github.com/traefik/traefik/tree/v2.7/docs/content/user-guides/docker-compose for some examples for enabling HTTPS using ACME + # You will need a domain name. E.g. 'example.com' + # This example requests TLS certs via ACME via DNS challenge using OVHcloud as the DNS provider. + # For a list of supported DNS providers, see: https://doc.traefik.io/traefik/https/acme/#providers + traefik: + image: traefik:v2 + environment: + - OVH_ENDPOINT=xxx + - OVH_APPLICATION_KEY=xxx + - OVH_APPLICATION_SECRET=xxx + - OVH_CONSUMER_KEY=xxx + volumes: + # Allow traefik to listen to the Docker events + - /var/run/docker.sock:/var/run/docker.sock:ro + - acme-volume:/letsencrypt + ports: + - 80:80 + - 443:443 + networks: + - traefik-network + restart: unless-stopped + command: + - --global.checknewversion=false + - --global.sendanonymoususage=false + # - --log.level=DEBUG + - --providers.docker=true + - --providers.docker.exposedbydefault=false + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --certificatesresolvers.myresolver.acme.dnschallenge=true + - --certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh + # - --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory + - --certificatesresolvers.myresolver.acme.email=postmaster@example.com + - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json + + # snappymail will be available at: https://snappymail.example.com + snappymail: + image: leojonathanoh/snappymail:merge + labels: + - "traefik.enable=true" + - "traefik.docker.network=traefik-network" + # traefik v2 + # http + - "traefik.http.routers.snappymail-http.entrypoints=web" + - "traefik.http.routers.snappymail-http.rule=Host(`snappymail.example.com`)" + - "traefik.http.routers.snappymail-http.middlewares=snappymail-http-myRedirectScheme" # Redirect http to https + - "traefik.http.middlewares.snappymail-http-myRedirectScheme.redirectScheme.scheme=https" # Redirect http to https + # https + - "traefik.http.routers.snappymail.entrypoints=websecure" + - "traefik.http.routers.snappymail.tls" + - "traefik.http.routers.snappymail.rule=Host(`snappymail.example.com`)" + - "traefik.http.services.snappymail.loadbalancer.server.port=8888" + expose: + - 8888 + volumes: + - snappymail:/var/lib/snappymail + networks: + - default + - traefik-network + restart: unless-stopped + +networks: + default: + traefik-network: + name: traefik-network + +volumes: + acme-volume: + driver: local + snappymail: + driver: local