Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
163 lines (136 loc) · 7.16 KB
/
Copy pathDockerfile
File metadata and controls
163 lines (136 loc) · 7.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
FROM node:24.22.1-bookworm-slim AS base
# C.UTF-8 ships with Debian, so no locale generation is needed.
# REDISMS_VERSION pins the Redis that redis-memory-server (AP_REDIS_TYPE=MEMORY)
# compiles at image build and looks up at runtime — the default "stable" drifted
# to Redis 8, whose in-tree modules need cmake/pkg-config and break the build;
# Redis 7.x compiles with gcc/make alone. Keep this pin in the base stage so the
# runtime binary-cache key matches the one baked at build.
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
REDISMS_VERSION=7.4.2
# Install all system dependencies in a single layer. No apt cache mounts: docker-clean in the
# node base image wipes /var/cache/apt anyway, and a persisted /var/lib/apt/lists goes stale
# against rotated bookworm-security packages, failing the build with hash/size fetch errors.
# libcap2 is isolate's runtime lib (the isolate binaries ship prebuilt in api assets).
# tini runs as PID 1 and reaps orphaned sandbox children; node as PID 1 never waits on them and they pile up as zombies.
RUN apt-get update && \
apt-get install -y --no-install-recommends \
openssh-client \
python3 \
g++ \
build-essential \
git \
poppler-utils \
poppler-data \
procps \
tini \
unzip \
curl \
ca-certificates \
iptables \
libcap2 && \
rm -rf /var/lib/apt/lists/*
# Download, extract, and clean up bun in a single layer so the zip never ships
RUN export ARCH=$(uname -m) && \
if [ "$ARCH" = "x86_64" ]; then \
curl -fSL --retry 5 --retry-delay 2 https://github.com/oven-sh/bun/releases/download/bun-v1.4.4/bun-linux-x64-baseline.zip -o bun.zip; \
elif [ "$ARCH" = "aarch64" ]; then \
curl -fSL --retry 5 --retry-delay 2 https://github.com/oven-sh/bun/releases/download/bun-v1.4.4/bun-linux-aarch64.zip -o bun.zip; \
fi && \
unzip bun.zip && \
mv bun-*/bun /usr/local/bin/bun && \
chmod +x /usr/local/bin/bun && \
rm -rf bun.zip bun-* && \
bun --version
# Download deno (the code-sandbox runtime) — pinned to match the `deno` npm dep.
# The forked engine resolves it via AP_DENO_PATH, so no PATH lookup is needed.
RUN export ARCH=$(uname -m) && \
if [ "$ARCH" = "x86_64" ]; then \
curl -fSL https://github.com/denoland/deno/releases/download/v2.9.3/deno-x86_64-unknown-linux-gnu.zip -o deno.zip; \
elif [ "$ARCH" = "aarch64" ]; then \
curl -fSL https://github.com/denoland/deno/releases/download/v2.9.3/deno-aarch64-unknown-linux-gnu.zip -o deno.zip; \
fi && \
unzip deno.zip -d /usr/local/bin && \
chmod +x /usr/local/bin/deno && \
rm -f deno.zip && \
deno --version
ENV AP_DENO_PATH=/usr/local/bin/deno
# Install global npm packages in a single layer
RUN --mount=type=cache,target=/root/.npm \
npm install -g --no-fund --no-audit \
node-gyp \
npm@11.11.0 \
esbuild@0.25.0
# Install isolated-vm globally (needed for sandboxes)
RUN --mount=type=cache,target=/root/.bun/install/cache \
cd /usr/src && bun install isolated-vm@6.2.0
### STAGE 1: Build ###
FROM base AS build
WORKDIR /usr/src/app
# Copy dependency files and workspace package.json files for resolution
COPY .npmrc package.json bun.lock bunfig.toml ./
COPY packages/ ./packages/
# Install all dependencies with frozen lockfile
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install --frozen-lockfile
# Copy remaining source code (turbo config, etc.)
COPY . .
# Build frontend, engine, server API, and worker
RUN NODE_OPTIONS=--max-old-space-size=4096 npx turbo run build --filter=web --filter=@activepieces/engine --filter=api --filter=worker
# Source maps are off unless AP_BUILD_SOURCEMAP=true: there is no upload step yet, so
# they'd be ~21MB of output that this layer deletes anyway. Turning them on adds about
# 0.6GB of peak memory and a second or two. A build that opts in must upload them BEFORE
# this line; the delete stays so source is never served from the shipped image.
# TODO(cloud-ci): set AP_BUILD_SOURCEMAP=true and upload with sentry-cli when SENTRY_AUTH_TOKEN is set.
RUN find dist/packages/web -name '*.map' -delete
# Generate migration manifest (ordered list of migration names) for image-tag-based rollback
RUN node -e "\
const {getMigrations} = require('./packages/server/api/dist/src/app/database/postgres-connection');\
const names = getMigrations().map(M => new M().name);\
process.stdout.write(JSON.stringify(names));\
" > packages/server/api/dist/src/migration-manifest.json
# Remove workspaces not needed at runtime: pieces except the 5 the api imports,
# plus web/cli/tests-e2e/embed-sdk whose deps (react & friends) would otherwise land
# in the runtime node_modules. dist/packages/web is already built and kept.
# Then drop the removed entries from the root workspaces list, drop the engine's test-only
# core-piece devDependencies (their workspaces are gone), and regenerate bun.lock.
RUN rm -rf packages/pieces/core packages/pieces/custom \
packages/web packages/cli packages/tests-e2e packages/ee && \
find packages/pieces/community -mindepth 1 -maxdepth 1 -type d \
! -name slack \
! -name square \
! -name facebook-leads \
! -name intercom \
! -name microsoft-teams-bot \
-exec rm -rf {} + && \
node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.workspaces=p.workspaces.filter(w=>fs.existsSync(w.replace('/*','')));fs.writeFileSync('package.json',JSON.stringify(p,null,2))" && \
node -e "const fs=require('fs');const f='packages/server/engine/package.json';const p=JSON.parse(fs.readFileSync(f,'utf8'));p.devDependencies=Object.fromEntries(Object.entries(p.devDependencies).filter(([n])=>!n.startsWith('@activepieces/piece-')));fs.writeFileSync(f,JSON.stringify(p,null,2))" && \
rm -f bun.lock && bun install
### STAGE 2: Run ###
FROM base AS run
WORKDIR /usr/src/app
# Copy static configuration files first (better layer caching)
COPY --from=build /usr/src/app/packages/server/api/src/assets/default.cf /usr/local/etc/isolate
COPY docker-entrypoint.sh .
# Copy root config files needed for dependency resolution
COPY --from=build /usr/src/app/package.json ./
COPY --from=build /usr/src/app/.npmrc ./
COPY --from=build /usr/src/app/bun.lock ./
COPY --from=build /usr/src/app/bunfig.toml ./
COPY --from=build /usr/src/app/LICENSE .
# Copy workspace package.json files (needed for bun workspace resolution)
COPY --from=build /usr/src/app/packages ./packages
# Copy built engine
COPY --from=build /usr/src/app/dist/packages/engine/ ./dist/packages/engine/
# Regenerate lockfile and install production dependencies (pieces were trimmed from workspace)
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install --production
# Copy frontend files
COPY --from=build /usr/src/app/dist/packages/web ./dist/packages/web/
ENV NODE_ENV=production
LABEL service=activepieces
# WORKER containers have no HTTP server; treat them as healthy (probe only the app).
HEALTHCHECK --interval=10s --timeout=5s --start-period=60s --retries=5 \
CMD [ "$AP_CONTAINER_TYPE" = "WORKER" ] && exit 0 || curl -fsS "http://localhost:${AP_PORT:-80}/api/v1/health" || exit 1
ENTRYPOINT ["tini", "--", "./docker-entrypoint.sh"]
EXPOSE 80