diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..37e7be3fb
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,10 @@
+// Ignore everything
+*
+
+// Allow what is needed
+!crates
+!tests
+!resources/docker/entrypoint.sh
+
+!Cargo.lock
+!Cargo.toml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..eb3e88eb2
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,423 @@
+name: "CI"
+
+on:
+  workflow_dispatch:
+    inputs:
+      Docker:
+        required: false
+        default: false
+        type: boolean
+      Release:
+        required: false
+        default: false
+        type: boolean
+
+# TODO Comment out the next 2 lines so that it won't interfere with the old CI when tagging a version
+#      Should fix after deprecate the old CI(build.yml)
+#   push:
+#     tags: ["v*.*.*"]
+
+env:
+  SCCACHE_GHA_ENABLED: true
+  RUSTC_WRAPPER: sccache
+  CARGO_TERM_COLOR: always
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  multiarch:
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - variant: gnu
+          - variant: musl
+    name: Merge image / ${{matrix.variant}}
+    runs-on: ubuntu-latest
+    permissions:
+      id-token: write
+      contents: read
+      attestations: write
+      packages: write
+    needs: [linux]
+    if: github.event_name == 'push' || inputs.Docker
+    steps:
+      - name: Log In to GitHub Container Registry
+        uses: docker/login-action@v3
+        with:
+          registry: ghcr.io
+          username: ${{github.repository_owner}}
+          password: ${{github.token}}
+
+      - name: Log In to DockerHub
+        uses: docker/login-action@v3
+        with:
+          username: ${{github.repository_owner}}
+          password: ${{secrets.DOCKERHUB_TOKEN}}
+
+      - name: Download ${{matrix.variant}} meta bake definition
+        uses: actions/download-artifact@v4
+        with:
+          name: bake-meta-${{matrix.variant}}
+          path: ${{ runner.temp }}/${{matrix.variant}}
+
+      - name: Download ${{matrix.variant}} digests
+        uses: actions/download-artifact@v4
+        with:
+          path: ${{ runner.temp }}/${{matrix.variant}}/digests
+          pattern: digests-${{matrix.variant}}-*
+          merge-multiple: true
+
+      - name: Create ${{matrix.variant}} manifest list and push
+        working-directory: ${{ runner.temp }}/${{matrix.variant}}/digests
+        run: |
+          docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("ghcr.io/${{github.repository}}")) | "-t " + .) | join(" ")' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) \
+            $(printf 'ghcr.io/${{github.repository}}@sha256:%s ' *)
+          docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("index.docker.io/${{github.repository}}")) | "-t " + .) | join(" ")' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) \
+            $(printf 'index.docker.io/${{github.repository}}@sha256:%s ' *)
+
+      - name: Inspect ${{matrix.variant}} image
+        id: manifest-digest
+        run: |
+          docker buildx imagetools inspect --format '{{json .Manifest}}' ghcr.io/${{github.repository}}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) | jq -r '.digest' > GHCR_DIGEST_SHA
+          echo "GHCR_DIGEST_SHA=$(cat GHCR_DIGEST_SHA)" | tee -a "${GITHUB_ENV}"
+          docker buildx imagetools inspect --format '{{json .Manifest}}' index.docker.io/${{github.repository}}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) | jq -r '.digest' > DOCKERHUB_DIGEST_SHA
+          echo "DOCKERHUB_DIGEST_SHA=$(cat DOCKERHUB_DIGEST_SHA)" | tee -a "${GITHUB_ENV}"
+
+      - name: Attest GHCR
+        uses: actions/attest-build-provenance@v2
+        with:
+          subject-name: ghcr.io/${{github.repository}}
+          subject-digest: ${{ env.GHCR_DIGEST_SHA }}
+          push-to-registry: true
+
+      - name: Attest Dockerhub
+        uses: actions/attest-build-provenance@v2
+        with:
+          subject-name: index.docker.io/${{github.repository}}
+          subject-digest: ${{ env.DOCKERHUB_DIGEST_SHA }}
+          push-to-registry: true
+
+  linux:
+    permissions:
+      id-token: write
+      contents: write
+      attestations: write
+      packages: write
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - target: x86_64-unknown-linux-gnu
+            platform: linux/amd64
+            suffix: ''
+          - target: x86_64-unknown-linux-musl
+            platform: linux/amd64
+            suffix: '-alpine'
+          - target: aarch64-unknown-linux-gnu
+            platform: linux/arm64
+            suffix: ''
+            build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
+          - target: aarch64-unknown-linux-musl
+            platform: linux/arm64
+            suffix: '-alpine'
+            build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
+          - target: armv7-unknown-linux-gnueabihf
+            platform: linux/arm/v7
+            suffix: ''
+            build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
+          - target: armv7-unknown-linux-musleabihf
+            platform: linux/arm/v7
+            suffix: '-alpine'
+            build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
+          - target: arm-unknown-linux-gnueabihf
+            platform: linux/arm/v6
+            suffix: ''
+          - target: arm-unknown-linux-musleabihf
+            platform: linux/arm/v6
+            suffix: '-alpine'
+    name: Build / ${{matrix.target}}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+        with:
+          platforms: "arm64,arm"
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+        with:
+          driver-opts: |
+            network=host
+
+      - name: Log In to GitHub Container Registry
+        uses: docker/login-action@v3
+        with:
+          registry: ghcr.io
+          username: ${{github.repository_owner}}
+          password: ${{github.token}}
+
+      - name: Log In to DockerHub
+        uses: docker/login-action@v3
+        with:
+          username: ${{github.repository_owner}}
+          password: ${{secrets.DOCKERHUB_TOKEN}}
+
+      - name: Calculate shasum of external deps
+        id: cal-dep-shasum
+        run: |
+          echo "checksum=$(yq -p toml -oy '.package[] | select((.source | contains("")) or (.checksum | contains("")))' Cargo.lock | sha256sum | awk '{print $1}')" >> "$GITHUB_OUTPUT"
+
+      - name: Cache apt
+        uses: actions/cache@v4
+        id: apt-cache
+        with:
+          path: |
+            var-cache-apt
+            var-lib-apt
+          key: apt-cache-${{ hashFiles('Dockerfile.build') }}
+
+      - name: Cache Cargo
+        uses: actions/cache@v4
+        id: cargo-cache
+        with:
+          path: |
+            usr-local-cargo-registry
+            usr-local-cargo-git
+          key: cargo-cache-${{ steps.cal-dep-shasum.outputs.checksum }}
+
+      - name: Inject cache into docker
+        uses: reproducible-containers/buildkit-cache-dance@v3.1.2
+        with:
+          cache-map: |
+            {
+              "var-cache-apt": "/var/cache/apt",
+              "var-lib-apt": "/var/lib/apt",
+              "usr-local-cargo-registry": "/usr/local/cargo/registry",
+              "usr-local-cargo-git": "/usr/local/cargo/git"
+            }
+          skip-extraction: ${{ steps.cargo-cache.outputs.cache-hit }} && ${{ steps.apt-cache.outputs.cache-hit }}
+
+      - name: Extract Metadata for Docker
+        uses: docker/metadata-action@v5
+        id: meta
+        with:
+          images: |
+            index.docker.io/${{github.repository}}
+            ghcr.io/${{github.repository}}
+          flavor: |
+            suffix=${{matrix.suffix}},onlatest=true
+          tags: |
+            type=ref,event=tag
+            type=ref,event=branch,prefix=branch-
+            type=edge,branch=main
+            type=semver,pattern=v{{major}}.{{minor}}
+
+      - name: Build Artifact
+        id: bake
+        uses: docker/bake-action@v6
+        env:
+          DOCKER_BUILD_RECORD_UPLOAD: false
+          TARGET: ${{matrix.target}}
+          GHCR_REPO: ghcr.io/${{github.repository}}
+          BUILD_ENV: ${{matrix.build_env}}
+          DOCKER_PLATFORM: ${{matrix.platform}}
+          SUFFIX: ${{matrix.suffix}}
+        with:
+          source: .
+          set: |
+            *.tags=
+            image.output=type=image,"name=ghcr.io/${{github.repository}},index.docker.io/${{github.repository}}",push-by-digest=true,name-canonical=true,push=true,compression=zstd,compression-level=9,force-compression=true,oci-mediatypes=true
+          files: |
+            docker-bake.hcl
+            ${{ steps.meta.outputs.bake-file }}
+          targets: ${{(github.event_name == 'push' || inputs.Docker) && 'build,image' || 'build'}}
+
+      - name: Upload Artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: artifact-${{matrix.target}}
+          path: |
+            artifact
+            !artifact/*.json
+
+      - name: Export digest & Rename meta bake definition file
+        if: github.event_name == 'push' || inputs.Docker
+        run: |
+          mv "${{ steps.meta.outputs.bake-file }}" "${{ runner.temp }}/bake-meta.json"
+          mkdir -p ${{ runner.temp }}/digests
+          digest="${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}"
+          touch "${{ runner.temp }}/digests/${digest#sha256:}"
+
+      - name: Upload digest
+        if: github.event_name == 'push' || inputs.Docker
+        uses: actions/upload-artifact@v4
+        with:
+          name: digests-${{matrix.suffix == '' && 'gnu' || 'musl'}}-${{ matrix.target }}
+          path: ${{ runner.temp }}/digests/*
+          if-no-files-found: error
+          retention-days: 1
+
+      - name: Upload GNU meta bake definition
+        uses: actions/upload-artifact@v4
+        if: (github.event_name == 'push' || inputs.Docker) && endsWith(matrix.target,'gnu') && startsWith(matrix.target,'x86')
+        with:
+          name: bake-meta-gnu
+          path: ${{ runner.temp }}/bake-meta.json
+          if-no-files-found: error
+          retention-days: 1
+
+      - name: Upload musl meta bake definition
+        uses: actions/upload-artifact@v4
+        if: (github.event_name == 'push' || inputs.Docker) && endsWith(matrix.target,'musl') && startsWith(matrix.target,'x86')
+        with:
+          name: bake-meta-musl
+          path: ${{ runner.temp }}/bake-meta.json
+          if-no-files-found: error
+          retention-days: 1
+
+  windows:
+    name: Build / ${{matrix.target}}
+    runs-on: windows-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+        #   - target: aarch64-pc-windows-msvc
+          - target: x86_64-pc-windows-msvc
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Run sccache-cache
+        uses: mozilla-actions/sccache-action@v0.0.7
+        with:
+          disable_annotations: true
+
+      - name: Build
+        run: |
+          rustup target add ${{matrix.target}}
+          cargo build --release --target ${{matrix.target}} -p mail-server --no-default-features --features "sqlite postgres mysql rocks elastic s3 redis azure enterprise"
+          cargo build --release --target ${{matrix.target}} -p stalwart-cli
+          mkdir -p artifacts
+          mv ./target/${{matrix.target}}/release/stalwart-mail.exe ./artifacts/stalwart-mail.exe
+          mv ./target/${{matrix.target}}/release/stalwart-cli.exe ./artifacts/stalwart-cli.exe
+
+      - name: Upload Artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: artifact-${{matrix.target}}
+          path: artifacts
+
+  macos:
+    name: Build / ${{matrix.target}}
+    runs-on: macos-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - target: aarch64-apple-darwin
+          - target: x86_64-apple-darwin
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Run sccache-cache
+        uses: mozilla-actions/sccache-action@v0.0.7
+        with:
+          disable_annotations: true
+
+      - name: Build FoundationDB Edition
+        run: |
+          rustup target add ${{matrix.target}}
+          # Get latest FoundationDB installer
+          curl -Lo foundationdb.pkg "https://glare.now.sh/apple/foundationdb/${{startsWith(matrix.target, 'x86') && 'x86_64' || 'arm64'}}.pkg"
+          sudo installer -allowUntrusted -dumplog -pkg foundationdb.pkg -target /
+          cargo build --release --target ${{matrix.target}} -p mail-server --no-default-features --features "foundationdb elastic s3 redis enterprise"
+          mkdir -p artifacts
+          mv ./target/${{matrix.target}}/release/stalwart-mail ./artifacts/stalwart-mail-foundationdb
+
+      - name: Build
+        run: |
+          rustup target add ${{matrix.target}}
+          cargo build --release --target ${{matrix.target}} -p mail-server --no-default-features --features "sqlite postgres mysql rocks elastic s3 redis azure enterprise"
+          cargo build --release --target ${{matrix.target}} -p stalwart-cli
+          mkdir -p artifacts
+          mv ./target/${{matrix.target}}/release/stalwart-mail ./artifacts/stalwart-mail
+          mv ./target/${{matrix.target}}/release/stalwart-cli ./artifacts/stalwart-cli
+
+      - name: Upload Artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: ${{matrix.target}}
+          path: artifacts
+
+  release:
+    name: Release
+    permissions:
+      id-token: write
+      contents: write
+      attestations: write
+    if: github.event_name == 'push' || inputs.Release
+    needs: [linux, windows, macos]
+    runs-on: ubuntu-latest
+    steps:
+      - name: Download Artifacts
+        uses: actions/download-artifact@v4
+        with:
+          path: archive
+          pattern: artifact-*
+
+      - name: Compress
+        run: |
+          set -eux
+          BASE_DIR="$(pwd)/archive"
+          compress_files() {
+              local dir="$1"
+              local archive_dir_name="${dir#artifact-}"
+              cd "$dir"
+              # Process each file in the directory
+              for file in `ls`; do
+                  filename="${file%.*}"
+                  extension="${file##*.}"
+                  if [ "$extension" = "exe" ]; then
+                      7z a -tzip "${filename}-${archive_dir_name}.zip" "$file" > /dev/null
+                  else
+                      tar -czf "${filename}-${archive_dir_name}.tar.gz" "$file"
+                  fi
+              done
+              cd $BASE_DIR
+          }
+          cd $BASE_DIR
+          for arch_dir in `ls`; do
+              dir_name=$(basename "$arch_dir")
+              compress_files "$dir_name"
+          done
+
+      - name: Attest binary
+        id: attest
+        uses: actions/attest-build-provenance@v2
+        with:
+          subject-path: |
+            archive/**/*.tar.gz
+            archive/**/*.zip
+
+      - name: Release
+        uses: softprops/action-gh-release@v2
+        with:
+          files: |
+            archive/**/*.tar.gz
+            archive/**/*.zip
+          prerelease: ${{!startsWith(github.ref, 'refs/tags/') || null}}
+          tag_name: ${{!startsWith(github.ref, 'refs/tags/') && 'nightly' || null}}
+          append_body: true
+          body: |
+            <hr />
+
+            ## Check binary attestation at [here](${{ steps.attest.outputs.attestation-url }})
diff --git a/Dockerfile.build b/Dockerfile.build
new file mode 100644
index 000000000..a5b203ef1
--- /dev/null
+++ b/Dockerfile.build
@@ -0,0 +1,151 @@
+# syntax=docker/dockerfile:1
+# check=skip=FromPlatformFlagConstDisallowed,RedundantTargetPlatform
+
+# *****************
+# Base image for planner & builder
+# *****************
+FROM --platform=$BUILDPLATFORM rust:slim-bookworm AS base
+
+ENV DEBIAN_FRONTEND="noninteractive" \
+    BINSTALL_DISABLE_TELEMETRY=true \
+    CARGO_TERM_COLOR=always \
+    LANG=C.UTF-8 \
+    TZ=UTC \
+    TERM=xterm-256color
+# With zig, we only need libclang and make
+RUN \
+  --mount=type=cache,target=/var/cache/apt,sharing=locked \
+  --mount=type=cache,target=/var/lib/apt,sharing=locked \
+  rm -f /etc/apt/apt.conf.d/docker-clean && \
+  echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache && \
+  apt-get update && \
+  apt-get install -yq --no-install-recommends curl jq xz-utils make libclang-16-dev
+# Install zig
+RUN ZIG_VERSION=$(curl --retry 5 -sL "https://api.github.com/repos/ziglang/zig/releases/latest" | jq -r '.tag_name') && \
+    [ ! -z "$ZIG_VERSION" ] && \
+    curl --retry 5 -Ls "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz" | tar -J -x -C /usr/local && \
+    ln -s "/usr/local/zig-linux-$(uname -m)-${ZIG_VERSION}/zig" /usr/local/bin/zig
+# Install cargo-binstall
+RUN curl --retry 5 -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
+# Install FoundationDB
+# TODO According to https://github.com/apple/foundationdb/issues/11448#issuecomment-2417766293
+# Once FoundationDB v7.3.53 gets released, we should be able to build the aarch64-unknown-linux-gnu target.
+# The last command is for future build use, so if you are building on a native arm64 device, please use docker qemu.
+RUN curl --retry 5 -Lso /usr/lib/libfdb_c.so "$(curl --retry 5 -Ls 'https://api.github.com/repos/apple/foundationdb/releases' | jq --arg arch "$(uname -m)" -r '.[] | select(.prerelease == false) | .assets[] | select(.name | test("libfdb_c." + $arch + ".so")) | .browser_download_url' | head -n1)"
+# Install cargo-chef & sccache & cargo-zigbuild
+RUN cargo binstall --no-confirm cargo-chef sccache cargo-zigbuild
+
+# *****************
+# Planner
+# *****************
+FROM base AS planner
+WORKDIR /app
+COPY . .
+# Generate recipe file
+RUN cargo chef prepare --recipe-path recipe.json
+
+# *****************
+# Builder
+# *****************
+FROM base AS builder
+WORKDIR /app
+COPY --from=planner /app/recipe.json recipe.json
+ARG TARGET
+ARG BUILD_ENV
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+# Install toolchain and specify some env variables
+RUN \
+  rustup set profile minimal && \
+  rustup target add ${TARGET} && \
+  mkdir -p artifact && \
+  touch /env-cargo && \
+  if [ ! -z "${BUILD_ENV}" ]; then \
+      echo "export ${BUILD_ENV}" >> /env-cargo; \
+      echo "Setting up ${BUILD_ENV}"; \
+  fi
+# Cargo-chef Cache layer
+RUN \
+    --mount=type=secret,id=ACTIONS_CACHE_URL,env=ACTIONS_CACHE_URL \
+    --mount=type=secret,id=ACTIONS_RUNTIME_TOKEN,env=ACTIONS_RUNTIME_TOKEN \
+    --mount=type=cache,target=/usr/local/cargo/registry \
+    --mount=type=cache,target=/usr/local/cargo/git \
+    # TODO According to https://github.com/apple/foundationdb/issues/11448#issuecomment-2417766293
+    # Once FoundationDB v7.3.53 gets released, we should be able to build the aarch64-unknown-linux-gnu target.
+    source /env-cargo && \
+    if [ "${TARGET}" = "x86_64-unknown-linux-gnu" ]; then \
+        RUSTFLAGS="-L /usr/lib" cargo chef cook --recipe-path recipe.json --zigbuild --release --target ${TARGET} -p mail-server --no-default-features --features "foundationdb elastic s3 redis enterprise"; \
+    fi
+RUN \
+    --mount=type=secret,id=ACTIONS_CACHE_URL,env=ACTIONS_CACHE_URL \
+    --mount=type=secret,id=ACTIONS_RUNTIME_TOKEN,env=ACTIONS_RUNTIME_TOKEN \
+    --mount=type=cache,target=/usr/local/cargo/registry \
+    --mount=type=cache,target=/usr/local/cargo/git \
+    source /env-cargo && \
+    cargo chef cook --recipe-path recipe.json --zigbuild --release --target ${TARGET} -p mail-server --no-default-features --features "sqlite postgres mysql rocks elastic s3 redis azure enterprise" && \
+    cargo chef cook --recipe-path recipe.json --zigbuild --release --target ${TARGET} -p stalwart-cli
+# Copy the source code
+COPY . .
+ENV RUSTC_WRAPPER="sccache" \
+    SCCACHE_GHA_ENABLED=true
+# Build foundationdb version
+RUN \
+    --mount=type=secret,id=ACTIONS_CACHE_URL,env=ACTIONS_CACHE_URL \
+    --mount=type=secret,id=ACTIONS_RUNTIME_TOKEN,env=ACTIONS_RUNTIME_TOKEN \
+    --mount=type=cache,target=/usr/local/cargo/registry \
+    --mount=type=cache,target=/usr/local/cargo/git \
+    # TODO According to https://github.com/apple/foundationdb/issues/11448#issuecomment-2417766293
+    # Once FoundationDB v7.3.53 gets released, we should be able to build the aarch64-unknown-linux-gnu target.
+    source /env-cargo && \
+    if [ "${TARGET}" = "x86_64-unknown-linux-gnu" ]; then \
+        RUSTFLAGS="-L /usr/lib" cargo zigbuild --release --target ${TARGET} -p mail-server --no-default-features --features "foundationdb elastic s3 redis enterprise"; \
+        mv /app/target/${TARGET}/release/stalwart-mail /app/artifact/stalwart-mail-foundationdb; \
+    fi
+# Build generic version
+RUN \
+    --mount=type=secret,id=ACTIONS_CACHE_URL,env=ACTIONS_CACHE_URL \
+    --mount=type=secret,id=ACTIONS_RUNTIME_TOKEN,env=ACTIONS_RUNTIME_TOKEN \
+    --mount=type=cache,target=/usr/local/cargo/registry \
+    --mount=type=cache,target=/usr/local/cargo/git \
+    source /env-cargo && \
+    cargo zigbuild --release --target ${TARGET} -p mail-server --no-default-features --features "sqlite postgres mysql rocks elastic s3 redis azure enterprise" && \
+    cargo zigbuild --release --target ${TARGET} -p stalwart-cli && \
+    mv /app/target/${TARGET}/release/stalwart-mail /app/artifact/stalwart-mail && \
+    mv /app/target/${TARGET}/release/stalwart-cli /app/artifact/stalwart-cli
+
+# *****************
+# Binary stage
+# *****************
+FROM scratch AS binaries
+COPY --from=builder /app/artifact /
+
+# *****************
+# Runtime image for GNU targets
+# *****************
+FROM --platform=$TARGETPLATFORM docker.io/library/debian:bookworm-slim AS gnu
+WORKDIR /opt/stalwart-mail
+RUN export DEBIAN_FRONTEND=noninteractive && \
+    apt-get update && \
+    apt-get install -yq ca-certificates tzdata
+COPY --from=builder /app/artifact/stalwart-mail /usr/local/bin
+COPY --from=builder /app/artifact/stalwart-cli /usr/local/bin
+COPY ./resources/docker/entrypoint.sh /usr/local/bin/entrypoint.sh
+RUN chmod -R 755 /usr/local/bin
+CMD ["/usr/local/bin/stalwart-mail"]
+VOLUME [ "/opt/stalwart-mail" ]
+EXPOSE	443 25 110 587 465 143 993 995 4190 8080
+ENTRYPOINT ["/bin/sh", "/usr/local/bin/entrypoint.sh"]
+
+# *****************
+# Runtime image for musl targets
+# *****************
+FROM --platform=$TARGETPLATFORM alpine AS musl
+WORKDIR /opt/stalwart-mail
+RUN apk add --update --no-cache ca-certificates tzdata && rm -rf /var/cache/apk/*
+COPY --from=builder /app/artifact/stalwart-mail /usr/local/bin
+COPY --from=builder /app/artifact/stalwart-cli /usr/local/bin
+COPY ./resources/docker/entrypoint.sh /usr/local/bin/entrypoint.sh
+RUN chmod -R 755 /usr/local/bin
+CMD ["/usr/local/bin/stalwart-mail"]
+VOLUME [ "/opt/stalwart-mail" ]
+EXPOSE	443 25 110 587 465 143 993 995 4190 8080
+ENTRYPOINT ["/bin/sh", "/usr/local/bin/entrypoint.sh"]
diff --git a/docker-bake.hcl b/docker-bake.hcl
new file mode 100644
index 000000000..ca4a21287
--- /dev/null
+++ b/docker-bake.hcl
@@ -0,0 +1,50 @@
+variable "TARGET" {
+  default = "$TARGET"
+}
+variable "GHCR_REPO" {
+  default = "$GHCR_REPO"
+}
+variable "BUILD_ENV" {
+  default = "$BUILD_ENV"
+}
+variable "SUFFIX" {
+  default = "$SUFFIX"
+}
+variable "DOCKER_PLATFORM" {
+  default = "$DOCKER_PLATFORM"
+}
+target "docker-metadata-action" {}
+target "build" {
+  secret = [
+    "type=env,id=ACTIONS_CACHE_URL",
+    "type=env,id=ACTIONS_RUNTIME_TOKEN"
+  ]
+  args = {
+    TARGET = "${TARGET}"
+    BUILD_ENV = equal("", "${BUILD_ENV}") ? null : "${BUILD_ENV}"
+  }
+  target = "binaries"
+  cache-from = [
+    "type=registry,ref=${GHCR_REPO}-buildcache:${TARGET}"
+  ]
+  cache-to = [
+    "type=registry,ref=${GHCR_REPO}-buildcache:${TARGET},mode=max,compression=zstd,compression-level=9,force-compression=true,oci-mediatypes=true,image-manifest=false"
+  ]
+  context = "./"
+  dockerfile = "Dockerfile.build"
+  output = ["./artifact"]
+}
+target "image" {
+  inherits = ["build","docker-metadata-action"]
+  cache-to = [""]
+  cache-from = [
+    "type=registry,ref=${GHCR_REPO}-buildcache:${TARGET}"
+  ]
+  target = equal("", "${SUFFIX}") ? "gnu" : "musl"
+  platforms = [
+    "${DOCKER_PLATFORM}"
+  ]
+  output = [
+    ""
+  ]
+}