VI Shipping

Build Images

A deployment runs container images. Toa builds them from the Context: one per composition, and one per extension service that no composition runs. You write no Dockerfile; you say where the images go and, when a component needs something special, what to add to the build.

§Where images go

The Context toa create wrote has a placeholder under registry. Put your registry there:

# context.toa.yaml
registry:
  base: registry.example.com/store

base is prepended to every image name. The core composition of store becomes registry.example.com/store/store/composition-core. registry: registry.example.com/store is shorthand for the same.

docker must be logged in to the registry before toa push or toa deploy.

The registry usually differs per environment:

registry:
  base@local: localhost:5000
  base@production: registry.example.com/store

§Building

$ npx toa build               # build on the local Docker daemon
$ npx toa push production     # build and push
$ npx toa deploy production   # build, push and deploy

toa push and toa deploy skip an image the registry already has. Deploying sources that were already pushed builds nothing.

To see what would be built without building it:

$ npx toa export images ./images   # Dockerfiles and sources, as files
$ npx toa export tags production   # the image reference of every workload

To run the image of a composition on your machine, see toa compose --dock.

§Tags

An image’s tag is eight hexadecimal digits that follow from what the image was built from.

  • The tag changes when a member component changes, and not otherwise. A release that touches one component produces a new image for one composition.
  • The tag carries no environment. The same sources deployed to staging and production are one image, built once. What was tested is what is released.

toa deploy production also moves a second tag, <image>:production, onto the image it deployed, so the registry shows what each environment runs. toa push writes only the first kind.

Beside each image the repository holds a deps-<tag> image with the components’ npm dependencies. It is rebuilt only when dependencies, the base image or the build options change, so a deploy that changes code alone is quick.

Do not name an environment eight hexadecimal digits, or deps- and eight: nothing that cleans the registry could tell such an environment tag from a build’s.

§What goes into an image

  • Each component’s dependencies are installed from its own package.json, in its own directory. Dependencies of the repository root are not in the image.
  • Pin git dependencies to a commit. A dependency named by a moving ref, such as a branch, is installed once and not refreshed until something else changes the dependencies.
  • Sources are copied as files. A directory linked into a component with a symbolic link is copied as what it points to, so components can share source through links. A link the build cannot read fails the build.
  • Heavy optional packages are installed only where a component needs them. A composition whose components declare a storage with the s3 provider gets the AWS SDK; others do not. Your workspace needs the same packages to run locally; toa npm installs them.

§Platforms

registry:
  platforms: [linux/amd64, linux/arm64]

What the images are built for. Without the key, they are built for linux/amd64, linux/arm/v7 and linux/arm64.

platforms: ~ builds only for the machine that builds. It is faster, and it is what a local registry usually wants:

registry:
  platforms: [linux/amd64]
  platforms@local: ~

Building for fewer platforms is the simplest way to shorten a build.

§Private registries

registry:
  credentials: registry-credentials

credentials is the name of a Kubernetes secret holding registry credentials, in the form Kubernetes uses for pulling images. The pods use it as their image pull secret.

Toa does not create this secret. Create it in the namespace yourself; a deploy that does not find it there is refused with the other missing secrets.

§The base image

Every image is built from ghcr.io/toa-io/runtime:<version>, which is published with each Toa release and has the runtime installed. The version is runtime.version of the Context, or the version of the CLI that builds when the Context does not say:

runtime:
  version: 1.0.0

§Your own base image

Name another base for the whole Context:

registry:
  build:
    image: node:24.22.1-alpine3.24
    run: |
      npm i --prefix /toa @toa.io/runtime --omit=dev
      ln -s /toa/node_modules/.bin/toa /usr/local/bin/toa

A base image must provide the toa binary. The stock image has it; one of your own has to install it, as the run lines above do. Install the runtime into /toa.

A single component can ask for a base of its own, in its manifest:

# components/pictures/manifest.toa.yaml
build:
  image: node:24.22.1-bookworm-slim

All components of a composition are built into one image. If they ask for different bases, the composition has to choose with its image key, or the build is refused.

§Adding build steps

pictures needs ffmpeg in its image. run adds commands to the build, one RUN instruction per line:

registry:
  build:
    run: |
      apk add --no-cache ffmpeg
      apk add --no-cache imagemagick

A component can add its own:

# components/pictures/manifest.toa.yaml
build:
  run: apk add --no-cache vips

Both are applied, the Context’s first. Use the Context’s for what every image needs and the component’s for what one component needs.

§Build arguments

arguments passes environment variables of the building machine into the build:

registry:
  build:
    arguments: [NPM_TOKEN]
    run: npm config set //npm.example.com/:_authToken ${NPM_TOKEN}
$ NPM_TOKEN=... npx toa deploy production

Each named variable is available to the run commands and to the dependency install.

A build argument stays in the image. It is readable by anyone who can pull the image and by every container that runs it. Pass only tokens you can afford to expose that way, scoped to reading packages.

§A private npm registry

runtime:
  registry: https://npm.example.com
  proxy: http://proxy.example.com:3128

Every image build installs from this registry, through this proxy. Both are optional and independent.

§Extension service images

The gateway and the other extension services need images too. registry.services says where they come from:

registry:
  services: published
ValueMeaning
buildBuild one image per service into registry.base. The default.
publishedUse the images Toa publishes; build nothing.

published is faster and is the usual choice:

ghcr.io/toa-io/extension-exposition-gateway:<version>
ghcr.io/toa-io/extension-introspection-explorer:<version>
ghcr.io/toa-io/extension-configuration-values:<version>

Before choosing:

  • Published images are pulled from ghcr.io, not from your registry. They are public, and registry.credentials does not apply to them. A cluster that cannot reach ghcr.io needs build.
  • build needs @toa.io/runtime installed on the machine that builds, beside @toa.io/operations. published does not.
  • With build, every Toa upgrade rebuilds every service image.
  • A service that a composition runs is part of that composition’s image. Neither setting applies to it.

The setting can differ per environment:

registry:
  services: published
  services@local: build

§Staying on a supported release

Toa’s published images of old prereleases are removed: a prerelease older than 90 days that is not among the last 30 is deleted, with its service images. Stable releases stay.

An application pinned to a removed prerelease cannot pull published service images and cannot rebuild on that runtime’s base image. Images already in your own registry keep running. Upgrade regularly, or pin a stable release.

§Cleaning your registry

Every deploy of changed sources adds images. Nothing removes them for you. When you set up retention in your registry, keep:

  • every image an environment tag (:production, :staging) currently points at;
  • the last few build tags, which are what you can roll back to;
  • the deps- images those depend on.

How far back you can roll is exactly what retention leaves.

§Every registry key

KeyDefaultMeaning
baserequiredregistry and path prefix for images
platformslinux/amd64, linux/arm/v7, linux/arm64platforms to build for; ~ for the builder’s own
credentialsnonename of the image pull secret
servicesbuildbuild or published
build.imagethe Toa runtime imagebase image
build.runnoneextra build commands, one per line
build.argumentsnoneenvironment variables passed to the build

And of runtime: version, registry, proxy. In a component’s manifest: build.image and build.run.

Search
Search the documentation