# =========================================================================== # Dependency stages # =========================================================================== FROM node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436 AS front-deps WORKDIR /app COPY ./package.json ./yarn.lock ./.yarnrc.yml ./tsconfig.base.json ./nx.json /app/ COPY ./.yarn/releases /app/.yarn/releases COPY ./.yarn/patches /app/.yarn/patches COPY ./packages/twenty-ui/package.json /app/packages/twenty-ui/ COPY ./packages/twenty-shared/package.json /app/packages/twenty-shared/ COPY ./packages/twenty-front/package.json /app/packages/twenty-front/ COPY ./packages/twenty-front-component-renderer/package.json /app/packages/twenty-front-component-renderer/ COPY ./packages/twenty-sdk/package.json /app/packages/twenty-sdk/ COPY ./packages/twenty-client-sdk/package.json /app/packages/twenty-client-sdk/ RUN yarn workspaces focus twenty twenty-front twenty-front-component-renderer twenty-ui twenty-shared twenty-sdk twenty-client-sdk && yarn cache clean && npx nx reset FROM node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436 AS server-deps WORKDIR /app COPY ./package.json ./yarn.lock ./.yarnrc.yml ./tsconfig.base.json ./nx.json /app/ COPY ./.yarn/releases /app/.yarn/releases COPY ./.yarn/patches /app/.yarn/patches COPY ./packages/twenty-emails/package.json /app/packages/twenty-emails/ COPY ./packages/twenty-server/package.json /app/packages/twenty-server/ COPY ./packages/twenty-server/patches /app/packages/twenty-server/patches COPY ./packages/twenty-shared/package.json /app/packages/twenty-shared/ COPY ./packages/twenty-client-sdk/package.json /app/packages/twenty-client-sdk/ RUN yarn workspaces focus twenty twenty-server twenty-emails twenty-shared twenty-client-sdk && yarn cache clean && npx nx reset FROM server-deps AS twenty-server-build COPY ./packages/twenty-emails /app/packages/twenty-emails COPY ./packages/twenty-shared /app/packages/twenty-shared COPY ./packages/twenty-client-sdk /app/packages/twenty-client-sdk COPY ./packages/twenty-server /app/packages/twenty-server RUN if [ -d /app/packages/twenty-server/dist ]; then \ echo "Using pre-built server from host"; \ else \ npx nx run twenty-server:lingui:extract && \ npx nx run twenty-server:lingui:compile && \ npx nx run twenty-emails:lingui:extract && \ npx nx run twenty-emails:lingui:compile && \ npx nx run twenty-server:build; \ fi # Clean server build output (type declarations and compiled tests are not needed at runtime; # source maps are kept because twenty-infra extracts them from the image for Sentry uploads) RUN find /app/packages/twenty-server/dist -name '*.d.ts' -delete \ && rm -rf /app/packages/twenty-server/dist/packages/twenty-server/test RUN yarn workspaces focus --production twenty-emails twenty-shared twenty-client-sdk twenty-server FROM front-deps AS twenty-front-build COPY ./packages/twenty-front /app/packages/twenty-front COPY ./packages/twenty-front-component-renderer /app/packages/twenty-front-component-renderer COPY ./packages/twenty-ui /app/packages/twenty-ui COPY ./packages/twenty-shared /app/packages/twenty-shared COPY ./packages/twenty-sdk /app/packages/twenty-sdk COPY ./packages/twenty-client-sdk /app/packages/twenty-client-sdk RUN npx nx run twenty-front:lingui:extract && \ npx nx run twenty-front:lingui:compile # To skip the memory-intensive frontend build, pre-build on the host: # npx nx build twenty-front # The check below will use packages/twenty-front/build/ if it already exists. RUN if [ -d /app/packages/twenty-front/build ]; then \ echo "Using pre-built frontend from host"; \ else \ NODE_OPTIONS="--max-old-space-size=8192" npx nx build twenty-front; \ fi # =========================================================================== # Target: twenty-server (server only, no frontend) # =========================================================================== FROM node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436 AS twenty-server RUN apk add --no-cache \ 'curl>=8.19.0-r0' \ 'nghttp2-libs>=1.69.0-r0' \ 'libcrypto3>=3.5.7-r0' \ 'libssl3>=3.5.7-r0' \ 'postgresql18-client>=18.4-r0' \ jq COPY ./packages/twenty-docker/twenty/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh WORKDIR /app/packages/twenty-server ARG APP_VERSION ENV APP_VERSION=$APP_VERSION ENV NODE_ENV=production # Workspace root config COPY --chown=1000 --from=twenty-server-build /app/package.json /app/yarn.lock /app/.yarnrc.yml /app/ COPY --chown=1000 --from=twenty-server-build /app/tsconfig.base.json /app/nx.json /app/ COPY --chown=1000 --from=twenty-server-build /app/.yarn /app/.yarn COPY --chown=1000 --from=twenty-server-build /app/node_modules /app/node_modules # Server package (compiled dist + package.json only, no src/) COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-server/package.json /app/packages/twenty-server/ COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-server/dist /app/packages/twenty-server/dist COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-server/patches /app/packages/twenty-server/patches # Workspace packages (dist + package.json; node_modules symlinks resolve to these) COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-shared/package.json /app/packages/twenty-shared/ COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-shared/dist /app/packages/twenty-shared/dist COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-emails/package.json /app/packages/twenty-emails/ COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-emails/dist /app/packages/twenty-emails/dist COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-client-sdk/package.json /app/packages/twenty-client-sdk/ COPY --chown=1000 --from=twenty-server-build /app/packages/twenty-client-sdk/dist /app/packages/twenty-client-sdk/dist RUN rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx \ /usr/local/include/node && \ find /app/node_modules -type d -name example -prune -exec rm -rf {} + RUN mkdir -p /app/.local-storage /app/packages/twenty-server/.local-storage && \ chown 1000:1000 /app/.local-storage /app/packages/twenty-server/.local-storage USER 1000 CMD ["node", "dist/main"] ENTRYPOINT ["/app/entrypoint.sh"] # =========================================================================== # Target: twenty (server + frontend) # =========================================================================== FROM twenty-server AS twenty COPY --chown=1000 --from=twenty-front-build /app/packages/twenty-front/build /app/packages/twenty-server/dist/front # =========================================================================== # Target: twenty-app-dev (all-in-one with Postgres + Redis) # =========================================================================== FROM alpine:3.20 AS s6-fetch ARG S6_OVERLAY_VERSION=3.2.0.2 ARG TARGETARCH RUN if [ "$TARGETARCH" = "arm64" ]; then echo "aarch64" > /tmp/s6arch; \ else echo "x86_64" > /tmp/s6arch; fi RUN S6_ARCH=$(cat /tmp/s6arch) && \ wget -O /tmp/s6-overlay-noarch.tar.xz \ "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" && \ wget -O /tmp/s6-overlay-noarch.tar.xz.sha256 \ "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz.sha256" && \ wget -O /tmp/s6-overlay-arch.tar.xz \ "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz" && \ wget -O /tmp/s6-overlay-arch.tar.xz.sha256 \ "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz.sha256" && \ cd /tmp && \ NOARCH_SUM=$(awk '{print $1}' s6-overlay-noarch.tar.xz.sha256) && \ ARCH_SUM=$(awk '{print $1}' s6-overlay-arch.tar.xz.sha256) && \ echo "$NOARCH_SUM s6-overlay-noarch.tar.xz" | sha256sum -c - && \ echo "$ARCH_SUM s6-overlay-arch.tar.xz" | sha256sum -c - FROM node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436 AS twenty-app-dev # s6-overlay COPY --from=s6-fetch /tmp/s6-overlay-noarch.tar.xz /tmp/ COPY --from=s6-fetch /tmp/s6-overlay-arch.tar.xz /tmp/ RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz \ && tar -C / -Jxpf /tmp/s6-overlay-arch.tar.xz \ && rm /tmp/s6-overlay-*.tar.xz # See twenty-server above: patched Alpine OpenSSL libs (psql/curl/redis link # these system libs; Node bundles its own). RUN apk add --no-cache \ postgresql18 postgresql18-contrib \ redis \ 'libcrypto3>=3.5.7-r0' \ 'libssl3>=3.5.7-r0' \ curl jq su-exec # Workspace root config COPY --from=twenty-server-build /app/package.json /app/yarn.lock /app/.yarnrc.yml /app/ COPY --from=twenty-server-build /app/tsconfig.base.json /app/nx.json /app/ COPY --from=twenty-server-build /app/.yarn /app/.yarn COPY --from=twenty-server-build /app/node_modules /app/node_modules # Server package (compiled dist + package.json only, no src/) COPY --from=twenty-server-build /app/packages/twenty-server/package.json /app/packages/twenty-server/ COPY --from=twenty-server-build /app/packages/twenty-server/dist /app/packages/twenty-server/dist COPY --from=twenty-server-build /app/packages/twenty-server/patches /app/packages/twenty-server/patches # Workspace packages (dist + package.json; node_modules symlinks resolve to these) COPY --from=twenty-server-build /app/packages/twenty-shared/package.json /app/packages/twenty-shared/ COPY --from=twenty-server-build /app/packages/twenty-shared/dist /app/packages/twenty-shared/dist COPY --from=twenty-server-build /app/packages/twenty-emails/package.json /app/packages/twenty-emails/ COPY --from=twenty-server-build /app/packages/twenty-emails/dist /app/packages/twenty-emails/dist COPY --from=twenty-server-build /app/packages/twenty-client-sdk/package.json /app/packages/twenty-client-sdk/ COPY --from=twenty-server-build /app/packages/twenty-client-sdk/dist /app/packages/twenty-client-sdk/dist # Frontend static build COPY --from=twenty-front-build /app/packages/twenty-front/build /app/packages/twenty-server/dist/front # Source maps are not needed in the dev image (no Sentry). Node dev headers are # dropped for the same scanner-hygiene reasons as in twenty-server above. RUN find /app/packages/twenty-server/dist -name '*.js.map' -delete \ && rm -rf /usr/local/include/node # s6 service definitions COPY packages/twenty-docker/twenty-app-dev/rootfs/ / RUN mkdir -p /data/postgres /data/redis /app/packages/twenty-server/.local-storage \ && chown -R postgres:postgres /data/postgres \ && chown 1000:1000 /data/redis /app/packages/twenty-server/.local-storage ARG APP_VERSION=0.0.0 ENV S6_KEEP_ENV=1 ENV PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/default \ SERVER_URL=https://leon4gr45-twenty.hf.space \ REDIS_URL=redis://localhost:6379 \ STORAGE_TYPE=local \ APP_SECRET=twenty-app-dev-secret-not-for-production \ APP_VERSION=$APP_VERSION \ NODE_ENV=development \ NODE_PORT=7860 \ DISABLE_DB_MIGRATIONS=true \ DISABLE_CRON_JOBS_REGISTRATION=true \ IS_BILLING_ENABLED=false \ SIGN_IN_PREFILLED=true \ APPLICATION_LOG_DRIVER=CONSOLE EXPOSE 7860 VOLUME ["/data/postgres", "/app/packages/twenty-server/.local-storage"] LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty LABEL org.opencontainers.image.description="All-in-one Twenty image for local development and SDK usage. Includes PostgreSQL, Redis, server, and worker." ENTRYPOINT ["/init"]