Sign inSign up

tlan16/vaultwarden-i386

By tlan16

Updated 6 days ago

Image
0

309

tlan16/vaultwarden-i386 repository overview

Overview

This Docker image is for Vaultwarden, specifically to be run on old intel 32bit CPU.

It essentially pulls latest Vaultwarden applied minimal changes to support i386, build and publish docker image.

CPU architecture other than i386 is likely already supported by the official Vaultwarden, if so, use theirs.

Generated Dockerfile

# syntax=docker/dockerfile:1
# check=skip=FromPlatformFlagConstDisallowed,RedundantTargetPlatform

# This file was generated using a Jinja2 template.
# Please make your changes in `DockerSettings.yaml` or `Dockerfile.j2` and then `make`
# This will generate two Dockerfile's `Dockerfile.debian` and `Dockerfile.alpine`

# Using multistage build:
# 	https://docs.docker.com/develop/develop-images/multistage-build/
# 	https://whitfin.io/speeding-up-rust-docker-builds/

####################### VAULT BUILD IMAGE #######################
# The web-vault digest specifies a particular web-vault build on Docker Hub.
# Using the digest instead of the tag name provides better security,
# as the digest of an image is immutable, whereas a tag name can later
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
#   click the tag name to view the digest of the image it currently points to.
# - From the command line:
#     $ docker pull docker.io/vaultwarden/web-vault:v2026.7.0
#     $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2026.7.0
#     [docker.io/vaultwarden/web-vault@sha256:ba8bab66d4330ab9dbafa8f245bcbe99cf6ee3f2c8ce9b5fbb10e9c49658451c]
#
# - Conversely, to get the tag name from the digest:
#     $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:ba8bab66d4330ab9dbafa8f245bcbe99cf6ee3f2c8ce9b5fbb10e9c49658451c
#     [docker.io/vaultwarden/web-vault:v2026.7.0]
#
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:ba8bab66d4330ab9dbafa8f245bcbe99cf6ee3f2c8ce9b5fbb10e9c49658451c AS vault

########################## Cross Compile Docker Helper Scripts ##########################
## We use the linux/amd64 no matter which Build Platform, since these are all bash scripts
## And these bash scripts do not have any significant difference if at all
FROM --platform=linux/amd64 docker.io/tonistiigi/xx@sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707 AS xx

########################## BUILD IMAGE ##########################
# hadolint ignore=DL3006
FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.98.1-slim-trixie AS build
# hadolint ignore=DL3067
COPY --from=xx / /
ARG TARGETARCH
ARG TARGETVARIANT
ARG TARGETPLATFORM

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Build time options to avoid dpkg warnings and help with reproducible builds.
ENV DEBIAN_FRONTEND=noninteractive \
    LANG=C.UTF-8 \
    TZ=UTC \
    TERM=xterm-256color \
    CARGO_HOME="/root/.cargo" \
    USER="root"
# Install clang && xx-c-essentials to get `xx-cargo` working
# Install pkg-config to allow amd64 builds to find all libraries
# Install git so build.rs can determine the correct version
# Install the libc cross packages based upon the debian-arch
RUN apt-get update && \
    apt-get install -y \
        --no-install-recommends \
        clang \
        git && \
    xx-apt-get install -y \
        --no-install-recommends \
        libpq-dev \
        libpq5 \
        libssl-dev \
        libmariadb-dev \
        pkg-config \
        zlib1g-dev \
        libzstd-dev \
        xx-c-essentials && \
    # Run xx-cargo early, since it sometimes seems to break when run at a later stage
    echo "export CARGO_TARGET=$(xx-cargo --print-target-triple)" >> /env-cargo

RUN echo 'export RUSTFLAGS="-C link-arg=-Wl,-rpath-link,/usr/lib/i386-linux-gnu -C link-arg=-Wl,-rpath-link,/lib/i386-linux-gnu"' >> /env-cargo


# Create CARGO_HOME folder and don't download rust docs
RUN mkdir -pv "${CARGO_HOME}" && \
    rustup set profile minimal

# Creates a dummy project used to grab dependencies
RUN USER=root cargo new --bin /app
WORKDIR /app

RUN . /env-cargo && \
    rustup target add "${CARGO_TARGET}"

# Copies over *only* your manifests and build files
COPY ./Cargo.* ./rust-toolchain.toml ./build.rs ./
COPY ./macros ./macros

ARG CARGO_PROFILE=release

# Configure the DB ARG as late as possible to not invalidate the cached layers above
ARG DB=sqlite,mysql,postgresql

# Builds your dependencies and removes the
# dummy project, except the target folder
# This folder contains the compiled dependencies
RUN . /env-cargo && \
    # Configure xx-cargo for target pkg-config and Debian transitive library lookup
    # https://github.com/tonistiigi/xx/pull/108#issuecomment-3700635977
    # https://github.com/dani-garcia/vaultwarden/discussions/7522
    if xx-info is-cross; then \
        XX_RUSTFLAGS="-C link-arg=-Wl,-rpath-link,/usr/lib/$(xx-info triple)"; \
        export XX_RUSTFLAGS; \
    fi && \
    PKG_CONFIG="$(command -v "$(xx-info)-pkg-config")" xx-cargo build --features ${DB} --profile "${CARGO_PROFILE}" && \
    find . -not -path "./target*" -delete

# Copies the complete project
# To avoid copying unneeded files, use .dockerignore
COPY . .

ARG VW_VERSION

# Builds again, this time it will be the actual source files being build
RUN . /env-cargo && \
    # Make sure that we actually build the project by updating the src/main.rs timestamp
    # Also do this for build.rs to ensure the version is rechecked
    touch build.rs src/main.rs && \
    # Create a symlink to the binary target folder to easy copy the binary in the final stage
    # Configure xx-cargo for target pkg-config and Debian transitive library lookup
    # https://github.com/tonistiigi/xx/pull/108#issuecomment-3700635977
    # https://github.com/dani-garcia/vaultwarden/discussions/7522
    if xx-info is-cross; then \
        XX_RUSTFLAGS="-C link-arg=-Wl,-rpath-link,/usr/lib/$(xx-info triple)"; \
        export XX_RUSTFLAGS; \
    fi && \
    PKG_CONFIG="$(command -v "$(xx-info)-pkg-config")" xx-cargo build --features ${DB} --profile "${CARGO_PROFILE}" && \
    if [ "${CARGO_PROFILE}" = "dev" ] ; then \
        ln -vfsr "/app/target/${CARGO_TARGET}/debug" /app/target/final ; \
    else \
        ln -vfsr "/app/target/${CARGO_TARGET}/${CARGO_PROFILE}" /app/target/final ; \
    fi


######################## RUNTIME IMAGE  ########################
# Create a new stage with a minimal image
# because we already have a binary built
#
# To build these images you need to have qemu binfmt support.
# See the following pages to help install these tools locally
# Ubuntu/Debian: https://wiki.debian.org/QemuUserEmulation
# Arch Linux: https://wiki.archlinux.org/title/QEMU#Chrooting_into_arm/arm64_environment_from_x86_64
#
# Or use a Docker image which modifies your host system to support this.
# The GitHub Actions Workflow uses the same image as used below.
# See: https://github.com/tonistiigi/binfmt
# Usage: docker run --privileged --rm tonistiigi/binfmt --install arm64,arm
# To uninstall: docker run --privileged --rm tonistiigi/binfmt --uninstall 'qemu-*'
#
# We need to add `--platform` here, because of a podman bug: https://github.com/containers/buildah/issues/4742
# hadolint ignore=DL3065
FROM --platform=$TARGETPLATFORM docker.io/library/debian:trixie-slim

ENV ROCKET_PROFILE="release" \
    ROCKET_ADDRESS=0.0.0.0 \
    ROCKET_PORT=80 \
    DEBIAN_FRONTEND=noninteractive

# Create data folder and Install needed libraries
RUN mkdir /data && \
    apt-get update && apt-get install -y \
        --no-install-recommends \
        ca-certificates \
        curl \
        libmariadb3 \
        libpq5 \
        openssl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

VOLUME /data
EXPOSE 80

# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /

COPY docker/healthcheck.sh docker/start.sh /

COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/final/vaultwarden .

HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]

CMD ["/start.sh"]

Build Script (build-docker-image.sh)

#!/usr/bin/env bash
#
# build-docker-image.sh
# ---------------------------------------------------------------------------
# Builds a Vaultwarden Docker image for 32-bit x86 (i686 / linux/386), which
# upstream does NOT publish, and saves it to a portable tarball you can copy
# to the production server and `docker load` there.
#
# It generates a PATCHED copy of vaultwarden's docker/Dockerfile.debian to
# work around the i386 cross-link failure on Debian trixie, where OpenSSL's
# libcrypto.so pulls in zlib + zstd but the cross-linker can't find them:
#   1. adds `libzstd-dev` to the cross (xx-apt-get) install list
#   2. sets RUSTFLAGS with -rpath-link to /usr/lib/i386-linux-gnu and
#      /lib/i386-linux-gnu so the linker resolves libcrypto's transitive deps
# The vaultwarden source is cloned fresh on each build from the latest release.
#
# Usage:
#   ./build-docker-image.sh                # build + save with defaults
#   ./build-docker-image.sh --publish      # build + publish to Docker Hub
#   DB="sqlite" ./build-docker-image.sh    # smaller image, sqlite only
#
# After it finishes you'll get:  ./vaultwarden-i386.tar.gz
# ---------------------------------------------------------------------------
set -euo pipefail

# ---- Parse arguments -------------------------------------------------------
PUBLISH=false
for arg in "$@"; do
  case "${arg}" in
    --publish) PUBLISH=true ;;
  esac
done

# ---- Configuration (override via environment variables) -------------------
PLATFORM="${PLATFORM:-linux/386}"                       # GOARCH=386 == i386/i686
IMAGE_TAG="${IMAGE_TAG:-vaultwarden/server:i386}"       # must match docker-compose.yml
PUBLISH_IMAGE="${PUBLISH_IMAGE:-tlan16/vaultwarden-i386}"  # Docker Hub image name for --publish
DB="${DB:-sqlite}"                                      # cargo features to build
CARGO_PROFILE="${CARGO_PROFILE:-release}"               # release | dev
OUTPUT_FILE="${OUTPUT_FILE:-vaultwarden-i386.tar.gz}"   # portable tarball
MTRIPLE="${MTRIPLE:-i386}"                              # Debian multiarch prefix for 386

# ---- Resolve paths --------------------------------------------------------
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VW_DIR="${REPO_ROOT}/vaultwarden"
SRC_DOCKERFILE="${VW_DIR}/docker/Dockerfile.debian"
GEN_DOCKERFILE="${REPO_ROOT}/Dockerfile.i386.generated"

echo "==> Repo root:   ${REPO_ROOT}"
echo "==> Source:      ${VW_DIR}"
echo "==> Platform:    ${PLATFORM}"
echo "==> Image tag:   ${IMAGE_TAG}"
echo "==> DB features: ${DB}"
echo "==> Profile:     ${CARGO_PROFILE}"
echo "==> Output:      ${REPO_ROOT}/${OUTPUT_FILE}"
echo

# ---- Sanity checks --------------------------------------------------------
command -v docker >/dev/null 2>&1 || { echo "ERROR: docker not found in PATH." >&2; exit 1; }
docker buildx version >/dev/null 2>&1 || { echo "ERROR: 'docker buildx' is required." >&2; exit 1; }

# ---- Get latest vaultwarden release version --------------------------------
echo "==> Fetching latest vaultwarden release from GitHub..."
LATEST_RELEASE="$(curl -s https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest | jq -r '.tag_name')"
if [[ -z "${LATEST_RELEASE}" ]] || [[ "${LATEST_RELEASE}" == "null" ]]; then
  echo "ERROR: Failed to fetch latest release from GitHub API" >&2
  exit 1
fi
echo "==> Latest vaultwarden release: ${LATEST_RELEASE}"

# ---- Check if this version is already published ----------------------------
if [[ "${PUBLISH}" == "true" ]]; then
  echo "==> Checking if ${PUBLISH_IMAGE}:${LATEST_RELEASE} already exists on Docker Hub..."
  EXISTING_TAGS="$(curl -s "https://hub.docker.com/v2/repositories/${PUBLISH_IMAGE}/tags/" 2>/dev/null | jq -r '.results[].name' 2>/dev/null || true)"
  if echo "${EXISTING_TAGS}" | grep -qFx "${LATEST_RELEASE}"; then
    echo
    echo "==> ${PUBLISH_IMAGE}:${LATEST_RELEASE} already published. Skipping build."
    echo "    Existing tags: $(echo "${EXISTING_TAGS}" | tr '\n' ' ')"
    exit 0
  fi
  echo "==> Version not yet published, proceeding with build."
fi

# ---- Clone latest vaultwarden release -------------------------------------
echo "==> Cloning vaultwarden ${LATEST_RELEASE}..."
rm -rf "${VW_DIR}"
git clone --depth 1 --branch "${LATEST_RELEASE}" https://github.com/dani-garcia/vaultwarden.git "${VW_DIR}"
VW_VERSION="${LATEST_RELEASE}"

# ---- Generate the patched Dockerfile --------------------------------------
echo "==> Generating patched Dockerfile for ${MTRIPLE} (${GEN_DOCKERFILE})..."
awk -v MTRIPLE="${MTRIPLE}" '
{
    print
    # Patch 1: install the 32-bit zstd dev package alongside zlib
    if ($0 ~ /zlib1g-dev[[:space:]]*\\[[:space:]]*$/) {
        match($0, /^[[:space:]]*/)
        indent = substr($0, 1, RLENGTH)
        print indent "libzstd-dev \\"
    }
    # Patch 2: after CARGO_TARGET is written to /env-cargo, add RUSTFLAGS
    # so the cross-linker can find libcrypto.so'\''s transitive zlib/zstd deps
    if ($0 ~ /export CARGO_TARGET=.*print-target-triple/) {
        printf "\nRUN echo %cexport RUSTFLAGS=%c-C link-arg=-Wl,-rpath-link,/usr/lib/%s-linux-gnu -C link-arg=-Wl,-rpath-link,/lib/%s-linux-gnu%c%c >> /env-cargo\n\n", 39, 34, MTRIPLE, MTRIPLE, 34, 39
    }
}
' "${SRC_DOCKERFILE}" > "${GEN_DOCKERFILE}"

# Quick verification the patches landed
grep -q "libzstd-dev" "${GEN_DOCKERFILE}" || { echo "ERROR: libzstd-dev patch failed to apply." >&2; exit 1; }
grep -q "rpath-link" "${GEN_DOCKERFILE}"  || { echo "ERROR: RUSTFLAGS patch failed to apply." >&2; exit 1; }
echo "    Patches applied OK."

# ---- Register QEMU binfmt so the linux/386 runtime stage can run ----------
echo "==> Ensuring QEMU binfmt (386) emulation is registered..."
docker run --privileged --rm tonistiigi/binfmt --install 386 >/dev/null 2>&1 || \
  echo "    (binfmt install skipped/failed - continuing; usually fine on Docker Desktop)"

# ---- Ensure a buildx builder exists ---------------------------------------
BUILDER="vaultwarden-i386-builder"
if ! docker buildx inspect "${BUILDER}" >/dev/null 2>&1; then
  echo "==> Creating buildx builder '${BUILDER}'..."
  docker buildx create --name "${BUILDER}" --driver docker-container >/dev/null
fi
docker buildx use "${BUILDER}"
docker buildx inspect --bootstrap >/dev/null

# ---- Build the image ------------------------------------------------------
# Context is the submodule dir (relative COPY paths resolve there); -f points
# at the generated patched Dockerfile in the repo root.

# Pull latest remote image to use as cache (if available)
echo
echo "==> Pulling ${PUBLISH_IMAGE}:latest for build cache..."
docker pull --platform "${PLATFORM}" "${PUBLISH_IMAGE}:latest" 2>/dev/null || \
  echo "    (No remote image found, building from scratch)"

echo
echo "==> Building ${IMAGE_TAG} for ${PLATFORM} (this can take a while)..."

if [[ "${PUBLISH}" == "true" ]]; then
  # Build and push directly to Docker Hub
  docker buildx build \
    --builder "${BUILDER}" \
    --platform "${PLATFORM}" \
    --file "${GEN_DOCKERFILE}" \
    --build-arg DB="${DB}" \
    --build-arg CARGO_PROFILE="${CARGO_PROFILE}" \
    --build-arg VW_VERSION="${VW_VERSION}" \
    --cache-from "type=registry,ref=${PUBLISH_IMAGE}:latest" \
    --tag "${PUBLISH_IMAGE}:${VW_VERSION}" \
    --tag "${PUBLISH_IMAGE}:latest" \
    --provenance=false \
    --push \
    "${VW_DIR}"

  # ---- Update Docker Hub description ----------------------------------------
  echo
  echo "==> Updating Docker Hub description..."
  SCRIPT_PATH="${REPO_ROOT}/build-docker-image.sh"
  
  # Build description: Dockerfile first, then build script
  DESCRIPTION_FILE=$(mktemp)
  {
    echo "# Overview"
    echo ""
    echo "This Docker image is for Vaultwarden, specifically to be run on old intel 32bit CPU."
    echo ""
    echo "It essentially pulls latest Vaultwarden applied minimal changes to support i386, build and publish docker image."
    echo ""
    echo "CPU architecture other than i386 is likely already supported by the official Vaultwarden, if so, use theirs."
    echo ""
    echo "# Generated Dockerfile"
    echo ""
    echo '```dockerfile'
    cat "${GEN_DOCKERFILE}"
    echo '```'
    echo ""
    echo "---"
    echo ""
    echo "# Build Script (build-docker-image.sh)"
    echo ""
    echo '```bash'
    cat "${SCRIPT_PATH}"
    echo '```'
  } > "${DESCRIPTION_FILE}"
  
  # Push description to Docker Hub using docker hub API
  # Requires DOCKERHUB_USER and DOCKERHUB_TOKEN (from hub.docker.com settings → Security)
  if [[ -n "${DOCKERHUB_USER:-}" ]] && [[ -n "${DOCKERHUB_TOKEN:-}" ]]; then
    # Get JWT token for Docker Hub API
    LOGIN_TOKEN=$(curl -s -X POST \
      -H "Content-Type: application/json" \
      -d "{\"username\": \"${DOCKERHUB_USER}\", \"password\": \"${DOCKERHUB_TOKEN}\"}" \
      "https://hub.docker.com/v2/users/login/" | jq -r '.token')
    
    if [[ -n "${LOGIN_TOKEN}" ]] && [[ "${LOGIN_TOKEN}" != "null" ]]; then
      curl -s -X PATCH \
        -H "Authorization: JWT ${LOGIN_TOKEN}" \
        -H "Content-Type: application/json" \
        -d "$(jq -Rs '{full_description: .}' < "${DESCRIPTION_FILE}")" \
        "https://hub.docker.com/v2/repositories/${PUBLISH_IMAGE}/" >/dev/null && \
        echo "    Description updated on Docker Hub" || \
        echo "    Warning: Failed to update description"
    else
      echo "    Warning: Failed to authenticate with Docker Hub (check DOCKERHUB_USER and DOCKERHUB_TOKEN)"
    fi
  else
    echo "    Skipped: Set DOCKERHUB_USER and DOCKERHUB_TOKEN to enable description updates"
    echo "    Get token from: https://hub.docker.com/settings/security"
  fi
  
  rm -f "${DESCRIPTION_FILE}"

  echo
  echo "==> Done."
  echo "    Built and published: ${PUBLISH_IMAGE}:${VW_VERSION} (${PLATFORM})"
  echo "    Also tagged as:      ${PUBLISH_IMAGE}:latest"
else
  # Build locally and save to tarball
  docker buildx build \
    --builder "${BUILDER}" \
    --platform "${PLATFORM}" \
    --file "${GEN_DOCKERFILE}" \
    --build-arg DB="${DB}" \
    --build-arg CARGO_PROFILE="${CARGO_PROFILE}" \
    --build-arg VW_VERSION="${VW_VERSION}" \
    --cache-from "type=registry,ref=${PUBLISH_IMAGE}:latest" \
    --tag "${IMAGE_TAG}" \
    --provenance=false \
    --load \
    "${VW_DIR}"

  # ---- Save to a portable tarball -------------------------------------------
  echo
  echo "==> Saving image to ${REPO_ROOT}/${OUTPUT_FILE} ..."
  docker save "${IMAGE_TAG}" | gzip > "${REPO_ROOT}/${OUTPUT_FILE}"

  echo
  echo "==> Done."
  echo "    Built:  ${IMAGE_TAG} (${PLATFORM}, ${VW_VERSION})"
  echo "    Saved:  ${REPO_ROOT}/${OUTPUT_FILE}"
  echo
  echo "Next steps on the PRODUCTION (i686) server:"
  echo "  1. scp ${OUTPUT_FILE} user@prod:/opt/docker-vaultwarden/"
  echo "  2. gunzip -c ${OUTPUT_FILE} | docker load"
  echo "  3. docker compose up -d"
fi

rm -rf "${VW_DIR}"

Tag summary

Content type

Image

Digest

sha256:3c28b4931

Size

87 MB

Last updated

6 days ago

docker pull tlan16/vaultwarden-i386