Skip to main content

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:

deployments/production.json
{
"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}, ...

ParameterDefaultDescription
baseImagenode:22-slimBase image, scratch for none
imageRepository to push to, without tag; required to push
tags["${git.version}"]Tags of the image, invalid characters are replaced by -
workdir/appFolder 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
labelsLabels added to the default OCI ones
userthe base image oneUser 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
mtimeSOURCE_DATE_EPOCH, or 0Modification time of the files and creation time of the image
credentialsCredentials per registry host, override ~/.docker/config.json
insecureRegistriesRegistries 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), 0 by 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_modules comes 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​