├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── bug_report.md │ ├── config.yml │ └── feature.yml ├── SUPPORT.md ├── dependabot.yml ├── docker-msmtpd.jpg ├── labels.yml └── workflows │ ├── build.yml │ ├── labels.yml │ └── test.yml ├── Dockerfile ├── LICENSE ├── README.md ├── docker-bake.hcl ├── examples ├── compose │ └── compose.yml └── kubernetes │ └── deployment.yaml ├── rootfs └── etc │ └── cont-init.d │ ├── 00-fix-logs.sh │ ├── 01-fix-uidgid.sh │ ├── 02-config.sh │ └── 03-create-service.sh └── test └── compose.yml /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = false 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = true 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /rootfs/** linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @crazy-max 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: crazy-max 2 | custom: https://www.paypal.me/crazyws 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 2 | name: Bug Report 3 | description: Report a bug 4 | labels: 5 | - kind/bug 6 | - status/triage 7 | 8 | body: 9 | - type: checkboxes 10 | attributes: 11 | label: Support guidelines 12 | description: Please read the support guidelines before proceeding. 13 | options: 14 | - label: I've read the [support guidelines](https://github.com/crazy-max/docker-msmtpd/blob/master/.github/SUPPORT.md) 15 | required: true 16 | 17 | - type: checkboxes 18 | attributes: 19 | label: I've found a bug and checked that ... 20 | description: | 21 | Make sure that your request fulfills all of the following requirements. If one requirement cannot be satisfied, explain in detail why. 22 | options: 23 | - label: ... the documentation does not mention anything about my problem 24 | - label: ... there are no open or closed issues that are related to my problem 25 | 26 | - type: textarea 27 | attributes: 28 | label: Description 29 | description: | 30 | Please provide a brief description of the bug in 1-2 sentences. 31 | validations: 32 | required: true 33 | 34 | - type: textarea 35 | attributes: 36 | label: Expected behaviour 37 | description: | 38 | Please describe precisely what you'd expect to happen. 39 | validations: 40 | required: true 41 | 42 | - type: textarea 43 | attributes: 44 | label: Actual behaviour 45 | description: | 46 | Please describe precisely what is actually happening. 47 | validations: 48 | required: true 49 | 50 | - type: textarea 51 | attributes: 52 | label: Steps to reproduce 53 | description: | 54 | Please describe the steps to reproduce the bug. 55 | placeholder: | 56 | 1. ... 57 | 2. ... 58 | 3. ... 59 | validations: 60 | required: true 61 | 62 | - type: textarea 63 | attributes: 64 | label: Docker info 65 | description: | 66 | Output of `docker info` command. 67 | render: text 68 | validations: 69 | required: true 70 | 71 | - type: textarea 72 | attributes: 73 | label: Docker Compose config 74 | description: | 75 | Output of `docker compose config` command. 76 | render: yaml 77 | 78 | - type: textarea 79 | attributes: 80 | label: Logs 81 | description: | 82 | Please provide the container logs (set `LOG_LEVEL=debug` if applicable). 83 | render: text 84 | validations: 85 | required: true 86 | 87 | - type: textarea 88 | attributes: 89 | label: Additional info 90 | description: | 91 | Please provide any additional information that seem useful. 92 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | ### Behaviour 7 | 8 | #### Steps to reproduce this issue 9 | 10 | 1. 11 | 2. 12 | 3. 13 | 14 | #### Expected behaviour 15 | 16 | > Tell me what should happen 17 | 18 | #### Actual behaviour 19 | 20 | > Tell me what happens instead 21 | 22 | ### Configuration 23 | 24 | * Docker version (type `docker --version`) : 25 | * Docker compose version if applicable (type `docker-compose --version`) : 26 | * Platform (Debian 9, Ubuntu 18.04, ...) : 27 | * System info (type `uname -a`) : 28 | * Include all necessary configuration files : `docker-compose.yml`, `.env`, ... 29 | 30 | ### Docker info 31 | 32 | ``` 33 | > Output of command `docker info` 34 | ``` 35 | 36 | ### Logs 37 | 38 | ``` 39 | > Container logs (set LOG_LEVEL to debug if applicable) 40 | ``` 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 2 | blank_issues_enabled: true 3 | contact_links: 4 | - name: Questions and Discussions 5 | url: https://github.com/crazy-max/docker-msmtpd/discussions/new 6 | about: Use Github Discussions to ask questions and/or open discussion topics. 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 2 | name: Feature request 3 | description: Missing functionality? Come tell us about it! 4 | labels: 5 | - kind/enhancement 6 | - status/triage 7 | 8 | body: 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: Description 13 | description: What is the feature you want to see? 14 | validations: 15 | required: true 16 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support [![](https://isitmaintained.com/badge/resolution/crazy-max/docker-msmtpd.svg)](https://isitmaintained.com/project/crazy-max/docker-msmtpd) 2 | 3 | ## Reporting an issue 4 | 5 | Please do a search in [open issues](https://github.com/crazy-max/docker-msmtpd/issues?utf8=%E2%9C%93&q=) to see if the issue or feature request has already been filed. 6 | 7 | If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment. 8 | 9 | :+1: - upvote 10 | 11 | :-1: - downvote 12 | 13 | If you cannot find an existing issue that describes your bug or feature, submit an issue using the guidelines below. 14 | 15 | ## Writing good bug reports and feature requests 16 | 17 | File a single issue per problem and feature request. 18 | 19 | * Do not enumerate multiple bugs or feature requests in the same issue. 20 | * Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes. 21 | 22 | The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix. 23 | 24 | You are now ready to [create a new issue](https://github.com/crazy-max/docker-msmtpd/issues/new/choose)! 25 | 26 | ## Closure policy 27 | 28 | * Support directly related to msmtpd will not be provided if your problem is not related to the operation of this image. 29 | * Issues that don't have the information requested above (when applicable) will be closed immediately and the poster directed to the support guidelines. 30 | * Issues that go a week without a response from original poster are subject to closure at my discretion. 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "08:00" 8 | timezone: "Europe/Paris" 9 | labels: 10 | - "kind/dependencies" 11 | - "bot" 12 | -------------------------------------------------------------------------------- /.github/docker-msmtpd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazy-max/docker-msmtpd/e1b38c5e7cc4ed59fb1118c00ef6adcb08bd7173/.github/docker-msmtpd.jpg -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | ## more info https://github.com/crazy-max/ghaction-github-labeler 2 | - 3 | name: "bot" 4 | color: "69cde9" 5 | description: "" 6 | - 7 | name: "good first issue" 8 | color: "7057ff" 9 | description: "" 10 | - 11 | name: "help wanted" 12 | color: "4caf50" 13 | description: "" 14 | - 15 | name: "area/ci" 16 | color: "ed9ca9" 17 | description: "" 18 | - 19 | name: "area/dockerfile" 20 | color: "03a9f4" 21 | description: "" 22 | - 23 | name: "kind/bug" 24 | color: "b60205" 25 | description: "" 26 | - 27 | name: "kind/dependencies" 28 | color: "0366d6" 29 | description: "" 30 | - 31 | name: "kind/docs" 32 | color: "c5def5" 33 | description: "" 34 | - 35 | name: "kind/duplicate" 36 | color: "cccccc" 37 | description: "" 38 | - 39 | name: "kind/enhancement" 40 | color: "0054ca" 41 | description: "" 42 | - 43 | name: "kind/invalid" 44 | color: "e6e6e6" 45 | description: "" 46 | - 47 | name: "kind/upstream" 48 | color: "fbca04" 49 | description: "" 50 | - 51 | name: "kind/wontfix" 52 | color: "ffffff" 53 | description: "" 54 | - 55 | name: "status/automerge" 56 | color: "8f4fbc" 57 | description: "" 58 | - 59 | name: "status/needs-investigation" 60 | color: "e6625b" 61 | description: "" 62 | - 63 | name: "status/needs-more-info" 64 | color: "795548" 65 | description: "" 66 | - 67 | name: "status/stale" 68 | color: "237da0" 69 | description: "" 70 | - 71 | name: "status/triage" 72 | color: "dde4b7" 73 | description: "" 74 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'master' 15 | tags: 16 | - '*' 17 | paths-ignore: 18 | - '**.md' 19 | pull_request: 20 | paths-ignore: 21 | - '**.md' 22 | 23 | env: 24 | DOCKERHUB_SLUG: crazymax/msmtpd 25 | GHCR_SLUG: ghcr.io/crazy-max/msmtpd 26 | 27 | jobs: 28 | build: 29 | runs-on: ubuntu-latest 30 | permissions: 31 | # same as global permissions 32 | contents: read 33 | # required to push to GHCR 34 | packages: write 35 | steps: 36 | - 37 | name: Docker meta 38 | id: meta 39 | uses: docker/metadata-action@v5 40 | with: 41 | images: | 42 | ${{ env.DOCKERHUB_SLUG }} 43 | ${{ env.GHCR_SLUG }} 44 | tags: | 45 | type=match,pattern=(.*)-r,group=1 46 | type=ref,event=pr 47 | type=edge 48 | labels: | 49 | org.opencontainers.image.title=msmtpd 50 | org.opencontainers.image.description=Lightweight SMTP relay using msmtpd 51 | org.opencontainers.image.vendor=CrazyMax 52 | - 53 | name: Set up QEMU 54 | uses: docker/setup-qemu-action@v3 55 | - 56 | name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@v3 58 | - 59 | name: Login to DockerHub 60 | if: github.event_name != 'pull_request' 61 | uses: docker/login-action@v3 62 | with: 63 | username: ${{ secrets.DOCKER_USERNAME }} 64 | password: ${{ secrets.DOCKER_PASSWORD }} 65 | - 66 | name: Login to GHCR 67 | if: github.event_name != 'pull_request' 68 | uses: docker/login-action@v3 69 | with: 70 | registry: ghcr.io 71 | username: ${{ github.repository_owner }} 72 | password: ${{ secrets.GITHUB_TOKEN }} 73 | - 74 | name: Build 75 | uses: docker/bake-action@v6 76 | with: 77 | files: | 78 | ./docker-bake.hcl 79 | cwd://${{ steps.meta.outputs.bake-file }} 80 | targets: image-all 81 | push: ${{ github.event_name != 'pull_request' }} 82 | - 83 | name: Check manifest 84 | if: github.event_name != 'pull_request' 85 | run: | 86 | docker buildx imagetools inspect ${{ env.DOCKERHUB_SLUG }}:${{ steps.meta.outputs.version }} 87 | docker buildx imagetools inspect ${{ env.GHCR_SLUG }}:${{ steps.meta.outputs.version }} 88 | - 89 | name: Inspect image 90 | if: github.event_name != 'pull_request' 91 | run: | 92 | docker pull ${{ env.DOCKERHUB_SLUG }}:${{ steps.meta.outputs.version }} 93 | docker image inspect ${{ env.DOCKERHUB_SLUG }}:${{ steps.meta.outputs.version }} 94 | docker pull ${{ env.GHCR_SLUG }}:${{ steps.meta.outputs.version }} 95 | docker image inspect ${{ env.GHCR_SLUG }}:${{ steps.meta.outputs.version }} 96 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | name: labels 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'master' 15 | paths: 16 | - '.github/labels.yml' 17 | - '.github/workflows/labels.yml' 18 | pull_request: 19 | paths: 20 | - '.github/labels.yml' 21 | - '.github/workflows/labels.yml' 22 | 23 | jobs: 24 | labeler: 25 | runs-on: ubuntu-latest 26 | permissions: 27 | # same as global permissions 28 | contents: read 29 | # required to update labels 30 | issues: write 31 | steps: 32 | - 33 | name: Checkout 34 | uses: actions/checkout@v4 35 | - 36 | name: Run Labeler 37 | uses: crazy-max/ghaction-github-labeler@v5 38 | with: 39 | dry-run: ${{ github.event_name == 'pull_request' }} 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'master' 15 | paths-ignore: 16 | - '**.md' 17 | pull_request: 18 | paths-ignore: 19 | - '**.md' 20 | 21 | env: 22 | BUILD_TAG: msmtpd:test 23 | CONTAINER_NAME: msmtpd 24 | 25 | jobs: 26 | test: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - 30 | name: Checkout 31 | uses: actions/checkout@v4 32 | - 33 | name: Set up QEMU 34 | uses: docker/setup-qemu-action@v3 35 | - 36 | name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v3 38 | - 39 | name: Build 40 | uses: docker/bake-action@v6 41 | with: 42 | source: . 43 | targets: image-local 44 | env: 45 | DEFAULT_TAG: ${{ env.BUILD_TAG }} 46 | - 47 | name: Start 48 | run: | 49 | docker compose up -d 50 | working-directory: test 51 | env: 52 | MSMTPD_IMAGE: ${{ env.BUILD_TAG }} 53 | MSMTPD_CONTAINER: ${{ env.CONTAINER_NAME }} 54 | - 55 | name: Check container logs 56 | uses: crazy-max/.github/.github/actions/container-logs-check@main 57 | with: 58 | container_name: ${{ env.CONTAINER_NAME }} 59 | log_check: "[services.d] done" 60 | timeout: 120 61 | - 62 | name: Logs 63 | if: always() 64 | run: | 65 | docker compose logs 66 | working-directory: test 67 | env: 68 | MSMTPD_IMAGE: ${{ env.BUILD_TAG }} 69 | MSMTPD_CONTAINER: ${{ env.CONTAINER_NAME }} 70 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG MSMTP_VERSION=1.8.28 4 | ARG ALPINE_VERSION=3.21 5 | ARG XX_VERSION=1.6.1 6 | 7 | FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx 8 | FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS base 9 | COPY --from=xx / / 10 | RUN apk --update --no-cache add clang curl file lld make musl-dev pkgconfig tar xz 11 | ARG MSMTP_VERSION 12 | WORKDIR /src 13 | RUN curl -sSL "https://marlam.de/msmtp/releases/msmtp-$MSMTP_VERSION.tar.xz" | tar xJv --strip 1 14 | 15 | FROM base AS builder 16 | ARG TARGETPLATFORM 17 | RUN xx-apk --no-cache --no-scripts add g++ gettext-dev gnutls-dev libidn2-dev 18 | RUN <

2 | 3 |

4 | Latest Version 5 | Build Status 6 | Docker Stars 7 | Docker Pulls 8 |
Become a sponsor 9 | Donate Paypal 10 |

11 | 12 | ## About 13 | 14 | Lightweight SMTP relay using [msmtpd](https://marlam.de/msmtp/) as a Docker 15 | image. 16 | 17 | > [!TIP] 18 | > Want to be notified of new releases? Check out 🔔 [Diun (Docker Image Update Notifier)](https://github.com/crazy-max/diun) 19 | > project! 20 | 21 | ___ 22 | 23 | * [Features](#features) 24 | * [Build locally](#build-locally) 25 | * [Image](#image) 26 | * [Environment variables](#environment-variables) 27 | * [Ports](#ports) 28 | * [Usage](#usage) 29 | * [Docker Compose](#docker-compose) 30 | * [Kubernetes](#kubernetes) 31 | * [Command line](#command-line) 32 | * [Upgrade](#upgrade) 33 | * [Contributing](#contributing) 34 | * [License](#license) 35 | 36 | ## Features 37 | 38 | * Run as non-root user 39 | * Latest [msmtp/msmtpd](https://marlam.de/msmtp/) release compiled from source 40 | * Bind to [unprivileged port](#ports) 41 | * Multi-platform image 42 | 43 | ## Build locally 44 | 45 | ```shell 46 | git clone https://github.com/crazy-max/docker-msmtpd.git 47 | cd docker-msmtpd 48 | 49 | # Build image and output to docker (default) 50 | docker buildx bake 51 | 52 | # Build multi-platform image 53 | docker buildx bake image-all 54 | ``` 55 | 56 | ## Image 57 | 58 | | Registry | Image | 59 | |--------------------------------------------------------------------------------------------------|---------------------------------| 60 | | [Docker Hub](https://hub.docker.com/r/crazymax/msmtpd/) | `crazymax/msmtpd` | 61 | | [GitHub Container Registry](https://github.com/users/crazy-max/packages/container/package/msmtpd) | `ghcr.io/crazy-max/msmtpd` | 62 | 63 | Following platforms for this image are available: 64 | 65 | ``` 66 | $ docker buildx imagetools inspect crazymax/msmtpd --format "{{json .Manifest}}" | \ 67 | jq -r '.manifests[] | select(.platform.os != null and .platform.os != "unknown") | .platform | "\(.os)/\(.architecture)\(if .variant then "/" + .variant else "" end)"' 68 | 69 | linux/386 70 | linux/amd64 71 | linux/arm/v6 72 | linux/arm/v7 73 | linux/arm64 74 | linux/ppc64le 75 | linux/s390x 76 | ``` 77 | 78 | ## Environment variables 79 | 80 | * `TZ`: Timezone assigned to the container (default `UTC`) 81 | * `PUID`: Daemon user id (default `1500`) 82 | * `PGID`: Daemon group id (default `1500`) 83 | * `SMTP_HOST`: SMTP relay server to send the mail to. **required** 84 | * `SMTP_PORT`: Port that the SMTP relay server listens on. Default `25` or `465` if TLS. 85 | * `SMTP_TLS`: Enable or disable TLS (also known as SSL) for secured connections (`on` or `off`). 86 | * `SMTP_STARTTLS`: Start TLS from within the session (`on`, default), or tunnel the session through TLS (`off`). 87 | * `SMTP_TLS_CHECKCERT`: Enable or disable checks of the server certificate (`on` or `off`). They are enabled by default. 88 | * `SMTP_AUTH`: Enable or disable authentication and optionally [choose a method](https://marlam.de/msmtp/msmtp.html#Authentication-commands) to use. The argument `on` chooses a method automatically. 89 | * `SMTP_USER`: Set the username for authentication. Authentication must be activated with the `SMTP_AUTH` env var. 90 | * `SMTP_PASSWORD`: Set the password for authentication. Authentication must be activated with the `SMTP_AUTH` env var. 91 | * `SMTP_DOMAIN`: Argument of the `SMTP EHLO` command (default `localhost`) 92 | * `SMTP_FROM`: Set the envelope-from address. Supported substitution patterns can be found [here](https://marlam.de/msmtp/msmtp.html#Commands-specific-to-sendmail-mode). 93 | * `SMTP_ALLOW_FROM_OVERRIDE`: Allow configured envelope-from address to be overriden by actual SMTP MAIL FROM . Can be [`on` or `off`](https://marlam.de/msmtp/msmtp.html#Commands-specific-to-sendmail-mode) (default `on`) 94 | * `SMTP_SET_FROM_HEADER`: When to set a From header. Can be [`auto`, `on` or `off`](https://marlam.de/msmtp/msmtp.html#Commands-specific-to-sendmail-mode) (default `auto`) 95 | * `SMTP_SET_DATE_HEADER`: When to set a Date header. Can be [`auto` or `off`](https://marlam.de/msmtp/msmtp.html#Commands-specific-to-sendmail-mode) (default `auto`) 96 | * `SMTP_REMOVE_BCC_HEADERS`: Controls whether to remove Bcc headers. Can be [`on` or `off`](https://marlam.de/msmtp/msmtp.html#Commands-specific-to-sendmail-mode) (default `on`) 97 | * `SMTP_UNDISCLOSED_RECIPIENTS`: When set, the original To, Cc, and Bcc headers of the mail are removed and a single new header line `To: undisclosed-recipients:;` is added. Can be [`on` or `off`](https://marlam.de/msmtp/msmtp.html#Commands-specific-to-sendmail-mode) (default `off`) 98 | * `SMTP_DSN_NOTIFY`: Set the condition(s) under which the mail system should send DSN (Delivery Status Notification) messages as comma separated values. Available values are [`off`, `never`, `failure`, `delay` and `success`](https://marlam.de/msmtp/msmtp.html#index-dsn_005fnotify) (default `off`) 99 | * `SMTP_DSN_RETURN`: Controls how much of a mail should be returned in DSN (Delivery Status Notification) messages. Can be [`headers`, `full` or `off`](https://marlam.de/msmtp/msmtp.html#index-dsn_005freturn) (default `off`) 100 | 101 | > [!NOTE] 102 | > `SMTP_USER_FILE` and `SMTP_PASSWORD_FILE` can be used to fill in the value 103 | > from a file, especially for Docker's secrets feature. 104 | 105 | > [!NOTE] 106 | > More info: https://marlam.de/msmtp/msmtp.html 107 | 108 | ## Ports 109 | 110 | * `2500`: SMTP relay port 111 | 112 | ## Usage 113 | 114 | ### Docker Compose 115 | 116 | Docker compose is the recommended way to run this image. You can use the following 117 | [docker compose template](examples/compose/compose.yml), then run the container: 118 | 119 | ```bash 120 | docker compose up -d 121 | docker compose logs -f 122 | ``` 123 | 124 | ### Kubernetes 125 | 126 | To install on a Kubernetes cluster, you can use the following 127 | [kubernetes deployment template](examples/kubernetes/deployment.yaml), then create the deployment: 128 | 129 | ```bash 130 | kubectl apply -f deployment.yaml 131 | ``` 132 | 133 | ### Command line 134 | 135 | You can also use the following minimal command: 136 | 137 | ```bash 138 | $ docker run -d -p 2500:2500 --name msmtpd \ 139 | -e "SMTP_HOST=smtp.example.com" \ 140 | crazymax/msmtpd 141 | ``` 142 | 143 | ## Upgrade 144 | 145 | Recreate the container whenever I push an update: 146 | 147 | ```bash 148 | docker compose pull 149 | docker compose up -d 150 | ``` 151 | 152 | ## Contributing 153 | 154 | Want to contribute? Awesome! The most basic way to show your support is to star 155 | the project, or to raise issues. You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) 156 | or by making a [PayPal donation](https://www.paypal.me/crazyws) to ensure this 157 | journey continues indefinitely! 158 | 159 | Thanks again for your support, it is much appreciated! :pray: 160 | 161 | ## License 162 | 163 | MIT. See `LICENSE` for more details. 164 | -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "DEFAULT_TAG" { 2 | default = "msmtpd:local" 3 | } 4 | 5 | // Special target: https://github.com/docker/metadata-action#bake-definition 6 | target "docker-metadata-action" { 7 | tags = ["${DEFAULT_TAG}"] 8 | } 9 | 10 | // Default target if none specified 11 | group "default" { 12 | targets = ["image-local"] 13 | } 14 | 15 | target "image" { 16 | inherits = ["docker-metadata-action"] 17 | } 18 | 19 | target "image-local" { 20 | inherits = ["image"] 21 | output = ["type=docker"] 22 | } 23 | 24 | target "image-all" { 25 | inherits = ["image"] 26 | platforms = [ 27 | "linux/amd64", 28 | "linux/arm/v6", 29 | "linux/arm/v7", 30 | "linux/arm64", 31 | "linux/386", 32 | "linux/ppc64le", 33 | "linux/s390x" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /examples/compose/compose.yml: -------------------------------------------------------------------------------- 1 | name: msmtpd 2 | 3 | services: 4 | msmtpd: 5 | image: crazymax/msmtpd 6 | container_name: msmtpd 7 | ports: 8 | - target: 2500 9 | published: 2500 10 | protocol: tcp 11 | environment: 12 | - "TZ=Europe/Paris" 13 | - "PUID=1500" 14 | - "PGID=1500" 15 | - "SMTP_HOST=smtp.gmail.com" 16 | - "SMTP_PORT=587" 17 | - "SMTP_TLS=on" 18 | - "SMTP_STARTTLS=on" 19 | - "SMTP_TLS_CHECKCERT=on" 20 | - "SMTP_AUTH=on" 21 | - "SMTP_USER_FILE=/run/secrets/smtp_user" 22 | - "SMTP_PASSWORD_FILE=/run/secrets/smtp_password" 23 | - "SMTP_DOMAIN=localhost" 24 | secrets: 25 | - smtp_user 26 | - smtp_password 27 | restart: always 28 | 29 | secrets: 30 | smtp_user: 31 | external: true 32 | smtp_password: 33 | external: true 34 | -------------------------------------------------------------------------------- /examples/kubernetes/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: msmtpd 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | name: msmtpd-config 11 | namespace: msmtpd 12 | data: 13 | smtp_user: | 14 | foo@gmail.com 15 | smtp_password: | 16 | gmailpassword 17 | --- 18 | apiVersion: apps/v1 19 | kind: StatefulSet 20 | metadata: 21 | name: msmtpd 22 | namespace: msmtpd 23 | labels: 24 | app: msmtpd 25 | spec: 26 | serviceName: msmtpd 27 | replicas: 1 28 | selector: 29 | matchLabels: 30 | app: msmtpd 31 | template: 32 | metadata: 33 | labels: 34 | app: msmtpd 35 | name: msmtpd 36 | spec: 37 | containers: 38 | - name: msmtpd 39 | image: crazymax/msmtpd 40 | imagePullPolicy: IfNotPresent 41 | env: 42 | - name: TZ 43 | value: Europe/Paris 44 | - name: PUID 45 | value: "1500" 46 | - name: PGID 47 | value: "1500" 48 | - name: SMTP_HOST 49 | value: "smtp.gmail.com" 50 | - name: SMTP_PORT 51 | value: "587" 52 | - name: SMTP_TLS 53 | value: "on" 54 | - name: SMTP_STARTTLS 55 | value: "on" 56 | - name: SMTP_TLS_CHECKCERT 57 | value: "on" 58 | - name: SMTP_AUTH 59 | value: "on" 60 | - name: SMTP_USER_FILE 61 | value: "/run/secrets/smtp_user" 62 | - name: SMTP_PASSWORD_FILE 63 | value: "/run/secrets/smtp_password" 64 | - name: SMTP_DOMAIN 65 | value: "localhost" 66 | volumeMounts: 67 | - name: msmtpd-config-user 68 | mountPath: /run/secrets/smtp_user 69 | subPath: smtp_user 70 | - name: msmtpd-config-password 71 | mountPath: /run/secrets/smtp_password 72 | subPath: smtp_password 73 | volumes: 74 | - name: msmtpd-config-user 75 | configMap: 76 | name: msmtpd-config 77 | - name: msmtpd-config-password 78 | configMap: 79 | name: msmtpd-config 80 | --- 81 | apiVersion: v1 82 | kind: Service 83 | metadata: 84 | name: smtp 85 | namespace: msmtpd 86 | spec: 87 | selector: 88 | app: msmtpd 89 | ports: 90 | - port: 25 91 | targetPort: 2500 92 | protocol: TCP 93 | name: smtp 94 | -------------------------------------------------------------------------------- /rootfs/etc/cont-init.d/00-fix-logs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | # shellcheck shell=sh 3 | 4 | # Fix access rights to stdout and stderr 5 | chown ${PUID}:${PGID} /proc/self/fd/1 /proc/self/fd/2 || true 6 | -------------------------------------------------------------------------------- /rootfs/etc/cont-init.d/01-fix-uidgid.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | # shellcheck shell=sh 3 | 4 | if [ -n "${PGID}" ] && [ "${PGID}" != "$(id -g msmtpd)" ]; then 5 | echo "Switching to PGID ${PGID}..." 6 | sed -i -e "s/^msmtpd:\([^:]*\):[0-9]*/msmtpd:\1:${PGID}/" /etc/group 7 | sed -i -e "s/^msmtpd:\([^:]*\):\([0-9]*\):[0-9]*/msmtpd:\1:\2:${PGID}/" /etc/passwd 8 | fi 9 | if [ -n "${PUID}" ] && [ "${PUID}" != "$(id -u msmtpd)" ]; then 10 | echo "Switching to PUID ${PUID}..." 11 | sed -i -e "s/^msmtpd:\([^:]*\):[0-9]*:\([0-9]*\)/msmtpd:\1:${PUID}:\2/" /etc/passwd 12 | fi 13 | -------------------------------------------------------------------------------- /rootfs/etc/cont-init.d/02-config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | TZ=${TZ:-UTC} 5 | 6 | #SMTP_HOST=${SMTP_HOST:-smtp.example.com} 7 | #SMTP_PORT=${SMTP_PORT:-25} 8 | #SMTP_TLS=${SMTP_TLS:-off} 9 | #SMTP_STARTTLS=${SMTP_STARTTLS:-off} 10 | #SMTP_TLS_CHECKCERT=${SMTP_TLS_CHECKCERT:-on} 11 | #SMTP_AUTH=${SMTP_AUTH:-off} 12 | #SMTP_USER=${SMTP_USER:-foo} 13 | #SMTP_PASSWORD=${SMTP_PASSWORD:-bar} 14 | #SMTP_DOMAIN=${SMTP_DOMAIN:-example.com} 15 | #SMTP_FROM=${SMTP_FROM:-foo@example.com} 16 | #SMTP_SET_FROM_HEADER=${SMTP_SET_FROM_HEADER:-auto} 17 | #SMTP_SET_DATE_HEADER=${SMTP_SET_DATE_HEADER:-auto} 18 | #SMTP_REMOVE_BCC_HEADERS=${SMTP_REMOVE_BCC_HEADERS:-on} 19 | #SMTP_UNDISCLOSED_RECIPIENTS=${SMTP_UNDISCLOSED_RECIPIENTS:-off} 20 | #SMTP_DSN_NOTIFY=${SMTP_DSN_NOTIFY:-off} 21 | #SMTP_DSN_RETURN=${SMTP_DSN_RETURN:-off} 22 | 23 | # From https://github.com/docker-library/mariadb/blob/master/docker-entrypoint.sh#L21-L41 24 | # usage: file_env VAR [DEFAULT] 25 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 26 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 27 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 28 | file_env() { 29 | local var="$1" 30 | local fileVar="${var}_FILE" 31 | local def="${2:-}" 32 | if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then 33 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 34 | exit 1 35 | fi 36 | local val="$def" 37 | if [ "${!var:-}" ]; then 38 | val="${!var}" 39 | elif [ "${!fileVar:-}" ]; then 40 | val="$(< "${!fileVar}")" 41 | fi 42 | export "$var"="$val" 43 | unset "$fileVar" 44 | } 45 | 46 | if [ -z "$SMTP_HOST" ]; then 47 | >&2 echo "ERROR: SMTP_HOST must be defined" 48 | exit 1 49 | fi 50 | 51 | echo "Setting timezone to ${TZ}..." 52 | ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime 53 | echo ${TZ} > /etc/timezone 54 | 55 | echo "Creating configuration..." 56 | cat > /etc/msmtprc <> /etc/msmtprc; fi 66 | if [ -n "$SMTP_TLS" ]; then echo "tls $SMTP_TLS" >> /etc/msmtprc; fi 67 | if [ -n "$SMTP_STARTTLS" ]; then echo "tls_starttls $SMTP_STARTTLS" >> /etc/msmtprc; fi 68 | if [ -n "$SMTP_TLS_CHECKCERT" ]; then echo "tls_certcheck $SMTP_TLS_CHECKCERT" >> /etc/msmtprc; fi 69 | if [ -n "$SMTP_AUTH" ]; then echo "auth $SMTP_AUTH" >> /etc/msmtprc; fi 70 | if [ -n "$SMTP_USER" ]; then echo "user $SMTP_USER" >> /etc/msmtprc; fi 71 | if [ -n "$SMTP_PASSWORD" ]; then echo "password $SMTP_PASSWORD" >> /etc/msmtprc; fi 72 | if [ -n "$SMTP_DOMAIN" ]; then echo "domain $SMTP_DOMAIN" >> /etc/msmtprc; fi 73 | if [ -n "$SMTP_FROM" ]; then echo "from $SMTP_FROM" >> /etc/msmtprc; fi 74 | if [ -n "$SMTP_ALLOW_FROM_OVERRIDE" ]; then echo "allow_from_override $SMTP_ALLOW_FROM_OVERRIDE" >> /etc/msmtprc; fi 75 | if [ -n "$SMTP_SET_FROM_HEADER" ]; then echo "set_from_header $SMTP_SET_FROM_HEADER" >> /etc/msmtprc; fi 76 | if [ -n "$SMTP_SET_DATE_HEADER" ]; then echo "set_date_header $SMTP_SET_DATE_HEADER" >> /etc/msmtprc; fi 77 | if [ -n "$SMTP_REMOVE_BCC_HEADERS" ]; then echo "remove_bcc_headers $SMTP_REMOVE_BCC_HEADERS" >> /etc/msmtprc; fi 78 | if [ -n "$SMTP_UNDISCLOSED_RECIPIENTS" ]; then echo "undisclosed_recipients $SMTP_UNDISCLOSED_RECIPIENTS" >> /etc/msmtprc; fi 79 | if [ -n "$SMTP_DSN_NOTIFY" ]; then echo "dsn_notify $SMTP_DSN_NOTIFY" >> /etc/msmtprc; fi 80 | if [ -n "$SMTP_DSN_RETURN" ]; then echo "dsn_return $SMTP_DSN_RETURN" >> /etc/msmtprc; fi 81 | unset SMTP_USER 82 | unset SMTP_PASSWORD 83 | -------------------------------------------------------------------------------- /rootfs/etc/cont-init.d/03-create-service.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | # shellcheck shell=sh 3 | 4 | mkdir -p /etc/services.d/msmtpd 5 | cat > /etc/services.d/msmtpd/run <