oci
Build and push OCI container images of a Webda application without Docker.
The Webda/ContainerDeployer adds one layer to a base image: the packaged application (the package files, its
production dependencies and the deployed configuration). The layer is reproducible, so the same sources give the same
image digest on any machine.
Installation
pnpm add -D @webda/oci
Usage
Declare the deployer as a unit of a deployment:
{
"units": [
{
"name": "Image",
"type": "Webda/ContainerDeployer",
"baseImage": "node:22-slim@sha256:<digest>",
"image": "ghcr.io/my-org/my-app"
}
]
}
Then run:
webda -d production container build # build into an OCI layout (or a .tar archive)
webda -d production container push # build and push to the registry
webda -d production deploy # same as container push, as part of the deployment
Parameters
Every string accepts the deployment templates: ${package.version}, ${git.commit}, ${deployment},
${deployer.name}, ${env.MY_VAR}, ...
| Parameter | Default | Description |
|---|---|---|
baseImage | node:22-slim | Base image, scratch for none |
image | Repository to push to, without tag; required to push | |
tags | ["${git.version}"] | Tags of the image, invalid characters are replaced by - |
workdir | /app | Folder of the application in the image |
entrypoint | ["node", "<workdir>/node_modules/@webda/core/lib/bin/cli.js"] | Entrypoint of the image |
cmd | ["serve", "--bind", "0.0.0.0"] | Arguments of the entrypoint |
env | { "NODE_ENV": "production" } | Environment variables |
labels | Labels added to the default OCI ones | |
user | the base image one | User the container runs as |
exposedPorts | ["18080/tcp"] | Exposed ports |
platforms | ["linux/amd64"] | Platforms to build, several produce a multi-platform index |
output | .webda/oci/${deployer.name} | OCI layout folder, or a tar archive when ending with .tar |
mtime | SOURCE_DATE_EPOCH, or 0 | Modification time of the files and creation time of the image |
credentials | Credentials per registry host, override ~/.docker/config.json | |
insecureRegistries | Registries reached over plain http (localhost always is) |
Registry credentials
Credentials come from ~/.docker/config.json (including credential helpers), unless the credentials parameter sets
them for a host. Read them from the environment with templates, or commit them encrypted with webda config encrypt:
{
"credentials": {
"ghcr.io": { "username": "${env.GHCR_USER}", "password": "${env.GHCR_TOKEN}" }
}
}
Reproducible images
- every file of the layer has the same owner, permissions (only the executable bit is kept) and modification time, and the files are sorted;
- the creation date and modification time are
SOURCE_DATE_EPOCH(in seconds),0by default; - the base image must be pinned by digest for the image to stay the same over time: the build warns when it is not;
node_modulescomes from your lockfile: install with--frozen-lockfile.
Library
The building blocks are exported for custom tooling: buildImage, pushImage, writeLayoutArchive, ImageLayout,
RegistryClient, parseImageReference, generateTar, ...
See also
Classes
Interfaces
- AuthChallenge
- BuildImageOptions
- BuiltImage
- BuiltPlatformImage
- Descriptor
- FetchedManifest
- HistoryEntry
- ImageConfiguration
- ImageIndex
- ImageManifest
- ImageReference
- PlainRegistryCredentials
- Platform
- RegistryClientOptions
- RegistryCredentials
- RegistryOptions
- TarEntry
- TarOptions
Variables
Functions
- buildImage
- comparePaths
- createImageConfiguration
- createRegistryClient
- forceUnixGzipOs
- formatImageReference
- formatPlatform
- generateTar
- getHelperCredentials
- getRegistryApiHost
- isIndex
- isPinnedBaseImage
- isValidTag
- mergeEnv
- parseAuthChallenge
- parseImageReference
- parsePlatform
- paxRecord
- platformMatches
- pushImage
- readDockerConfig
- resolveCredentials
- sha256
- toJsonBytes
- toOciMediaType
- toTag
- withDirectories
- writeLayoutArchive