Docker deployments
Build container images with a committed lockfile, isolated credentials, and portable workspace output.
Use a frozen install for a single package. For a workspace application, use lpm deploy to create a portable production directory.
docker build -t acme-api .Both recipes require a committed lpm.lock.
The examples pin LPM CLI to 0.78.0. Choose an approved base-image digest and matching runtime version for production.
Exclude credentials from the build context
Create this file before the first build:
.git
**/node_modules
**/.lpm
**/.env*
**/.npmrc
**/.netrc
**/_netrc
**/.ssh
**/*.key
**/*.pem
.lpm-migrate-manifest.json
**/*.backupAdd any other plaintext secret exports your project uses. If the application ships a public certificate or example environment file, review the exclusions.
A Docker secret mount keeps credentials out of COPY. Build commands and lifecycle scripts can still read a mounted credential during that step.
Build a single-package image
This example runs an existing server.js without a compilation step:
# syntax=docker/dockerfile:1.10
FROM node:22-bookworm-slim
RUN npm install --global @lpm-registry/cli@0.78.0
WORKDIR /app
COPY lpm.lock ./
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
--mount=type=secret,id=lpm_token,env=LPM_TOKEN \
lpm fetch
COPY . .
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
--mount=type=secret,id=lpm_token,env=LPM_TOKEN \
lpm install --offline --frozen-lockfile --prod
CMD ["node", "server.js"]The lockfile-only fetch layer can survive ordinary source changes. The source enters before installation so root lifecycle scripts and local dependencies have their files.
Secret mounts are optional for public dependencies. For a protected build:
# npm-compatible registry credentials
docker build --secret id=npmrc,src=.npmrc -t acme-api .
# LPM.dev Registry token already present in the shell environment
docker build --secret id=lpm_token,env=LPM_TOKEN -t acme-api .Do not put credentials in Docker ARG or ENV instructions. Those values can enter build metadata or image layers.
Match the target platform
Without --platform, lpm fetch selects the build environment's platform. For an explicit target:
lpm fetch --platform linux/x64/glibc
lpm fetch --platform linux/x64/musl
lpm fetch --platform linux/arm64/muslMatch the architecture and libc to the runtime image. Debian uses glibc. Alpine uses musl.
Local file: and link: dependencies need their source directories before installation. Fetch does not download those local sources.
Keep the package store available
Installed links can refer to the package store. In the single-stage example, the store remains in the image alongside node_modules.
A temporary BuildKit cache mount at /root/.lpm/store disappears after its RUN step. Do not rely on that mount for runtime links.
For a smaller runtime image, use a self-contained framework output or the workspace deployment pattern below. Copying only node_modules can leave links broken.
--offline controls package-network work. Root lifecycle scripts still run and can access the network unless Docker restricts it separately.
Build a workspace application
Prepare the application and its local dependencies before deployment:
# syntax=docker/dockerfile:1.10
FROM node:22-bookworm-slim AS build
RUN npm install --global @lpm-registry/cli@0.78.0
WORKDIR /repo
COPY . .
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
--mount=type=secret,id=lpm_token,env=LPM_TOKEN \
lpm ci
RUN lpm run build --filter 'api...'
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
--mount=type=secret,id=lpm_token,env=LPM_TOKEN \
lpm deploy /prod/api --filter api
FROM node:22-bookworm-slim AS runtime
WORKDIR /app
COPY --from=build /prod/api /app
CMD ["node", "dist/server.js"]Replace api, the build command, and the entrypoint with your application values. The member's files field must include the built output.
The output directory must sit outside the workspace. The filter must select exactly one member.
Deployment includes local workspace dependencies, node_modules, a deployment-local store, and a lockfile. Internal links remain usable after the directory moves on Unix.
The runtime image does not need LPM CLI unless application commands use it. Build native dependencies for the same operating system, architecture, libc, and runtime ABI.
--prod is the default selection. --dev selects development dependencies instead of production dependencies. Use --no-optional to omit optional dependencies.
Control deployment contents
For copied member and workspace-dependency sources, deployment selects files through package.json > files, otherwise .npmignore, otherwise .gitignore.
These source copies exclude package-manager state, VCS files, dotenv files, and .npmrc, .netrc, and _netrc credential files. Credential names match regardless of letter case.
A files allowlist does not override these exclusions. The exclusions do not filter files inside installed registry packages or other local directory dependencies.
Review the generated directory before publishing the image. Deployment does not identify every possible filename that can contain a secret.
Troubleshooting
| Failure | Recovery |
|---|---|
| Offline package is absent | Fetch online for the target platform and retain the resulting store. |
| Root lifecycle script cannot find a file | Copy the required project files before installation. |
| Runtime dependency link is broken | Keep the referenced store or use a self-contained deployment directory. |
| Native dependency cannot load | Match the build and runtime platform and Node ABI. |
| Deployment filter matches zero or several packages | Preview and narrow it with lpm filter. |
| Built files are absent | Run the build and include its output in the member's files list. |
See also
- lpm fetch — populate the store
- lpm install — frozen and offline installation
- Monorepo setup — workspace tasks and deployment
- CI/CD setup — credentials and caches