# Docker Deployment

Build a runtime artifact, then turn it into a container image:

```bash
alepha build --runtime node
alepha image --tag
```

`manifest.json` requires `manifestVersion: 2`. Rebuild an older artifact before deploying it. Lore, Bay and other artifact consumers must accept this major; additive fields do not change it.

`alepha image` reads `manifest.json` from `build.output.dist` (default `dist/`) and packages the first declared runtime slice. Building needs an image name: `image.tag` in the config (see below), unless `--tag` is a complete `name:tag`. A workerd or static primary slice is refused because it cannot start a container process. The docker CLI is required; this command belongs to a local machine or CI runner.

## Dockerfile ownership

With no Dockerfile in the app directory, Alepha generates one beside `alepha.config.ts`. Commit and edit that file. An existing Dockerfile is reused unchanged, with a warning if the manifest facts recorded in its header have moved. The build context is `dist/`, not the app directory.

```bash
alepha image --dockerfile         # generate without building an image
alepha image --tag=1.3.4          # configured name, version 1.3.4
alepha image --tag=myorg/app:v2   # complete tag override
```

| `--tag` value        | Image                    |
| -------------------- | ------------------------ |
| `--tag`, no value    | `<image.tag>:latest`     |
| `--tag=1.3.4`        | `<image.tag>:1.3.4`      |
| `--tag=:1.3.4`       | `<image.tag>:1.3.4`      |
| `--tag=myorg/app:v2` | `myorg/app:v2`, verbatim |

Every form but the last needs `image.tag` in the config.

The artifact has an `index.<runtime>.js` wrapper, `server/<runtime>/` chunks, `public/` assets, its manifest and package metadata. Migrations are copied into the image context when present. There is no generated Dockerfile inside `dist/`.

## Configuration

Image settings are top-level `image`, separate from `build`:

```typescript filename=alepha.config.ts
import { defineConfig } from "alepha/cli/config";

export default defineConfig({
  build: { runtime: ["node"] },
  image: {
    env: {
      DATA_DIR: "/data",
      DATABASE_URL: "sqlite:///data/app.db",
    },
    volumes: ["/data"],
    tag: "ghcr.io/myorg/myapp",
    oci: true,
    labels: {
      source: "https://github.com/myorg/myapp",
      licenses: "MIT",
    },
  },
});
```

| Setting         | Meaning                                                  |
| --------------- | -------------------------------------------------------- |
| `image.from`    | Base image (default `node:24-alpine`, `oven/bun:alpine`) |
| `image.command` | Server command (default `node` or `bun`)                 |
| `image.install` | Extra packages for the generated standard image          |
| `image.env`     | Baked defaults, overridden by runtime environment values |
| `image.volumes` | Volume mount points                                      |
| `image.user`    | Container user override (standard default: uid 1000)     |
| `image.tag`     | Default image name                                       |
| `image.args`    | Docker build arguments as a string array                 |
| `image.oci`     | Write the OCI labels below                               |
| `image.labels`  | `source`, `title`, `description`, `licenses`             |

With `oci: true`, each `image.labels` field becomes an `org.opencontainers.image.*` `LABEL` line in the generated Dockerfile (`source` is what links a GHCR package to its repository), and `alepha image` adds `revision` (the git commit), `created` and `version` (from the tag) at build time. Without `oci`, `image.labels` is ignored. The generated Dockerfile always labels the image `dev.alepha.runtime`, naming the slice it runs.

The generated standard image uses a Node or Bun base and installs runtime dependencies only when the artifact declares them. Each declared volume is prepared before its `VOLUME` line. Named volumes inherit those permissions; bind mounts retain the host directory's permissions. Baked values are public image defaults, so deliver secrets at runtime.

## Compiled image

```bash
alepha build --runtime bun
alepha image --compile --tag
```

This compiles the Bun slice first, then packages the binary on `gcr.io/distroless/cc-debian12` by default. `--compile my-app` names the binary. The base determines the Linux Bun target triple: a Bun binary needs libc and supporting libraries, so `scratch` and `distroless/static` are refused. A compiled image has no default user; set `image.user` when needed. Runtime dependencies must be bundled, and `image.install` is ignored in this mode.

For a binary without an image, use [alepha compile](https://latest.alepha.dev/docs/cli-commands-compile.md). For the complete image contract, see [alepha image](https://latest.alepha.dev/docs/cli-commands-image.md).

## Running

```bash
docker run -p 3000:3000 --env-file .env.production ghcr.io/myorg/myapp:latest
```

The generated image sets `SERVER_HOST=0.0.0.0`; supply `SERVER_PORT` to change the listening port. Both standard and compiled apps serve their client assets and apply the artifact's `_headers` file. See [Static File Headers](https://latest.alepha.dev/docs/guides-deployment-headers.md).

Container images are an artifact format. [alepha deploy](https://latest.alepha.dev/docs/cli-plugins-infra.md) drives configured infrastructure adapters; it does not turn a Docker image into an implicit provider deployment.
