Run in a container
Copy penv into your image from ghcr.io/penvhq/penv, pass a credential at run time, handle the per-start read, or ship an encrypted bundle instead.
A container has no keychain, so penv keeps no local copy and reads from us on every start. See local and cloud modes.
Image
FROM node:22-slim
COPY --from=ghcr.io/penvhq/penv:1.0.0 /penv /usr/local/bin/penv
WORKDIR /app
COPY . .
ENTRYPOINT ["penv", "run", "--"]
CMD ["node", "server.js"]ghcr.io/penvhq/penv (since 1.0.0-beta.2) is a scratch image holding the release-signed static binary at /penv, amd64 and arm64, with penv's own TLS roots. A prerelease is tagged with its version and next only.
Installer route: install the CLI (PENV_ALLOW_ROOT=1 under docker build).
Credential
docker run --rm -e PENV_TOKEN="$PENV_TOKEN" my-app:latestPENV_TOKEN takes precedence over every other credential source. Issue a pck_ credential in the console, or avoid a long-lived one:
| Host | Credential source |
|---|---|
| Presents a platform token | Put it in PENV_OIDC_TOKEN. See Kubernetes. |
| Proves nothing | Use PENV_TOKEN: enrollment needs an OS keychain. |
| ECS task role, EKS IRSA, EKS Pod Identity | penv 1.0.0-beta.3 and later sign your workspace id, and we accept the exchange; 1.0.0-beta.2 does not, so use PENV_TOKEN there. See authenticate an AWS workload. |
Never bake a credential or a .env into an image layer. Anyone who can pull your image can read every layer, and deleting a file in a later layer does not remove it from an earlier one.
Network at start
If penv cannot reach us, the start stops with exit code 5; your program never starts on stale values.
Point the readiness probe at the application so the orchestrator retries a failed start. An exchanged credential lives 15 minutes; penv run reads once and the child runs indefinitely.
TLS-inspecting proxy
docker run --rm -v /etc/ssl/certs/ca-certificates.crt:/ca.pem:ro -e SSL_CERT_FILE=/ca.pem my-app:latestSince 1.0.0-beta.2, SSL_CERT_FILE replaces penv's built-in roots with the PEM bundle it names. In an agent session penv refuses a bundle you can write.
Encrypted bundle
penv bundle --env production # writes .penv/production.bundle, prints PENV_BUNDLE_KEY=<hex> once
docker run --rm -e PENV_BUNDLE_KEY="$PENV_BUNDLE_KEY" my-app:latestpenv bundle (since 1.0.0-beta.2) seals one environment into a file you ship in the image. With PENV_BUNDLE_KEY set, penv run opens it instead of reading from us. A missing or unopenable bundle exits 3 (no_bundle, bundle_unreadable). A rotated value needs a new bundle and image.
File-only tools
penv pullpenv pull writes a plain .env at mode 0600, adds it to .gitignore, and skips dynamic values. Under a coding agent it exits 2.
penv pull inside docker build writes values into an image layer. Run it at container start, or in a build step whose output is not the image.
Setup
Copy /penv from ghcr.io/penvhq/penv into your image.
Set the entrypoint to penv run --.
Give the host an identity to exchange, or issue a pck_ credential in the console.
Pass it as PENV_TOKEN at run time.
Start the container and read stderr. Exit code 5 means penv could not reach us or found no credential.
Deploy to Vercel
Two Vercel paths: your build authenticates to us as its project via OIDC and runs penv, or we export values into the project's environment for functions.
Kubernetes
Authenticate your pod as its service account: bind a trust, project a token with your workspace id as audience, and hand it to penv run.