Ctrl K
ComunidadGuías…

Dockerfile

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Multi-stage Dockerfile for Actual-sync
# Optimized for minimal image size and production deployment

# Stage 1: Builder
# Use native platform for building to avoid QEMU emulation issues
FROM --platform=$BUILDPLATFORM node:22-alpine@sha256:fe4f6d74e20370acb04ad889077d14d0f64a9c8ea6a42bc60cddf1e844699914 AS builder

# Install build dependencies
RUN apk add --no-cache \
    python3 \
    make \
    g++ \
    sqlite

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install production dependencies only. devDependencies (jest, puppeteer — which
# downloads Chromium) are not needed: tests run in the dedicated CI job, and the
# production stage copies this node_modules verbatim, so it must be dev-free.
RUN npm ci --omit=dev

# Copy source code
COPY . .

# Stage 2: Production image
FROM node:22-alpine@sha256:fe4f6d74e20370acb04ad889077d14d0f64a9c8ea6a42bc60cddf1e844699914

# Build arguments for dynamic versioning
ARG VERSION=unknown
ENV VERSION=${VERSION}

# Install runtime dependencies, tini, and the tools needed for PUID/PGID support
# (su-exec to drop privileges, shadow for usermod/groupmod)
RUN apk add --no-cache sqlite tini su-exec shadow

# Create non-root user
RUN addgroup -g 1001 -S actualuser && \
    adduser -u 1001 -S actualuser -G actualuser

WORKDIR /app

# Copy package files and node_modules from builder
COPY package*.json ./
COPY --from=builder /app/node_modules ./node_modules

# Copy application files from builder
COPY --from=builder /app/src ./src
COPY --from=builder /app/config ./config
COPY --from=builder /app/scripts ./scripts
COPY --from=builder /app/index.js ./

# Bundle the config defaults (example + schema) at a path the user's config
# volume mount does NOT shadow, so first-run seeding and schema validation
# still work when /app/config is a fresh bind mount. (#96)
COPY --from=builder /app/config ./config-defaults

# Entrypoint handles PUID/PGID alignment and privilege drop
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# Create the writable volume dirs and own only those. App code and node_modules
# are world-readable as copied, so the runtime user can read them — a recursive
# chown over /app (thousands of node_modules files) is needlessly slow. The
# entrypoint also re-chowns data/logs to the runtime PUID/PGID at startup.
RUN mkdir -p /app/data /app/logs && \
    chown -R actualuser:actualuser /app/data /app/logs

# OCI metadata labels
LABEL org.opencontainers.image.version="${VERSION}" \
      org.opencontainers.image.title="actual-sync" \
      org.opencontainers.image.description="Automated bank synchronization service for Actual Budget with multi-server support" \
      org.opencontainers.image.vendor="Actual-sync" \
      org.opencontainers.image.licenses="MIT"

# NOTE: the container starts as root so the entrypoint can align PUID/PGID and
# fix volume ownership; it then drops to the non-root actualuser via su-exec.

# Expose health check port
EXPOSE 3000

# Health check using the built-in endpoint
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
    CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1); }).on('error', () => process.exit(1));"

# tini for signal handling; entrypoint applies PUID/PGID then drops privileges
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]

# Start the application
CMD ["node", "index.js"]