temperDocs
Menu

Guides

Build a template from a Dockerfile

Turn a Dockerfile into template build steps with the temper CLI or the TypeScript SDK, and see which instructions map to what.

The template API takes build steps, not Dockerfiles (see Templates). The TypeScript SDK translates a Dockerfile into those steps, reading COPY sources from your build context, and it ships a temper command that does the translation and the build in one go. You can use it from any language: the translation is plain JSON.

An example Dockerfile

dockerfile
FROM temper/default

ARG NODE_MAJOR=22
ENV APP_HOME=/srv/app
WORKDIR $APP_HOME

RUN apt-get update \
 && apt-get install -y --no-install-recommends ffmpeg poppler-utils \
 && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \
 && apt-get install -y nodejs

COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --chmod=755 bin/ bin/
COPY src/ src/
ENV PATH=/srv/app/bin:$PATH

CMD ["node", "src/server.js"]

FROM temper/default is the platform's runtime (Debian 13 with systemd). To build on another template of yours, write FROM temper/<name>:<version>, for example FROM temper/base:3.

deb.nodesource.com isn't one of the registries builds can reach by default, so add it to your developer-level egress allowlist before building this one.

Build it with the CLI

The temper command comes with the TypeScript SDK. It reads TEMPER_API_KEY and TEMPER_BASE_URL like the SDKs do.

bash
npm install --save-dev @temper-hq/sdk

# Print the steps the Dockerfile translates to (warnings go to stderr)
npx temper template translate -f Dockerfile

# Translate, start the build and follow its log until it is ready or failed
npx temper template build web-tools 1 -f Dockerfile --context . --build-arg NODE_MAJOR=22

npx temper template list
npx temper template get web-tools 1
npx temper template delete web-tools 1

The Dockerfile defaults to ./Dockerfile and the build context to the Dockerfile's directory. The context's .dockerignore is honoured, so node_modules and the like stay out of COPY.

Build it from code

In Node, templateFromDockerfile (from @temper-hq/sdk/node) reads the Dockerfile and its context and returns { from, steps, start, warnings }, ready for templates.createVersion.

TypeScript
import { Temper } from "@temper-hq/sdk";
import { templateFromDockerfile } from "@temper-hq/sdk/node";

const temper = new Temper();
const t = await templateFromDockerfile("./Dockerfile", { buildArgs: { NODE_MAJOR: "22" } });
for (const w of t.warnings) console.warn(w);
await temper.templates.createVersion("web-tools", "1", { from: t.from, steps: t.steps, start: t.start });
await temper.templates.waitForVersion("web-tools", "1", { onPoll: (v) => console.log(v.state) });

Outside Node, translateDockerfile(text, { context }) does the same with any source of files; memoryContext({ "app.py": "..." }) builds a context from strings or bytes.

From Python or Go, translate with the CLI and pass the JSON to the SDK:

import json
import subprocess

from temper_hq import Temper

t = json.loads(subprocess.run(
    ["npx", "temper", "template", "translate", "-f", "Dockerfile"], check=True, capture_output=True, text=True
).stdout)
with Temper() as temper:
    temper.templates.create_version("web-tools", "1", steps=t["steps"], from_=t["from"], start=t["start"])
    temper.templates.wait_for_version("web-tools", "1")
out, err := exec.Command("npx", "temper", "template", "translate", "-f", "Dockerfile").Output()
if err != nil {
	log.Fatal(err)
}
var t struct {
	From  *string               `json:"from"`
	Steps []temper.TemplateStep `json:"steps"`
	Start *temper.TemplateStart `json:"start"`
}
if err := json.Unmarshal(out, &t); err != nil {
	log.Fatal(err)
}
p := temper.CreateTemplateVersionParams{Steps: t.Steps, Start: t.Start}
if t.From != nil {
	p.From = *t.From
}
_, err = client.Templates.CreateVersion(ctx, "web-tools", "1", p)

How instructions translate

InstructionBecomes
FROM temper/defaultNo base: the platform's runtime.
FROM temper/<name>:<version>from: "<name>:<version>".
RUN (shell form, exec form, heredocs)A run step. Values of ARGs in scope are exported at the start of the command.
COPY, ADD (local files)One copy step per file. Text stays text; binary files are sent as base64. File permissions are kept; --chmod overrides them.
COPY --chown=...The copy steps, then a run step with chown.
ENVAn env step. $VAR and ${VAR} are expanded, with $PATH standing for the runtime's default PATH.
ARGIts value (from --build-arg or the default) is used for expansion and exported in later RUN steps; it isn't kept in the template.
WORKDIRA workdir step. Relative paths resolve against the previous one.
CMD, ENTRYPOINTstart. An exec-form ENTRYPOINT and CMD are combined into one argv, as Docker does.
EXPOSE, HEALTHCHECK, STOPSIGNAL, VOLUME, LABEL, MAINTAINERIgnored, with a warning.

These fail the translation with an error that names the line:

  • Any other FROM. Install what a base image would provide with RUN steps instead.
  • Multi-stage builds: a second FROM, or COPY --from. Build artifacts beforehand and COPY them in, or build them in a RUN step.
  • USER other than root. Environments have a single user, agent, which runs your agent and the start command; build steps run as root.
  • ADD of a URL or a git repository (use RUN curl ...), and ADD of a local archive, which Docker would unpack (use COPY and RUN tar -x).
  • SHELL (steps always use /bin/sh -c), ONBUILD, and RUN --mount.
  • More than 4 MiB of files from COPY and ADD together. Download large files in a RUN step.

A $PATH in a Dockerfile that builds on another template expands to the runtime's default PATH, not the base template's; the translation warns when that happens.