|
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