Files
actual/packages/sync-server/docker/alpine.Dockerfile
T
Matiss Janis AboltinsandClaude Opus 4.7 7501674613 [AI] Fix Docker build for workspace:* dependencies (#7564)
* [AI] Fix Docker build for workspace:* dependencies

Since @actual-app/crdt became a workspace:* dep, `yarn workspaces focus
--production` creates relative symlinks in node_modules that dangle when
only node_modules is copied into the prod image, breaking local Docker
builds with ERR_MODULE_NOT_FOUND: @actual-app/crdt.

Dereference yarn's workspace symlinks in the builder stage with `cp -RL`
so the prod stage can copy a self-contained node_modules without needing
to enumerate which workspace:* deps exist. Adding a new workspace:* dep
now requires zero Dockerfile changes.

Also move the sync-server .dockerignore to the repo root (and drop stray
local node_modules / .git / .yarn caches from the build context), since
docker builds use the repo root as context — the old sync-server-level
file was no longer being applied.

Fixes #7561.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* [AI] Strip dev-only dirs from dereferenced workspace packages

The generic `cp -RL` step copies full workspace package trees into the
image (src/, e2e/, tests, build-stats, etc.). Remove them after the
dereference — they're not needed at runtime, and skipping them recovers
~67MB from the final image on both alpine and ubuntu variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* [AI] Rephrase 7564 release note to be user-facing

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-22 19:20:51 +00:00

52 lines
1.8 KiB
Docker

FROM node:22-alpine AS builder
# Install required packages
RUN apk add --no-cache python3 openssl build-base
RUN corepack enable
WORKDIR /app
COPY .yarn ./.yarn
COPY yarn.lock package.json .yarnrc.yml ./
COPY packages ./packages
# Avoiding memory issues with ARMv7
RUN if [ "$(uname -m)" = "armv7l" ]; then yarn config set taskPoolConcurrency 2; yarn config set networkConcurrency 5; fi
# Focus the workspaces in production mode
RUN if [ "$(uname -m)" = "armv7l" ]; then npm_config_build_from_source=true yarn workspaces focus @actual-app/sync-server --production; else yarn workspaces focus @actual-app/sync-server --production; fi
# Dereference yarn's workspace:* symlinks so the prod stage can copy just node_modules.
RUN cp -RL node_modules node_modules.real \
&& rm -rf node_modules \
&& mv node_modules.real node_modules
# Strip dev-only content from dereferenced workspace packages to keep the final image lean.
RUN find node_modules/@actual-app -maxdepth 2 -type d \
\( -name src -o -name e2e -o -name __tests__ -o -name __mocks__ -o -name tests -o -name test -o -name build-stats \) \
-exec rm -rf {} +
FROM alpine:3.22 AS prod
# Minimal runtime dependencies
RUN apk add --no-cache nodejs tini
# Create a non-root user
ARG USERNAME=actual
ARG USER_UID=1001
ARG USER_GID=$USER_UID
RUN addgroup -S ${USERNAME} -g ${USER_GID} && adduser -S ${USERNAME} -G ${USERNAME} -u ${USER_UID}
RUN mkdir /data && chown -R ${USERNAME}:${USERNAME} /data
WORKDIR /app
ENV NODE_ENV=production
# sync-server entry flattened at /app so CMD stays `node app.js`.
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/packages/sync-server/package.json ./
COPY --from=builder /app/packages/sync-server/build ./
ENTRYPOINT ["/sbin/tini","-g", "--"]
EXPOSE 5006
CMD ["node", "app.js"]