Dockerfile
Rama main · réplica de lectura del repositorio original.
# SqlPackage, for the Azure SQL Database adapter.
#
# Built in its own stage so the .NET SDK never reaches the runtime image. The tool
# is published as portable IL under tools/<tfm>/any, so this produces the correct
# architecture automatically: buildx runs this stage once per target platform.
# Verified on linux/arm64 as well as linux/amd64 - Microsoft's standalone zip is
# x64-only, which is why the dotnet tool is used instead of the download.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS sqlpackage-build
RUN dotnet tool install --tool-path /tmp/sqlpkg microsoft.sqlpackage && \
PAYLOAD="$(find /tmp/sqlpkg/.store -type d -path '*/tools/net10.0/any' | head -1)" && \
test -n "$PAYLOAD" && \
mkdir -p /opt/sqlpackage && \
cp -a "$PAYLOAD"/. /opt/sqlpackage/
# Base Image: Node.js 24 on Debian Slim (bookworm)
FROM node:24-slim AS base
# Install system tools for database backups
# mariadb-client (Debian 12) -> mysql, mysqldump, mariadb, mariadb-dump (libmariadb3 3.3.x supports caching_sha2_password)
# postgresql-client-18 (PGDG) -> pg_dump, pg_restore, psql (backward compatible with PG 12-18)
# mongodb-database-tools (CDN) -> mongodump, mongorestore (direct download - APT repo has no arm64 builds for Debian 12;
# MongoDB only ships debian12 x86_64 packages; arm64 uses ubuntu2204 package)
# redis-tools -> redis-cli (for Redis backups)
# smbclient -> SMB/CIFS storage
# sqlite3 -> SQLite backups
# gosu -> privilege dropping (replaces Alpine su-exec)
# Step 1: Install prerequisites for adding external APT repositories
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
gnupg \
ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Step 2: Configure external APT repositories
RUN \
# PGDG: PostgreSQL 18 client
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/pgdg.gpg && \
echo "deb [signed-by=/etc/apt/trusted.gpg.d/pgdg.gpg] https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list
# Step 3: Install backup tools
# mariadb-client on Debian 12 ships with libmariadb3 3.3.x, which supports caching_sha2_password natively.
# It also provides mysql/mysqldump/mysqladmin as compatibility symlinks.
ARG TARGETARCH
RUN apt-get update && apt-get install -y --no-install-recommends \
mariadb-client \
postgresql-client-18 \
redis-tools \
smbclient \
lz4 \
zstd \
rsync \
sshpass \
openssh-client \
openssl \
curl \
zip \
sqlite3 \
gosu && \
# Step 4: MongoDB database tools - direct CDN download for multi-arch support
# MongoDB ships debian12 packages for x86_64 only; arm64 uses the ubuntu2204 build (compatible on Debian bookworm)
case "${TARGETARCH:-amd64}" in \
amd64) MONGO_DEB="mongodb-database-tools-debian12-x86_64-100.16.1.deb" ;; \
arm64) MONGO_DEB="mongodb-database-tools-ubuntu2204-arm64-100.16.1.deb" ;; \
*) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;; \
esac && \
curl -fsSL "https://fastdl.mongodb.org/tools/db/${MONGO_DEB}" -o /tmp/mongo-tools.deb && \
apt-get install -y --no-install-recommends /tmp/mongo-tools.deb && \
rm /tmp/mongo-tools.deb && \
rm -rf /var/lib/apt/lists/*
# Step 5: Firebird 5.x client tools (gbak, isql) for the Firebird adapter.
# Debian 12's apt repos only carry a 3.0 client, not 5.x, and the 5.x gbak/isql
# can talk to 3.x/4.x servers over the wire protocol - so download the official
# Firebird release tarball and extract just the client binaries + shared library.
#
# Verified against the actual GitHub release assets (2026-07-04):
# - Release tag (v${FIREBIRD_RELEASE_TAG}) and asset filename version
# (${FIREBIRD_ASSET_VERSION}) differ - assets embed a build number, e.g.
# "Firebird-5.0.3.1683-0-linux-x64.tar.gz" for tag "v5.0.3". Bump both when
# upgrading.
# - The top-level tarball only contains an install.sh + a nested
# buildroot.tar.gz - the actual bin/lib payload lives at
# buildroot.tar.gz:./opt/firebird/{bin,lib}, not at the tarball root.
# - gbak/isql depend on libtommath.so.1 and gbak also needs libz.so.1, neither
# of which ship inside buildroot.tar.gz - installed via apt below.
# libicuuc/libicui18n are dlopen'd at runtime for extended collations only
# (soft dependency) - omitted here since backup/restore doesn't need them.
ARG FIREBIRD_RELEASE_TAG=5.0.3
ARG FIREBIRD_ASSET_VERSION=5.0.3.1683-0
RUN apt-get update && apt-get install -y --no-install-recommends \
libtommath1 \
zlib1g && \
rm -rf /var/lib/apt/lists/* && \
case "${TARGETARCH:-amd64}" in \
amd64) FB_ARCH="x64" ;; \
arm64) FB_ARCH="arm64" ;; \
*) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;; \
esac && \
curl -fsSL "https://github.com/FirebirdSQL/firebird/releases/download/v${FIREBIRD_RELEASE_TAG}/Firebird-${FIREBIRD_ASSET_VERSION}-linux-${FB_ARCH}.tar.gz" -o /tmp/firebird.tar.gz && \
mkdir -p /tmp/firebird-extract && \
tar -xzf /tmp/firebird.tar.gz -C /tmp/firebird-extract --strip-components=1 && \
tar -xzf /tmp/firebird-extract/buildroot.tar.gz -C /tmp/firebird-extract && \
mkdir -p /opt/firebird/bin /opt/firebird/lib && \
cp /tmp/firebird-extract/opt/firebird/bin/gbak /opt/firebird/bin/ && \
cp /tmp/firebird-extract/opt/firebird/bin/isql /opt/firebird/bin/ && \
cp -a /tmp/firebird-extract/opt/firebird/lib/. /opt/firebird/lib/ && \
cp /tmp/firebird-extract/opt/firebird/firebird.msg /opt/firebird/ && \
ln -sf /opt/firebird/bin/gbak /usr/local/bin/gbak && \
ln -sf /opt/firebird/bin/isql /usr/local/bin/isql && \
echo "/opt/firebird/lib" > /etc/ld.so.conf.d/firebird.conf && \
ldconfig && \
rm -rf /tmp/firebird.tar.gz /tmp/firebird-extract
# Step 6: SqlPackage runtime, for the Azure SQL Database adapter.
#
# Only the .NET runtime, never the SDK - the tool itself came from the stage above.
# libicu is a hard dependency: without it .NET aborts at startup with a globalization
# error that says nothing about the missing package.
#
# The wrapper exists because the adapter resolves the binary with host.which("sqlpackage"),
# and the apphost shim from a --tool-path install is not on PATH here.
RUN apt-get update && apt-get install -y --no-install-recommends libicu72 && \
rm -rf /var/lib/apt/lists/* && \
curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh && \
bash /tmp/dotnet-install.sh --channel 10.0 --runtime dotnet --install-dir /usr/share/dotnet --no-path && \
rm /tmp/dotnet-install.sh
COPY --from=sqlpackage-build /opt/sqlpackage /opt/sqlpackage
RUN printf '#!/bin/sh\nexec /usr/share/dotnet/dotnet /opt/sqlpackage/sqlpackage.dll "$@"\n' > /usr/local/bin/sqlpackage && \
chmod +x /usr/local/bin/sqlpackage && \
sqlpackage /version
# Enable corepack for pnpm support and symlink PostgreSQL 18 binaries
# On Debian with PGDG, pg binaries live under /usr/lib/postgresql/18/bin/
RUN corepack enable && corepack prepare pnpm@10.29.3 --activate && \
ln -sf /usr/lib/postgresql/18/bin/pg_dump /usr/local/bin/pg_dump && \
ln -sf /usr/lib/postgresql/18/bin/pg_restore /usr/local/bin/pg_restore && \
ln -sf /usr/lib/postgresql/18/bin/psql /usr/local/bin/psql
# Validate pg_dump version resolves correctly (fail-fast on broken symlinks/packages)
RUN pg_dump --version | grep -q 'PostgreSQL) 18\.' || \
(echo "ERROR: pg_dump version validation failed! Check PostgreSQL 18 client package." && exit 1)
# 1. Install Dependencies
FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# 2. Builder Phase
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Environment variables for build
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_OPTIONS="--max-old-space-size=4096"
# Generate Prisma Client, build Next.js app, and compile custom server
# --mount=type=cache persists the Next.js incremental build cache (.next/cache)
# across Docker builds via GitHub Actions cache (type=gha,mode=max in release.yml).
# Next.js reuses webpack/SWC artefacts for unchanged modules, cutting rebuild time significantly.
RUN --mount=type=cache,id=next-cache,target=/app/.next/cache \
pnpm prisma generate && pnpm run build && npx tsc -p tsconfig.server.json
# 3. Runner Phase (The actual image)
FROM base AS runner
WORKDIR /app
# The Recovery Kit reads this off disk when a user downloads one, so it has to be in the
# image. A missing file is not a build error - the kit is generated with a placeholder
# apologising for its absence, which nobody discovers until they need it. Guarded by
# tests/unit/lint-guards/recovery-kit-shipped.test.ts.
COPY --from=builder --link --chown=1001:1001 /app/scripts/dbackup-recover.js ./scripts/dbackup-recover.js
# Swaps in a configuration restore before the migrations run, called by docker-entrypoint.sh.
COPY --from=builder --link --chown=1001:1001 /app/scripts/apply-pending-restore.js ./scripts/apply-pending-restore.js
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# Default environment variables (can be overridden at runtime)
ENV DATABASE_URL="file:/data/db/dbackup.db"
ENV TZ="UTC"
ENV LOG_LEVEL="info"
ENV PUID=1001
ENV PGID=1001
RUN groupadd --system --gid 1001 nodejs && \
useradd --system --uid 1001 --gid nodejs --no-create-home nextjs
# Copy built files (--link for better layer caching)
COPY --from=builder --link --chown=1001:1001 /app/public ./public
COPY --from=builder --link --chown=1001:1001 /app/.next/standalone ./
COPY --from=builder --link --chown=1001:1001 /app/.next/static ./.next/static
COPY --from=builder --link --chown=1001:1001 /app/prisma ./prisma
# Create runtime data directory + install Prisma CLI for migrations
# Prisma version is read from package.json to stay in sync automatically
#
# pnpm 10 skips the install scripts of dependencies, so the postinstall of @prisma/engines
# never downloads the schema engine. Left like that, `prisma migrate deploy` fetches it from
# binaries.prisma.sh on the first start of every new container, and a host without internet
# or without working DNS at that moment never starts. `prisma version` downloads the engines
# here instead, and the build fails when the schema engine is still missing.
#
# pnpm add -g runs as root, so /pnpm is handed to the runtime user afterwards
COPY --from=builder --link /app/package.json /tmp/package.json
RUN mkdir -p /data/db /data/certs && \
chown -R 1001:1001 /data && \
PRISMA_VERSION=$(node -e "console.log(require('/tmp/package.json').devDependencies.prisma.replace(/[\^~>=<]/g,''))") && \
pnpm add -g prisma@${PRISMA_VERSION} && \
prisma version && \
rm /tmp/package.json && \
chown -R 1001:1001 /pnpm
RUN test -n "$(find /pnpm -type f -name 'schema-engine-*' | head -1)" || \
(echo "ERROR: the Prisma schema engine is missing from the image, migrations would download it at startup" && exit 1)
# Copy compiled custom HTTPS server (replaces default Next.js server entry point)
COPY --from=builder --link --chown=1001:1001 /app/custom-server.js ./custom-server.js
COPY --from=builder --link --chown=1001:1001 /app/server/client-address.js ./server/client-address.js
# Copy entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Health check: verify app + database are reachable
# Uses --insecure for self-signed certs; falls back to http if DISABLE_HTTPS=true
# PORT env var is respected - defaults to 3000 if not set
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -fk https://localhost:${PORT:-3000}/api/health 2>/dev/null || curl -f http://localhost:${PORT:-3000}/api/health || exit 1
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
ENV DISABLE_HTTPS="false"
ENV DATA_DIR="/data"
ENTRYPOINT ["docker-entrypoint.sh"]