Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions packages/backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
#
# Once the commands have been run, you can build the image using `yarn build-image`

# ---- Builder ----
FROM node:22-bookworm-slim AS build
# From mise.toml
FROM node:22-bookworm-slim

# Set Python interpreter for `node-gyp` to use
ENV PYTHON=/usr/bin/python3
ENV NODE_ENV=production
ENV NODE_OPTIONS="--no-node-snapshot"

RUN groupmod -g 150 node && usermod -u 150 -g 150 node
RUN corepack enable
# Set the owner of the cache directory to node so we can use corepack
RUN mkdir -p /home/node/.cache && chown -R node:node /home/node/.cache

# Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend.
# If sqlite3 is not needed anymore, remove libsqlite3-dev and better-sqlite3.
Expand All @@ -24,17 +28,30 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
apt-get install -y --no-install-recommends python3 g++ build-essential libsqlite3-dev && \
rm -rf /var/lib/apt/lists/*

# Use the least-privileged user even during build
RUN groupmod -g 150 node && usermod -u 150 -g 150 node
RUN corepack enable
RUN mkdir -p /home/node/.cache && chown -R node:node /home/node/.cache

# From here on we use the least-privileged `node` user to run the backend.
USER node

# This should create the app dir as `node`.
# If it is instead created as `root` then the `tar` command below will fail: `can't create directory 'packages/': Permission denied`.
# If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`) so the app dir is correctly created as `node`.
WORKDIR /app

# Copy files needed by Yarn
COPY --chown=node:node .yarn ./.yarn
COPY --chown=node:node .yarnrc.yml .
COPY --chown=node:node backstage.json .
COPY --chown=node:node .yarnrc.yml ./
COPY --chown=node:node backstage.json ./

# This switches many Node.js dependencies to production mode.
ENV NODE_ENV=production

# This disables node snapshot for Node 20 to work with the Scaffolder
# Not sure if needed for Node 22.
ENV NODE_OPTIONS="--no-node-snapshot"

# Copy repo skeleton first, to avoid unnecessary docker cache invalidation.
# The skeleton contains the package.json of each package in the monorepo,
# and along with yarn.lock and the root package.json, that's enough to run yarn install.
COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./
RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz

Expand All @@ -45,13 +62,7 @@ RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid
COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz

# ---- Runtime ----
FROM gcr.io/distroless/nodejs22-debian12

WORKDIR /app
COPY --from=build --chown=nonroot:nonroot /app /app

ENV NODE_ENV=production
ENV NODE_OPTIONS="--no-node-snapshot"
RUN mv packages packages_tmp
RUN mkdir packages

CMD ["packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml", "--config", "app-config.runtime.yaml"]
CMD ["sh", "-c", "cp -r packages_tmp/* packages/ && node packages/backend --config app-config.yaml --config app-config.production.yaml --config app-config.runtime.yaml"]