Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
438 lines (386 loc) · 22.6 KB
/
Copy pathDockerfile
File metadata and controls
438 lines (386 loc) · 22.6 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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
FROM node:24-alpine AS builder
ARG NEXT_PUBLIC_DOCUMENTS_COLLAB_URL
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
NEXT_PUBLIC_DOCUMENTS_COLLAB_URL=${NEXT_PUBLIC_DOCUMENTS_COLLAB_URL}
WORKDIR /app
# Corporate TLS-intercepting proxies re-sign HTTPS with a root CA the host
# trusts but this build does not, breaking every in-build download (apk, yarn).
# Trust any PEM dropped into docker/certs/ (see its README); no-op when empty.
# The certs land in /usr/local/share/ca-certificates so a later
# update-ca-certificates keeps them, AND get appended to the live bundle so the
# very first apk fetch below already trusts the proxy.
# cert[s] is a glob + docker/README.md an always-present anchor: the COPY then
# succeeds even when docker/certs/ is missing from a partial checkout.
# The anchor MUST live inside docker/: BuildKit only transfers the paths a
# COPY names, so anchoring outside it leaves the whole docker/ directory out
# of the filtered context and the unmatched glob fails on `lstat /docker`.
COPY docker/README.md docker/cert[s] /tmp/om-certs/
RUN set -eu; \
mkdir -p /usr/local/share/ca-certificates; \
for cert in /tmp/om-certs/*.crt /tmp/om-certs/*.pem; do \
[ -f "$cert" ] || continue; \
name="$(basename "$cert")"; \
cp "$cert" "/usr/local/share/ca-certificates/om-extra-${name%.*}.crt"; \
cat "$cert" >> /etc/ssl/certs/ca-certificates.crt; \
printf '\n' >> /etc/ssl/certs/ca-certificates.crt; \
done; \
rm -rf /tmp/om-certs
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
# Corporate networks that category-block dl-cdn.alpinelinux.org can point apk
# at an internal mirror (e.g. Artifactory alpine remote): set ALPINE_MIRROR in
# the repo-root .env; empty (the default) leaves the official CDN in place.
ARG ALPINE_MIRROR=""
RUN [ -z "$ALPINE_MIRROR" ] || sed -i "s|https://dl-cdn.alpinelinux.org|$ALPINE_MIRROR|g" /etc/apk/repositories
# Install system deps required by optional native modules (Alpine uses apk)
RUN apk add --no-cache python3 make g++ ca-certificates openssl
# Enable Corepack for Yarn
RUN corepack enable
# Copy workspace manifests first so dependency installs stay cached across source-only changes.
COPY package.json yarn.lock .yarnrc.yml turbo.json ./
COPY tsconfig.base.json tsconfig.json ./
COPY apps/docs/package.json ./apps/docs/
COPY apps/mercato/package.json ./apps/mercato/
COPY packages/ai-assistant/package.json ./packages/ai-assistant/
COPY packages/cache/package.json ./packages/cache/
COPY packages/channel-apns/package.json ./packages/channel-apns/
COPY packages/channel-discord/package.json ./packages/channel-discord/
COPY packages/channel-expo/package.json ./packages/channel-expo/
COPY packages/channel-fcm/package.json ./packages/channel-fcm/
COPY packages/channel-gmail/package.json ./packages/channel-gmail/
COPY packages/channel-imap/package.json ./packages/channel-imap/
COPY packages/channel-resend/package.json ./packages/channel-resend/
COPY packages/channel-ses/package.json ./packages/channel-ses/
COPY packages/checkout/package.json ./packages/checkout/
COPY packages/cli/package.json ./packages/cli/
COPY packages/content/package.json ./packages/content/
COPY packages/core/package.json ./packages/core/
COPY packages/create-app/package.json ./packages/create-app/
COPY packages/documents/package.json ./packages/documents/
COPY packages/enterprise/package.json ./packages/enterprise/
COPY packages/eslint-plugin-ds/package.json ./packages/eslint-plugin-ds/
COPY packages/events/package.json ./packages/events/
COPY packages/gateway-stripe/package.json ./packages/gateway-stripe/
COPY packages/onboarding/package.json ./packages/onboarding/
COPY packages/queue/package.json ./packages/queue/
COPY packages/scheduler/package.json ./packages/scheduler/
COPY packages/search/package.json ./packages/search/
COPY packages/shared/package.json ./packages/shared/
COPY packages/starter/package.json ./packages/starter/
COPY packages/storage-s3/package.json ./packages/storage-s3/
COPY packages/sync-akeneo/package.json ./packages/sync-akeneo/
COPY packages/telemetry/package.json ./packages/telemetry/
COPY packages/tillio/package.json ./packages/tillio/
COPY packages/ui/package.json ./packages/ui/
COPY packages/web-research/package.json ./packages/web-research/
COPY packages/web-research-browser/package.json ./packages/web-research-browser/
COPY packages/web-research-exa/package.json ./packages/web-research-exa/
COPY packages/web-research-firecrawl/package.json ./packages/web-research-firecrawl/
COPY packages/web-research-model/package.json ./packages/web-research-model/
COPY packages/web-research-searxng/package.json ./packages/web-research-searxng/
COPY packages/web-research-serp/package.json ./packages/web-research-serp/
COPY packages/web-research-tavily/package.json ./packages/web-research-tavily/
COPY packages/webhooks/package.json ./packages/webhooks/
COPY scripts/official-modules-setup.mjs ./scripts/
COPY scripts/lib/official-modules.mjs ./scripts/lib/
# Install all dependencies (including devDependencies for build).
RUN yarn install --immutable
# Copy source files after dependencies are installed.
COPY packages/ ./packages/
COPY apps/ ./apps/
COPY scripts/ ./scripts/
COPY AGENTS.md BACKWARD_COMPATIBILITY.md ./
# Copy other necessary files
COPY newrelic.js ./
COPY jest.config.cjs jest.setup.ts jest.dom.setup.ts ./
COPY eslint.config.mjs ./
# Build the app
# Limit Node.js heap to 4GB and reduce worker count to avoid OOM in constrained Docker environments
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN yarn build
# Dev prebuild stage: install + build at NATIVE VM filesystem speed.
#
# Doing this work at image-build time (instead of at container start through
# the Windows bind mount) is what makes first boot fast: image builds read and
# write the Linux VM's own filesystem, get BuildKit layer caching (manifests
# unchanged => the whole install layer is reused), and produce Linux-native
# node_modules. The dev stage below copies these artifacts to /opt/prebuilt,
# and dev-entrypoint.sh seeds the runtime named volumes from there — a file
# copy instead of a full install/build over the bind mount.
#
# TURBO_CACHE_DIR rides inside node_modules so the turbo cache travels with
# the seeded volume and `yarn build:packages` at boot becomes cache hits.
FROM node:24-alpine AS dev-build
ENV NODE_ENV=development NEXT_TELEMETRY_DISABLED=1 TURBO_CACHE_DIR=/app/node_modules/.cache/turbo
WORKDIR /app
# Corporate proxy CA trust - see the builder stage comment / docker/certs/README.md.
# cert[s] is a glob + docker/README.md an always-present anchor: the COPY then
# succeeds even when docker/certs/ is missing from a partial checkout.
# The anchor MUST live inside docker/: BuildKit only transfers the paths a
# COPY names, so anchoring outside it leaves the whole docker/ directory out
# of the filtered context and the unmatched glob fails on `lstat /docker`.
COPY docker/README.md docker/cert[s] /tmp/om-certs/
RUN set -eu; \
mkdir -p /usr/local/share/ca-certificates; \
for cert in /tmp/om-certs/*.crt /tmp/om-certs/*.pem; do \
[ -f "$cert" ] || continue; \
name="$(basename "$cert")"; \
cp "$cert" "/usr/local/share/ca-certificates/om-extra-${name%.*}.crt"; \
cat "$cert" >> /etc/ssl/certs/ca-certificates.crt; \
printf '\n' >> /etc/ssl/certs/ca-certificates.crt; \
done; \
rm -rf /tmp/om-certs
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
# Optional internal Alpine mirror - see the builder stage comment.
ARG ALPINE_MIRROR=""
RUN [ -z "$ALPINE_MIRROR" ] || sed -i "s|https://dl-cdn.alpinelinux.org|$ALPINE_MIRROR|g" /etc/apk/repositories
RUN apk add --no-cache python3 make g++ ca-certificates openssl
RUN corepack enable
# Copy workspace manifests first so dependency installs stay cached across source-only changes.
COPY package.json yarn.lock .yarnrc.yml turbo.json ./
COPY tsconfig.base.json tsconfig.json ./
COPY apps/docs/package.json ./apps/docs/
COPY apps/mercato/package.json ./apps/mercato/
COPY packages/ai-assistant/package.json ./packages/ai-assistant/
COPY packages/cache/package.json ./packages/cache/
COPY packages/channel-apns/package.json ./packages/channel-apns/
COPY packages/channel-discord/package.json ./packages/channel-discord/
COPY packages/channel-expo/package.json ./packages/channel-expo/
COPY packages/channel-fcm/package.json ./packages/channel-fcm/
COPY packages/channel-gmail/package.json ./packages/channel-gmail/
COPY packages/channel-imap/package.json ./packages/channel-imap/
COPY packages/channel-resend/package.json ./packages/channel-resend/
COPY packages/channel-ses/package.json ./packages/channel-ses/
COPY packages/checkout/package.json ./packages/checkout/
COPY packages/cli/package.json ./packages/cli/
COPY packages/content/package.json ./packages/content/
COPY packages/core/package.json ./packages/core/
COPY packages/create-app/package.json ./packages/create-app/
COPY packages/documents/package.json ./packages/documents/
COPY packages/enterprise/package.json ./packages/enterprise/
COPY packages/eslint-plugin-ds/package.json ./packages/eslint-plugin-ds/
COPY packages/events/package.json ./packages/events/
COPY packages/gateway-stripe/package.json ./packages/gateway-stripe/
COPY packages/onboarding/package.json ./packages/onboarding/
COPY packages/queue/package.json ./packages/queue/
COPY packages/scheduler/package.json ./packages/scheduler/
COPY packages/search/package.json ./packages/search/
COPY packages/shared/package.json ./packages/shared/
COPY packages/starter/package.json ./packages/starter/
COPY packages/storage-s3/package.json ./packages/storage-s3/
COPY packages/sync-akeneo/package.json ./packages/sync-akeneo/
COPY packages/telemetry/package.json ./packages/telemetry/
COPY packages/tillio/package.json ./packages/tillio/
COPY packages/ui/package.json ./packages/ui/
COPY packages/web-research/package.json ./packages/web-research/
COPY packages/web-research-browser/package.json ./packages/web-research-browser/
COPY packages/web-research-exa/package.json ./packages/web-research-exa/
COPY packages/web-research-firecrawl/package.json ./packages/web-research-firecrawl/
COPY packages/web-research-model/package.json ./packages/web-research-model/
COPY packages/web-research-searxng/package.json ./packages/web-research-searxng/
COPY packages/web-research-serp/package.json ./packages/web-research-serp/
COPY packages/web-research-tavily/package.json ./packages/web-research-tavily/
COPY packages/webhooks/package.json ./packages/webhooks/
COPY scripts/official-modules-setup.mjs ./scripts/
COPY scripts/lib/official-modules.mjs ./scripts/lib/
RUN yarn install --immutable
COPY packages/ ./packages/
COPY apps/ ./apps/
COPY scripts/ ./scripts/
COPY AGENTS.md BACKWARD_COMPATIBILITY.md ./
COPY newrelic.js ./
COPY jest.config.cjs jest.setup.ts jest.dom.setup.ts ./
COPY eslint.config.mjs ./
# build -> generate (writes packages/core/generated/) -> rebuild so core picks
# up dist/generated — the same sequence dev-entrypoint.sh runs, done once here.
# `yarn generate` degrades gracefully without a database (skips cache purge).
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN yarn build:packages && yarn generate && yarn build:packages
# Dev stage: lean runtime + /opt/prebuilt artifacts for volume seeding.
# Stage-to-stage COPY keeps a SINGLE copy of node_modules in the final image
# (moving it within one stage would double the layer size).
FROM node:24-alpine AS dev
ENV NODE_ENV=development NEXT_TELEMETRY_DISABLED=1 TURBO_CACHE_DIR=/app/node_modules/.cache/turbo
WORKDIR /app
# Corporate proxy CA trust - see the builder stage comment / docker/certs/README.md.
# Baked into the runtime stage too: the entrypoint's fallback `yarn install`
# and any in-container downloads hit the same intercepting proxy.
# cert[s] is a glob + docker/README.md an always-present anchor: the COPY then
# succeeds even when docker/certs/ is missing from a partial checkout.
# The anchor MUST live inside docker/: BuildKit only transfers the paths a
# COPY names, so anchoring outside it leaves the whole docker/ directory out
# of the filtered context and the unmatched glob fails on `lstat /docker`.
COPY docker/README.md docker/cert[s] /tmp/om-certs/
RUN set -eu; \
mkdir -p /usr/local/share/ca-certificates; \
for cert in /tmp/om-certs/*.crt /tmp/om-certs/*.pem; do \
[ -f "$cert" ] || continue; \
name="$(basename "$cert")"; \
cp "$cert" "/usr/local/share/ca-certificates/om-extra-${name%.*}.crt"; \
cat "$cert" >> /etc/ssl/certs/ca-certificates.crt; \
printf '\n' >> /etc/ssl/certs/ca-certificates.crt; \
done; \
rm -rf /tmp/om-certs
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
# Optional internal Alpine mirror - see the builder stage comment.
ARG ALPINE_MIRROR=""
RUN [ -z "$ALPINE_MIRROR" ] || sed -i "s|https://dl-cdn.alpinelinux.org|$ALPINE_MIRROR|g" /etc/apk/repositories
# Build toolchain kept: the entrypoint's fallback `yarn install` (stale
# lockfile vs prebuilt image) still compiles native modules.
RUN apk add --no-cache python3 make g++ ca-certificates openssl
RUN corepack enable
# Prebuilt artifacts, staged OUTSIDE /app because the repo bind mount masks
# /app at runtime. dev-entrypoint.sh seeds the named volumes from here.
COPY --from=dev-build /app/yarn.lock /opt/prebuilt/yarn.lock
COPY --from=dev-build /app/node_modules /opt/prebuilt/node_modules
COPY --from=dev-build /app/packages/core/dist /opt/prebuilt/dist/core
COPY --from=dev-build /app/packages/shared/dist /opt/prebuilt/dist/shared
COPY --from=dev-build /app/packages/ui/dist /opt/prebuilt/dist/ui
COPY --from=dev-build /app/packages/cli/dist /opt/prebuilt/dist/cli
COPY --from=dev-build /app/packages/cache/dist /opt/prebuilt/dist/cache
COPY --from=dev-build /app/packages/content/dist /opt/prebuilt/dist/content
COPY --from=dev-build /app/packages/checkout/dist /opt/prebuilt/dist/checkout
COPY --from=dev-build /app/packages/events/dist /opt/prebuilt/dist/events
COPY --from=dev-build /app/packages/onboarding/dist /opt/prebuilt/dist/onboarding
COPY --from=dev-build /app/packages/queue/dist /opt/prebuilt/dist/queue
COPY --from=dev-build /app/packages/search/dist /opt/prebuilt/dist/search
COPY --from=dev-build /app/packages/scheduler/dist /opt/prebuilt/dist/scheduler
COPY --from=dev-build /app/packages/ai-assistant/dist /opt/prebuilt/dist/ai-assistant
COPY --from=dev-build /app/packages/create-app/dist /opt/prebuilt/dist/create-app
# The documents-collab sidecar runs `dist/server/documents-collab-server.js` out of the shared
# pkg_documents_dist volume, so this package must be seedable like the rest.
COPY --from=dev-build /app/packages/documents/dist /opt/prebuilt/dist/documents
# Entrypoint scripts are also bind-mounted at runtime (.:/app); baking them in
# keeps the image runnable/consistent on its own and matches the runner stage.
COPY docker/scripts/dev-entrypoint.sh /app/docker/scripts/dev-entrypoint.sh
COPY docker/scripts/init-or-migrate.sh /app/docker/scripts/init-or-migrate.sh
COPY docker/scripts/mcp-entrypoint.sh /app/docker/scripts/mcp-entrypoint.sh
RUN chmod +x /app/docker/scripts/dev-entrypoint.sh
RUN chmod +x /app/docker/scripts/init-or-migrate.sh
RUN chmod +x /app/docker/scripts/mcp-entrypoint.sh
EXPOSE 3000 4101
CMD ["/bin/sh", "/app/docker/scripts/dev-entrypoint.sh"]
# Production stage
FROM node:24-alpine AS runner
ARG CONTAINER_PORT=3000
ARG DOCUMENTS_COLLAB_PORT=4101
# Chromium backs the Documents PDF export (puppeteer-core). Build with
# --build-arg INSTALL_CHROMIUM=1 to include it; PDF export otherwise returns 503.
ARG INSTALL_CHROMIUM=0
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
PORT=${CONTAINER_PORT} \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
WORKDIR /app
# Corporate proxy CA trust - see the builder stage comment / docker/certs/README.md.
# cert[s] is a glob + docker/README.md an always-present anchor: the COPY then
# succeeds even when docker/certs/ is missing from a partial checkout.
# The anchor MUST live inside docker/: BuildKit only transfers the paths a
# COPY names, so anchoring outside it leaves the whole docker/ directory out
# of the filtered context and the unmatched glob fails on `lstat /docker`.
COPY docker/README.md docker/cert[s] /tmp/om-certs/
RUN set -eu; \
mkdir -p /usr/local/share/ca-certificates; \
for cert in /tmp/om-certs/*.crt /tmp/om-certs/*.pem; do \
[ -f "$cert" ] || continue; \
name="$(basename "$cert")"; \
cp "$cert" "/usr/local/share/ca-certificates/om-extra-${name%.*}.crt"; \
cat "$cert" >> /etc/ssl/certs/ca-certificates.crt; \
printf '\n' >> /etc/ssl/certs/ca-certificates.crt; \
done; \
rm -rf /tmp/om-certs
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
# Optional internal Alpine mirror - see the builder stage comment.
ARG ALPINE_MIRROR=""
RUN [ -z "$ALPINE_MIRROR" ] || sed -i "s|https://dl-cdn.alpinelinux.org|$ALPINE_MIRROR|g" /etc/apk/repositories
# Install only production system dependencies (Alpine uses apk)
# sudo: allows non-root user to chown the Railway-mounted volume at startup
RUN if [ "$INSTALL_CHROMIUM" = "1" ]; then \
apk add --no-cache ca-certificates chromium openssl sudo; \
else \
apk add --no-cache ca-certificates openssl sudo; \
fi
# Enable Corepack for Yarn
RUN corepack enable
# Copy workspace configuration for production install
COPY package.json yarn.lock .yarnrc.yml turbo.json ./
COPY tsconfig.base.json tsconfig.json ./
COPY --from=builder /app/.yarn ./.yarn
COPY --from=builder /app/apps/mercato/package.json ./apps/mercato/
COPY --from=builder /app/packages/ai-assistant/package.json ./packages/ai-assistant/
COPY --from=builder /app/packages/cache/package.json ./packages/cache/
COPY --from=builder /app/packages/channel-apns/package.json ./packages/channel-apns/
COPY --from=builder /app/packages/channel-discord/package.json ./packages/channel-discord/
COPY --from=builder /app/packages/channel-expo/package.json ./packages/channel-expo/
COPY --from=builder /app/packages/channel-fcm/package.json ./packages/channel-fcm/
COPY --from=builder /app/packages/channel-gmail/package.json ./packages/channel-gmail/
COPY --from=builder /app/packages/channel-imap/package.json ./packages/channel-imap/
COPY --from=builder /app/packages/channel-resend/package.json ./packages/channel-resend/
COPY --from=builder /app/packages/channel-ses/package.json ./packages/channel-ses/
COPY --from=builder /app/packages/checkout/package.json ./packages/checkout/
COPY --from=builder /app/packages/cli/package.json ./packages/cli/
COPY --from=builder /app/packages/content/package.json ./packages/content/
COPY --from=builder /app/packages/core/package.json ./packages/core/
COPY --from=builder /app/packages/create-app/package.json ./packages/create-app/
COPY --from=builder /app/packages/documents/package.json ./packages/documents/
COPY --from=builder /app/packages/enterprise/package.json ./packages/enterprise/
COPY --from=builder /app/packages/eslint-plugin-ds/package.json ./packages/eslint-plugin-ds/
COPY --from=builder /app/packages/events/package.json ./packages/events/
COPY --from=builder /app/packages/gateway-stripe/package.json ./packages/gateway-stripe/
COPY --from=builder /app/packages/onboarding/package.json ./packages/onboarding/
COPY --from=builder /app/packages/queue/package.json ./packages/queue/
COPY --from=builder /app/packages/scheduler/package.json ./packages/scheduler/
COPY --from=builder /app/packages/search/package.json ./packages/search/
COPY --from=builder /app/packages/shared/package.json ./packages/shared/
COPY --from=builder /app/packages/starter/package.json ./packages/starter/
COPY --from=builder /app/packages/telemetry/package.json ./packages/telemetry/
COPY --from=builder /app/packages/storage-s3/package.json ./packages/storage-s3/
COPY --from=builder /app/packages/sync-akeneo/package.json ./packages/sync-akeneo/
COPY --from=builder /app/packages/tillio/package.json ./packages/tillio/
COPY --from=builder /app/packages/ui/package.json ./packages/ui/
COPY --from=builder /app/packages/web-research/package.json ./packages/web-research/
COPY --from=builder /app/packages/web-research-browser/package.json ./packages/web-research-browser/
COPY --from=builder /app/packages/web-research-exa/package.json ./packages/web-research-exa/
COPY --from=builder /app/packages/web-research-firecrawl/package.json ./packages/web-research-firecrawl/
COPY --from=builder /app/packages/web-research-model/package.json ./packages/web-research-model/
COPY --from=builder /app/packages/web-research-searxng/package.json ./packages/web-research-searxng/
COPY --from=builder /app/packages/web-research-serp/package.json ./packages/web-research-serp/
COPY --from=builder /app/packages/web-research-tavily/package.json ./packages/web-research-tavily/
COPY --from=builder /app/packages/webhooks/package.json ./packages/webhooks/
# Install only production dependencies
RUN yarn workspaces focus @open-mercato/app --production
# Copy workspace sources after production dependencies are installed.
COPY --from=builder /app/packages/ ./packages/
# Copy built Next.js application
COPY --from=builder /app/apps/mercato/.mercato/next ./apps/mercato/.mercato/next
COPY --from=builder /app/apps/mercato/public ./apps/mercato/public
COPY --from=builder /app/apps/mercato/next.config.ts ./apps/mercato/
COPY --from=builder /app/apps/mercato/components.json ./apps/mercato/
COPY --from=builder /app/apps/mercato/tsconfig.json ./apps/mercato/
COPY --from=builder /app/apps/mercato/postcss.config.mjs ./apps/mercato/
# Copy generated files and other runtime necessities
COPY --from=builder /app/apps/mercato/.mercato/generated ./apps/mercato/.mercato/generated
COPY --from=builder /app/apps/mercato/src ./apps/mercato/src
COPY --from=builder /app/apps/mercato/types ./apps/mercato/types
# The init/migration entrypoint invokes the workspace CLI through this launcher.
COPY --from=builder /app/apps/mercato/scripts ./apps/mercato/scripts
# Copy runtime configuration files
COPY --from=builder /app/newrelic.js ./
# Copy Railway entrypoint script
COPY docker/scripts/railway-entrypoint.sh /app/docker/scripts/railway-entrypoint.sh
COPY docker/scripts/init-or-migrate.sh /app/docker/scripts/init-or-migrate.sh
COPY docker/scripts/mcp-entrypoint.sh /app/docker/scripts/mcp-entrypoint.sh
RUN chmod +x /app/docker/scripts/railway-entrypoint.sh
RUN chmod +x /app/docker/scripts/init-or-migrate.sh
RUN chmod +x /app/docker/scripts/mcp-entrypoint.sh
# Prepare storage directory for Railway volume mount
RUN mkdir -p /app/apps/mercato/storage
# Create non-root user and grant passwordless sudo for chown only
RUN adduser -D -u 1001 omuser \
&& chown -R omuser:omuser /app/apps/mercato/storage \
&& echo "omuser ALL=(root) NOPASSWD: /bin/chown" > /etc/sudoers.d/omuser \
&& chmod 0440 /etc/sudoers.d/omuser
USER omuser
EXPOSE ${CONTAINER_PORT} ${DOCUMENTS_COLLAB_PORT}
WORKDIR /app/apps/mercato
CMD ["yarn", "start"]