Enhancement: Add Docker Hub image (#965)

* Enhancement: Add Docker Hub image

* Push to ghcr.io

* Add all `org.opencontainers.image.xxx` labels using `--label` instead of `LABEL`

* Change `php-fpm` to listen on `:9000` to expose prometheus metrics via `php-fpm_exporter`

* Include `yarn.lock` to speed up builds

* Reorder

* Use correct sha for pr

* Add logging before overriding values in config

* Add `DEBUG=true` env var for verbose `entrypoint.sh` logs

* Print php exception when snappymail fails to generate data directory and config file on the very first time

* Log snappymail version

* Fix COPY statement

* Add .dockerignore

* Add `USER` and `ENTRYPOINT`

* Update `.dockerignore`

* Add docker image test

* Push only if image test succeeds

* Log when startup is successful

* Use plain `docker build` in `build_and_test.sh`

* Fix test output
This commit is contained in:
leo 2023-11-20 14:01:26 +00:00 committed by GitHub
parent 6552a93989
commit 24dbff999e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 5201 additions and 229 deletions

View file

@ -1,51 +1,116 @@
# syntax=docker/dockerfile:1
FROM alpine:3.15 AS builder
RUN apk add --no-cache php7 php7-json php-phar php-zip
RUN apk add --no-cache npm
RUN npm install -g gulp yarn
WORKDIR /source
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
# 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
USER root
WORKDIR /snappymail
VOLUME /var/lib/snappymail
EXPOSE 8888
EXPOSE 9000
ENTRYPOINT []
CMD ["/entrypoint.sh"]

92
.docker/release/files/entrypoint.sh Normal file → Executable file
View file

@ -1,23 +1,20 @@
#!/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 ""
DEBUG=${DEBUG:-}
if [ "$DEBUG" = 'true' ]; then
set -x
fi
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>/$UPLOAD_MAX_SIZE/g" /usr/local/etc/php-fpm.d/php-fpm.conf /etc/nginx/nginx.conf
sed -i "s/<MEMORY_LIMIT>/$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 +23,58 @@ 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
echo "[INFO] Snappymail version: $( ls /snappymail/snappymail/v )"
# 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 and generates the config file
# On error, print php exception and exit
EXITCODE=
su - www-data -s /bin/sh -c 'php /snappymail/index.php' > /tmp/out || EXITCODE=$?
if [ -n "$EXITCODE" ]; then
cat /tmp/out
exit "$EXITCODE"
fi
fi
echo "[INFO] Overriding values in snappymail configuration: $SNAPPYMAIL_CONFIG_FILE"
# 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
(
while ! nc -vz -w 1 localhost 8888 > /dev/null 2>&1; do echo "[INFO] Checking whether nginx is alive"; sleep 1; done
while ! nc -vz -w 1 localhost 9000 > /dev/null 2>&1; do echo "[INFO] Checking whether php-fpm is alive"; sleep 1; done
# 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
echo "[INFO] Creating Snappymail admin password file: $SNAPPYMAIL_ADMIN_PASSWORD_FILE"
wget -T 1 -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
# Touch supervisord PID file in order to fix permissions
touch /run/supervisord.pid
chown php-cli:php-cli /run/supervisord.pid
wget -T 1 -qO- 'http://localhost:8888/' > /dev/null
echo "[INFO] Snappymail ready at http://localhost:8888/"
) &
# 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

View file

@ -1,5 +0,0 @@
/snappymail/data/_data_/_default_/logs/* {
size 10M
rotate 0
missingok
}

View file

@ -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;
@ -95,7 +95,7 @@ http {
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTP_PROXY "";
fastcgi_index index.php;
fastcgi_pass unix:/tmp/php-fpm.sock;
fastcgi_pass 127.0.0.1:9000;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_param REMOTE_ADDR $http_x_real_ip;

View file

@ -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}

View file

@ -0,0 +1,3 @@
<?php
define('APP_DATA_FOLDER_PATH', '/var/lib/snappymail/');
?>

View file

@ -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
autorestart=unexpected

View file

@ -1,15 +1,21 @@
[global]
daemonize = no
error_log = /dev/stderr
log_buffering = no
[default]
listen = /tmp/php-fpm.sock
listen = 9000
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
pm.status_path = /status
php_admin_value[log_errors] = On
php_admin_value[expose_php] = Off
php_admin_value[display_errors] = Off

View file

@ -0,0 +1,7 @@
#!/bin/sh
# This script uses docker buildx to build a docker image, and loads it into the docker daemon. Then it executes ./test.sh to test the docker image
# It is useful for testing release builds in development
set -eu
IMAGE=snappymail/snappymail:test
DOCKER_BUILDX=1 docker build -t "$IMAGE" -f .docker/release/Dockerfile .
.docker/release/test/test.sh "$IMAGE"

View file

@ -0,0 +1,29 @@
# See: https://github.com/GoogleContainerTools/container-structure-test
# See: https://github.com/GoogleContainerTools/container-structure-test/issues/78
schemaVersion: 2.0.0
commandTests:
- name: Integration test
command: /bin/sh
args:
- -c
- |
set -eux
DEBUG=true /entrypoint.sh > /tmp/test 2>&1 &
sleep 5
cat /tmp/test
pidof supervisord
pidof nginx
pidof php-fpm
ls -al /var/lib/snappymail/_data_/_default_/configs/application.ini
ls -al /var/lib/snappymail/_data_/_default_/admin_password.txt
nc -vz localhost 8888
nc -vz localhost 9000
wget -S -T 3 -O /dev/null http://localhost:8888
kill `pidof supervisord`
metadataTest:
exposedPorts: ["8888", "9000"]
volumes: ["/var/lib/snappymail"]
entrypoint: []
cmd: ["/entrypoint.sh"]
workdir: /snappymail
user: root

10
.docker/release/test/test.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
# This script tests a given docker image using https://github.com/GoogleContainerTools/container-structure-test
set -eu
SCRIPT_DIR=$( cd "$(dirname "$0")" && pwd )
IMAGE=${1:-}
echo "Testing image: $IMAGE"
docker run --rm -i \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v "$SCRIPT_DIR/config.yaml:/config.yaml:ro" \
gcr.io/gcp-runtimes/container-structure-test:latest test --image "$IMAGE" --config config.yaml

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
/.git
/node_modules

127
.github/workflows/docker.yml vendored Normal file
View file

@ -0,0 +1,127 @@
name: docker
on:
push:
branches:
- master
tags:
- '**'
# TODO: Remove this before merging PR
pull_request:
branches:
- master
# This is needed to push to GitHub Container Registry. See https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
# This step generates the docker tags
# TODO: Remove type=ref,event=pr before merging PR
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
env:
# This env var ensures {{sha}} is a real commit SHA for type=ref,event=pr
DOCKER_METADATA_PR_HEAD_SHA: 'true'
with:
images: |
${{ github.repository }}
ghcr.io/${{ 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
# The rest of the org.opencontainers.image.xxx labels are dynamically generated
labels: |
org.opencontainers.image.description=SnappyMail
org.opencontainers.image.licenses=AGPLv3
# 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: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# See: https://github.com/docker/buildx/issues/59
- name: Build
id: build
uses: docker/build-push-action@v3
with:
context: '.'
file: ./.docker/release/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
- name: Docker images
run: |
docker images
- name: Test
run: |
TAG=$( echo "${{ steps.meta.outputs.tags }}" | head -n1 )
.docker/release/test/test.sh "$TAG"
- 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 }}
labels: ${{ steps.meta.outputs.labels }}
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

View file

@ -5,6 +5,10 @@
<br>
<h1>SnappyMail</h1>
<br>
[![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)
<p>
Simple, modern, lightweight &amp; fast web-based email client.
</p>

123
examples/README.md Normal file
View file

@ -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.

View file

@ -0,0 +1,57 @@
version: '2'
services:
snappymail:
image: leojonathanoh/snappymail:pr-1
ports:
- 8888:8888
environment:
- DEBUG=true
volumes:
- snappymail:/var/lib/snappymail
networks:
- default
- db-network
- redis-network
restart: unless-stopped
# This provides prometheus metrics for snappymail's php-fpm
php-fpm-exporter:
image: hipages/php-fpm_exporter:2.2.0
ports:
- 9253:9253
environment:
- PHP_FPM_SCRAPE_URI=tcp://snappymail:9000/status
networks:
- default
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

View file

@ -0,0 +1,56 @@
version: '2'
services:
snappymail:
image: leojonathanoh/snappymail:pr-1
ports:
- 8888:8888
environment:
- DEBUG=true
volumes:
- snappymail:/var/lib/snappymail
networks:
- default
- db-network
- redis-network
restart: unless-stopped
# This provides prometheus metrics for snappymail's php-fpm
php-fpm-exporter:
image: hipages/php-fpm_exporter:2.2.0
ports:
- 9253:9253
environment:
- PHP_FPM_SCRAPE_URI=tcp://snappymail:9000/status
networks:
- default
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

View file

@ -0,0 +1,30 @@
version: '2'
services:
snappymail:
image: leojonathanoh/snappymail:pr-1
ports:
- 8888:8888
environment:
- DEBUG=true
volumes:
- snappymail:/var/lib/snappymail
networks:
- default
restart: unless-stopped
# This provides prometheus metrics for snappymail's php-fpm
php-fpm-exporter:
image: hipages/php-fpm_exporter:2.2.0
ports:
- 9253:9253
environment:
- PHP_FPM_SCRAPE_URI=tcp://snappymail:9000/status
networks:
- default
networks:
default:
volumes:
snappymail:
driver: local

View file

@ -0,0 +1,86 @@
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:pr-1
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
environment:
- DEBUG=true
volumes:
- snappymail:/var/lib/snappymail
networks:
- default
- traefik-network
restart: unless-stopped
# This provides prometheus metrics for snappymail's php-fpm
php-fpm-exporter:
image: hipages/php-fpm_exporter:2.2.0
ports:
- 9253:9253
environment:
- PHP_FPM_SCRAPE_URI=tcp://snappymail:9000/status
networks:
- default
networks:
default:
traefik-network:
name: traefik-network
volumes:
acme-volume:
driver: local
snappymail:
driver: local

4493
yarn.lock Normal file

File diff suppressed because it is too large Load diff