|
1 | 1 | # This Dockerfile is used solely for production deployments to Moda
|
2 |
| -# For staging deployments, see src/deployments/staging/Dockerfile |
3 | 2 | # For building this file locally, see src/deployments/production/README.md
|
| 3 | +# Environment variables are set in the Moda configuration: |
| 4 | +# config/moda/configuration/*/env.yaml |
4 | 5 |
|
5 |
| -# -------------------------------------------------------------------------------- |
6 |
| -# BASE IMAGE |
7 |
| -# -------------------------------------------------------------------------------- |
| 6 | +# --------------------------------------------------------------- |
| 7 | +# BASE STAGE: Install linux dependencies and set up the node user |
| 8 | +# --------------------------------------------------------------- |
8 | 9 | # To update the sha:
|
9 | 10 | # https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble
|
10 | 11 | FROM ghcr.io/github/gh-base-image/gh-base-noble:20250131-172559-g0fd5a2edc AS base
|
11 | 12 |
|
| 13 | +# Install curl for Node install and determining the early access branch |
12 | 14 | # Install git for cloning docs-early-access & translations repos
|
13 |
| -# Install curl for determining the early access branch |
14 |
| -RUN apt-get -qq update && apt-get -qq install --no-install-recommends git curl |
15 |
| - |
16 | 15 | # Install Node.js latest LTS
|
17 | 16 | # https://github.com/nodejs/release#release-schedule
|
18 | 17 | # Ubuntu's apt-get install nodejs is _very_ outdated
|
19 |
| -RUN curl -sL https://deb.nodesource.com/setup_22.x | bash - |
20 |
| -RUN apt-get install -y nodejs |
21 |
| -RUN node --version |
22 |
| - |
23 |
| -# This directory is owned by the node user |
24 |
| -RUN useradd -ms /bin/bash node |
25 |
| -ARG APP_HOME=/home/node/app |
26 |
| -RUN mkdir -p $APP_HOME && chown -R node:node $APP_HOME |
| 18 | +# Must run as root |
| 19 | +RUN apt-get -qq update && apt-get -qq install --no-install-recommends curl git \ |
| 20 | + && curl -sL https://deb.nodesource.com/setup_22.x | bash - \ |
| 21 | + && apt-get install -y nodejs \ |
| 22 | + && node --version |
| 23 | + |
| 24 | +# Create the node user and home directory |
| 25 | +ARG APP_HOME="/home/node/app" # Define in base so all child stages inherit it |
| 26 | +RUN useradd -ms /bin/bash node \ |
| 27 | + && mkdir -p $APP_HOME && chown -R node:node $APP_HOME |
| 28 | + |
| 29 | +# ----------------------------------------------------------------- |
| 30 | +# CLONES STAGE: Clone docs-internal, early-access, and translations |
| 31 | +# ----------------------------------------------------------------- |
| 32 | +FROM base AS clones |
| 33 | +USER node:node |
27 | 34 | WORKDIR $APP_HOME
|
28 | 35 |
|
29 |
| -# Switch to root to ensure we have permissions to copy, chmod, and install |
30 |
| -USER root |
31 |
| - |
32 |
| -# Copy in build scripts |
33 |
| -COPY src/deployments/production/build-scripts/*.sh ./build-scripts/ |
34 |
| - |
35 |
| -# Make scripts executable |
36 |
| -RUN chmod +x build-scripts/*.sh |
37 |
| - |
38 | 36 | # We need to copy over content that will be merged with early-access
|
39 |
| -COPY content ./content |
40 |
| -COPY assets ./assets |
41 |
| -COPY data ./data |
| 37 | +COPY --chown=node:node content content/ |
| 38 | +COPY --chown=node:node assets assets/ |
| 39 | +COPY --chown=node:node data data/ |
| 40 | + |
| 41 | +# Copy in build scripts and make them executable |
| 42 | +COPY --chown=node:node --chmod=+x \ |
| 43 | + src/deployments/production/build-scripts/*.sh build-scripts/ |
42 | 44 |
|
43 | 45 | # Use the mounted --secret to:
|
44 | 46 | # - 1. Fetch the docs-internal repo
|
45 | 47 | # - 2. Fetch the docs-early-access repo & override docs-internal with early access content
|
46 | 48 | # - 3. Fetch each translations repo to the repo/translations directory
|
47 | 49 | # We use --mount-type=secret to avoid the secret being copied into the image layers for security
|
48 | 50 | # The secret passed via --secret can only be used in this RUN command
|
49 |
| -RUN --mount=type=secret,id=DOCS_BOT_PAT_READPUBLICKEY \ |
| 51 | +RUN --mount=type=secret,id=DOCS_BOT_PAT_READPUBLICKEY,mode=0444 \ |
50 | 52 | # We don't cache because Docker can't know if we need to fetch new content from remote repos
|
51 | 53 | echo "Don't cache this step by printing date: $(date)" && \
|
52 | 54 | . ./build-scripts/fetch-repos.sh
|
53 | 55 |
|
54 |
| -# Give node user access to the copied content since we cloned as root |
55 |
| -RUN chown -R node:node $APP_HOME/content |
56 |
| -RUN chown -R node:node $APP_HOME/assets |
57 |
| -RUN chown -R node:node $APP_HOME/data |
58 |
| -# Give node user access to translations repos |
59 |
| -RUN chown -R node:node $APP_HOME/translations |
60 |
| - |
61 |
| -# Change back to node to make sure we don't run anything as the root user |
62 |
| -USER node |
63 |
| - |
64 |
| -# --------------- |
65 |
| -# ALL DEPS Image |
66 |
| -# --------------- |
67 |
| -FROM base AS all_deps |
68 |
| - |
69 |
| -ARG APP_HOME=/home/node/app |
70 |
| -USER node |
| 56 | +# ----------------------------------------- |
| 57 | +# DEPENDENCIES STAGE: Install node packages |
| 58 | +# ----------------------------------------- |
| 59 | +FROM base AS dependencies |
| 60 | +USER node:node |
71 | 61 | WORKDIR $APP_HOME
|
72 | 62 |
|
73 | 63 | # Copy what is needed to run npm ci
|
74 | 64 | COPY --chown=node:node package.json package-lock.json ./
|
75 | 65 |
|
76 |
| -RUN npm ci --no-optional --registry https://registry.npmjs.org/ |
| 66 | +RUN npm ci --omit=optional --registry https://registry.npmjs.org/ |
77 | 67 |
|
78 |
| -# --------------- |
79 |
| -# BUILDER Image |
80 |
| -# --------------- |
81 |
| -FROM all_deps AS builder |
82 |
| - |
83 |
| -ARG APP_HOME=/home/node/app |
84 |
| -USER node |
| 68 | +# ----------------------------------------- |
| 69 | +# BUILD STAGE: Prepare for production stage |
| 70 | +# ----------------------------------------- |
| 71 | +FROM base AS build |
| 72 | +USER node:node |
85 | 73 | WORKDIR $APP_HOME
|
86 | 74 |
|
87 |
| -# Copy what is needed to: |
88 |
| -# 1. Build the app |
89 |
| -# 2. run warmup-remotejson script |
90 |
| -# 3. run precompute-pageinfo script |
91 |
| -# Dependencies |
92 |
| -COPY --chown=node:node --from=all_deps $APP_HOME/node_modules $APP_HOME/node_modules |
93 |
| -# Content with merged early-access content |
94 |
| -COPY --chown=node:node --from=base $APP_HOME/data ./data |
95 |
| -COPY --chown=node:node --from=base $APP_HOME/assets ./assets |
96 |
| -COPY --chown=node:node --from=base $APP_HOME/content ./content |
97 | 75 | # Source code
|
98 |
| -COPY --chown=node:node --from=all_deps $APP_HOME/package.json ./ |
99 |
| -COPY src ./src |
100 |
| -COPY next.config.js ./ |
101 |
| -COPY tsconfig.json ./ |
102 |
| - |
103 |
| -# 1. Build |
104 |
| -RUN npm run build |
105 |
| - |
106 |
| -# 2. Warm up the remotejson cache |
107 |
| -RUN npm run warmup-remotejson |
108 |
| - |
109 |
| -# 3. Precompute the pageinfo cache |
110 |
| -RUN npm run precompute-pageinfo -- --max-versions 2 |
111 |
| - |
112 |
| -# Prune deps for prod image |
113 |
| -RUN npm prune --production |
114 |
| - |
115 |
| -# -------------------------------------------------------------------------------- |
116 |
| -# PRODUCTION IMAGE |
117 |
| -# -------------------------------------------------------------------------------- |
| 76 | +COPY --chown=node:node src src/ |
| 77 | +COPY --chown=node:node package.json ./ |
| 78 | +COPY --chown=node:node next.config.js ./ |
| 79 | +COPY --chown=node:node tsconfig.json ./ |
| 80 | + |
| 81 | +# From the clones stage |
| 82 | +COPY --chown=node:node --from=clones $APP_HOME/data data/ |
| 83 | +COPY --chown=node:node --from=clones $APP_HOME/assets assets/ |
| 84 | +COPY --chown=node:node --from=clones $APP_HOME/content content/ |
| 85 | +COPY --chown=node:node --from=clones $APP_HOME/translations translations/ |
| 86 | + |
| 87 | +# From the dependencies stage |
| 88 | +COPY --chown=node:node --from=dependencies $APP_HOME/node_modules node_modules/ |
| 89 | + |
| 90 | +# Generate build files |
| 91 | +RUN npm run build \ |
| 92 | + && npm run warmup-remotejson \ |
| 93 | + && npm run precompute-pageinfo -- --max-versions 2 \ |
| 94 | + && npm prune --production |
| 95 | + |
| 96 | +# ------------------------------------------------- |
| 97 | +# PRODUCTION STAGE: What will run on the containers |
| 98 | +# ------------------------------------------------- |
118 | 99 | FROM base AS production
|
119 |
| - |
120 |
| -ARG APP_HOME=/home/node/app |
121 |
| -USER node |
| 100 | +USER node:node |
122 | 101 | WORKDIR $APP_HOME
|
123 | 102 |
|
124 |
| -# Copy the content with merged early-access content |
125 |
| -COPY --chown=node:node --from=base $APP_HOME/data ./data |
126 |
| -COPY --chown=node:node --from=base $APP_HOME/assets ./assets |
127 |
| -COPY --chown=node:node --from=base $APP_HOME/content ./content |
128 |
| - |
129 |
| -# Include cloned translations |
130 |
| -COPY --chown=node:node --from=base $APP_HOME/translations ./translations |
131 |
| - |
132 |
| -# Copy prod dependencies |
133 |
| -COPY --chown=node:node --from=builder $APP_HOME/package.json ./ |
134 |
| -COPY --chown=node:node --from=builder $APP_HOME/node_modules $APP_HOME/node_modules |
135 |
| - |
136 |
| -# Copy built artifacts needed at runtime for the server |
137 |
| -COPY --chown=node:node --from=builder $APP_HOME/.next $APP_HOME/.next |
| 103 | +# Source code |
| 104 | +COPY --chown=node:node src src/ |
| 105 | +COPY --chown=node:node package.json ./ |
| 106 | +COPY --chown=node:node next.config.js ./ |
| 107 | +COPY --chown=node:node tsconfig.json ./ |
138 | 108 |
|
139 |
| -# Copy cache files generated during build scripts |
140 |
| -COPY --chown=node:node --from=builder $APP_HOME/.remotejson-cache ./.remotejson-cache |
141 |
| -COPY --chown=node:node --from=builder $APP_HOME/.pageinfo-cache.json.br* ./.pageinfo-cache.json.br |
| 109 | +# From clones stage |
| 110 | +COPY --chown=node:node --from=clones $APP_HOME/data data/ |
| 111 | +COPY --chown=node:node --from=clones $APP_HOME/assets assets/ |
| 112 | +COPY --chown=node:node --from=clones $APP_HOME/content content/ |
| 113 | +COPY --chown=node:node --from=clones $APP_HOME/translations translations/ |
142 | 114 |
|
143 |
| -# Copy only what's needed to run the server |
144 |
| -COPY --chown=node:node --from=builder $APP_HOME/src ./src |
145 |
| -COPY --chown=node:node --from=builder $APP_HOME/.remotejson-cache ./.remotejson-cache |
146 |
| -COPY --chown=node:node --from=builder $APP_HOME/.pageinfo-cache.json.br* ./.pageinfo-cache.json.br |
147 |
| -COPY --chown=node:node --from=builder $APP_HOME/next.config.js ./ |
148 |
| -COPY --chown=node:node --from=builder $APP_HOME/tsconfig.json ./ |
| 115 | +# From dependencies stage (*modified in build stage) |
| 116 | +COPY --chown=node:node --from=build $APP_HOME/node_modules node_modules/ |
149 | 117 |
|
150 |
| -# - - - |
151 |
| -# Environment variables are set in the Moda |
152 |
| -# configuration: config/moda/configuration/*/env.yaml |
153 |
| -# - - - |
| 118 | +# From build stage |
| 119 | +COPY --chown=node:node --from=build $APP_HOME/.next .next/ |
| 120 | +COPY --chown=node:node --from=build $APP_HOME/.remotejson-cache ./ |
| 121 | +COPY --chown=node:node --from=build $APP_HOME/.pageinfo-cache.json.br* ./ |
154 | 122 |
|
155 | 123 | # This makes it possible to set `--build-arg BUILD_SHA=abc123`
|
156 | 124 | # and it then becomes available as an environment variable in the docker run.
|
157 | 125 | ARG BUILD_SHA
|
158 | 126 | ENV BUILD_SHA=$BUILD_SHA
|
159 | 127 |
|
160 | 128 | # Entrypoint to start the server
|
161 |
| -# Note: Currently we have to use tsx because we have a mix of `.ts` and `.js` files with multiple import patterns |
| 129 | +# Note: Currently we have to use tsx because |
| 130 | +# we have a mix of `.ts` and `.js` files with multiple import patterns |
162 | 131 | CMD ["node_modules/.bin/tsx", "src/frame/server.ts"]
|
0 commit comments