Docker
Inject varsafe secrets into containers at runtime — entrypoint wrapping, compose interpolation, and build-time mounts that never bake secrets into image layers.
The goal: containers receive secrets in their process environment at runtime, and nothing sensitive ever lands in an image layer, a build argument, or a committed file. The only credential a container needs is a single team-scoped API token.
Build Time vs Runtime
This distinction drives every pattern on this page:
- Runtime — the container fetches or receives secrets when it starts. Rotating a secret in varsafe takes effect on the next restart, with no rebuild. This is the default for application secrets.
- Build time — a secret is needed while building the image (private registry credentials, license keys). Use BuildKit secret mounts, never
ARG/ENV.
varsafe as the Entrypoint (recommended)
Install the CLI in the image and make varsafe run the entrypoint wrapper — secrets exist only in the application process environment, fetched fresh at every container start. This is a tested configuration:
# Runtime secret injection in a container: varsafe wraps the entrypoint,
# so secrets exist only in the process environment — never in a layer,
# build arg, or file.
FROM oven/bun:1-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fsSL https://varsafe.dev/install.sh | bash
ENV PATH="/root/.varsafe/bin:${PATH}"
WORKDIR /app
COPY . .
RUN bun install --production
# VARSAFE_TOKEN is provided at runtime (docker run -e VARSAFE_TOKEN=...)
ENTRYPOINT ["varsafe", "run", "-i", "your-project-id", "-e", "production", "--"]
CMD ["bun", "src/index.ts"]Run it with the token as the only secret you handle yourself:
docker run -e VARSAFE_TOKEN=vs_at_... my-image
Or with compose, taking the token from the host environment (also a tested configuration):
# Runtime injection with Docker Compose: the token comes from the host
# environment; application secrets never appear in this file.
services:
app:
build: .
environment:
VARSAFE_TOKEN: ${VARSAFE_TOKEN}
restart: unless-stopped
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 30s
timeout: 5s
retries: 3Rotate secrets in varsafe and containers pick them up on the next restart — no rebuild, no redeploy.
Inject from the Host Environment
Keep the CLI out of the image entirely: inject secrets into the shell that starts the containers, and let compose interpolate them.
varsafe run -- docker compose up
services:
app:
image: my-image
environment:
- DATABASE_URL=${DATABASE_URL}
- API_KEY=${API_KEY}
Compose resolves ${VAR} from the environment varsafe run created. Filter what gets injected with --include:
varsafe run --include 'DB_*,API_*' -- docker compose up
With docker run, list each variable to pass through (a bare -e KEY with no value forwards it from the host):
varsafe run -- docker run -e DATABASE_URL -e API_KEY my-image
Export to env_file
When a file is unavoidable, export one — and keep it out of git and off persistent disk where possible.
With docker run, use --format docker (unquoted KEY=value), since docker run --env-file treats quotes as literal characters:
varsafe export -f docker -o .env.production
docker run --env-file .env.production my-image
With compose, prefer a plaintext export to RAM-backed tmpfs, consumed and deleted in one command chain:
varsafe export --plain --tmpfs -o app.env
docker compose --env-file /dev/shm/app.env up -d
rm -f /dev/shm/app.env
Pipe via stdin
Pass secrets into docker run without any file at all:
varsafe export -f docker | docker run -i --env-file /dev/stdin my-image
Build-Time Secrets
For secrets needed during docker build, use BuildKit secret mounts — the secret is available to the single RUN instruction and never written to a layer:
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=env,target=/tmp/.env \
export $(cat /tmp/.env | xargs) && npm run build
varsafe export -f docker | docker build --secret id=env,src=/dev/stdin .
CI/CD
In a pipeline, store the API token as a masked CI variable and combine it with any pattern above — the full walkthrough, with tested GitLab CI and GitHub Actions configurations, is in CI/CD Pipelines.
Verify
docker run -e VARSAFE_TOKEN=vs_at_... my-image env | grep -c '='inside a shell entrypoint shows the injected variables arrived (avoid printing the values themselves)docker history my-imagemust show no secret values in any layer- Rotate a test secret in the dashboard, restart the container, and confirm the new value is live