# Self-hosted Next.js standalone build in Docker, Croct env vars behave differently than on Vercel

Asked by Marek Dvorak on 2025-05-21. Tags: nextjs, self-hosted, docker, setup.

We self-host everything, no PaaS. Next.js with `output: 'standalone'` in Docker.

The app ID comes up undefined in the browser even though the container
definitely has `NEXT_PUBLIC_CROCT_APP_ID` set. I checked inside the running
container, the variable is there.

It works if I bake the value into the image at build time, which I dislike
because now the image is environment-specific. Why does the runtime variable get
ignored, and is there a way around baking it in

## 2 answers

### Answer from Omar Haddad (2025-05-21)

This is Next.js behavior, not Croct. Anything prefixed `NEXT_PUBLIC_` is inlined
into the client bundle when `next build` runs. By the time your container
starts, the value (or lack of it) is already frozen in the JavaScript files.
Setting it at runtime does nothing for the browser bundle, which is exactly
what you observed.

So the variable must be present at build time. If you want one image per
environment, pass it as a build arg:

```dockerfile
ARG NEXT_PUBLIC_CROCT_APP_ID
ENV NEXT_PUBLIC_CROCT_APP_ID=$NEXT_PUBLIC_CROCT_APP_ID
RUN npm run build
```

The important split: `CROCT_API_KEY` is different. It is read server-side at
runtime, never ships to the browser, and can stay a proper container secret
injected at deploy time. Only the public app ID has the build-time constraint.

### Answer from Hannah Weiss (2025-05-22)

Worth noting the environment-specific image is less of a problem here than it
feels. The app ID is public by design, it is visible in the browser bundle
anyway, so baking it in leaks nothing. If each Croct environment (dev/prod) has
its own application ID, one image per environment actually maps cleanly to
that model.

Keep `CROCT_API_KEY` out of the image though. It needs the "Issue user tokens"
permission and should live in your secret store, injected at runtime like Omar
showed.

#### Reply from Marek Dvorak (2025-05-22)

Fine, I can live with build args for a value that is public anyway. API key
stays in the secret store. Case closed.
