alepha@docs:~/docs/framework/cli/commands$
cat 10-image.md | pretty
3 min read
Last commit:

#Image Command

Build a container image from ./dist.

#Quick Start

bash
alepha build
alepha image --tag
# → <image.tag>:latest

⚠️ It needs the docker CLI. It shells out to docker build, so it is a local and CI command and cannot run in an in-process build path.

#A Dockerfile you own

No Dockerfile in the app directory: one is generated there, beside alepha.config.ts. One already present: it is used untouched.

⚠️ It lands in the app directory, not in dist/, and that is not a preference: alepha build wipes dist/ on every run, so a file living there could never be committed or edited. The build context is still dist/, so the Dockerfile's COPY . . keeps meaning the built output rather than dragging src/ and node_modules/ in.

The generated header records the manifest facts it came from:

dockerfile
# Generated by `alepha image`, from this build's manifest:
#   runtime: node
#   entry: index.node.js
# It is yours now. Edit it freely; `alepha image` reuses it untouched
# and only warns when the facts above stop matching a later build.

A later run warns when they no longer match, and never fails. The file is yours after generation, so a hard failure would make an intentional edit feel like a bug; the warning exists only so the drift is not silent.

#Which slice it runs

The primary: the first declared runtime, read from the manifest. An image runs one process from one entry point, and this is the same rule every deployer follows.

A static artifact is refused (it runs no process, so there is nothing to start) and so is a workerd one (only Cloudflare runs it).

#A binary and nothing else

bash
alepha image --compile --tag

Compiles the bun slice first and ships an image holding the binary, on gcr.io/distroless/cc-debian12. Measured on a real app: 197 MB against 279 MB, and 49 MB against 67 MB compressed.

#⚠️ The libc, which is where this goes wrong

A Bun --compile binary is not static, under any triple:

txt
$ file dist/app
ELF 64-bit LSB executable, dynamically linked,
interpreter /lib/ld-musl-aarch64.so.1
$ ldd dist/app
Error loading shared library libstdc++.so.6: No such file or directory
Error loading shared library libgcc_s.so.1: No such file or directory

It needs an interpreter, libstdc++ and libgcc. Put it on a base that has none and you do not get a build failure: you get exec /app/app: no such file or directory, naming a file that is plainly there, from a container that simply stops.

So alepha image derives the triple from the base rather than inheriting whatever the host would default to, and refuses a base with no libc (scratch, distroless/static) rather than building one, because no triple fixes it.

The compiled variant stays root: the base has no shell, so a declared volume cannot be prepared at build time.

#Options

Flag Description
--tag, -t --tag uses latest, --tag=1.3.4 that version with the configured name, --tag=other/img:v1 verbatim
--compile, -c Compile the bun slice first and ship an image holding the binary. --compile name names it
--dockerfile Write the Dockerfile and stop, without building an image

#Configuration

image: is a top-level key in alepha.config.ts, not build.docker: config follows the command that reads it.

typescript
 1import { defineConfig } from "alepha/cli/config"; 2  3export default defineConfig({ 4  image: { 5    from: "node:26-alpine", 6    volumes: ["/data"], 7    env: { DATA_DIR: "/data" }, 8    image: { tag: "ghcr.io/myorg/app", oci: true }, 9  },10});

build.cloudflare correctly stays under build, because wrangler.jsonc really is written by the build.