> ## Documentation Index
> Fetch the complete documentation index at: https://unkey.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Unkey is two separate products. Compute builds, deploys, and runs apps behind a gateway. API Management issues API keys, enforces rate limits, manages identities and permissions, and reports usage. Say which product a page belongs to; a reader can use either without the other.
> Every Unkey API endpoint is an HTTP POST to https://api.unkey.com/v2/{service}.{procedure} with a root key in the Authorization: Bearer header. Root keys are workspace scoped.
> Error codes have the form err:{system}:{category}:{specific} and each has a page at /errors/{system}/{category}/{specific}.
> The word environment means production or preview in Compute. Rate limiting has four meanings on this site; the glossary lists them.

# Build-time secrets

> Use your environment variables during a build without baking them into the image.

Your build can read every [environment variable](/docs/compute/configure/environment-variables) of the <Tooltip tip="A production or preview environment of a Compute app, not the dashboard label on a key.">environment</Tooltip> you're deploying, and none of them end up in the image. Automatic builds need no setup. Dockerfile builds need one extra line in each `RUN` step that uses them.

This is safer than Docker's `ARG` and `ENV`, which are saved in the image history where anyone with the image can read them.

## Automatic builds

When the environment builds without a Dockerfile, every variable is set as an environment variable of the same name for your install and build commands, the way it would be in CI. Change a value and the next deployment rebuilds the steps that use it.

## Dockerfile builds

In a Dockerfile build, your variables arrive as a `.env` file. Mount it in each `RUN` step that needs it:

```dockerfile Dockerfile theme={"system"}
FROM node:lts-alpine AS builder
ARG UNKEY_SECRETS_ID
WORKDIR /app
COPY . .
RUN npm ci
RUN --mount=type=secret,id=${UNKEY_SECRETS_ID},target=/run/secrets/.env \
    set -a && . /run/secrets/.env && set +a && \
    npm run build

FROM node:lts-alpine
WORKDIR /app
COPY --from=builder /app .
CMD ["node", "dist/index.js"]
```

* **Declare `ARG UNKEY_SECRETS_ID` in every stage that mounts the file.** Docker doesn't carry `ARG` values across `FROM` lines.
* **`set -a && . /run/secrets/.env && set +a`** loads the variables for that one `RUN` step only. The file isn't saved in the layer.
* **Changing a variable reruns that step** and every step after it in the stage. Steps before it stay cached.

The file uses standard `.env` syntax, one `KEY=value` per line. Values with spaces, quotes, `$`, or newlines are single-quoted, so PEM keys and JSON come through unchanged. If your framework reads `.env` files itself, point it at `/run/secrets/.env` and make sure its parser handles quoted multi-line values.

## Troubleshooting

### A build step keeps using an old value

Older Dockerfiles mount the secret with the fixed id `env`. That still builds, but changing a variable doesn't rerun the step, so the old value can stay cached. Change the mount to `id=${UNKEY_SECRETS_ID}`.

### Cloning a private repository or installing a private package fails

We fetch only the repository being built, with read-only access. A `RUN git clone` of another private repository, a private submodule, or a private package registry isn't authenticated. Add the token it needs as an environment variable and use it in the step that needs it.

## Next steps

<Columns cols={2}>
  <Card title="Environment variables" icon="key" href="/docs/compute/configure/environment-variables">
    Kinds, limits, import, and how values reach running instances.
  </Card>

  <Card title="Dockerfile builds" icon="file-code" href="/docs/compute/build/dockerfile">
    Build context, the runtime contract, and explained errors.
  </Card>
</Columns>
