Managing secrets with private Docker images
Log in to add to favouritesPage last updated 06 February 2026
What are environment variables?
Environment variables are key-value pairs provided by the operating system that influence how applications run. They’re often used to configure paths, enable features, or inject sensitive information like API keys or tokens without hardcoding them into source code.
For example, the following commands print the PATH variable in different environments:
echo $PATH # macOS/Linux
echo $env:PATH # PowerShell
echo %PATH% # Windows CMD
node -e "console.log(process.env.PATH)" # Node.jsHow environment variables are used in development
In the JavaScript ecosystem, environment variables are often defined in a file named .env (or some variation) stored in the project root. This file contains values that are loaded at runtime and made available via process.env in Node.js:
# .env
PRIVATE_API_KEY=abc123node -e "console.log(process.env.PRIVATE_API_KEY)"These variables aren’t available by default - they need to be loaded explicitly in code (or implicitly through build tooling) using packages like dotenv.
Even for client-side web apps, Node.js is commonly used during the build step. Many frameworks (such as Next.js, Vite, or Webpack) allow you to expose specific variables to the frontend by prefixing them or using special loaders, plugins, imports, or comments. This means it's possible-intentionally or accidentally-to embed sensitive variables into client-side bundles.
What is a secret?
Environment variables themselves are not always sensitive. Many are harmless configuration flags or tokens used in low-risk contexts.
However, some variables-like API keys, credentials, or private tokens-must be treated as secrets. These values must never be committed to source control or exposed to the public.
For example, in Contensis:
- ✅ Access Token - used to fetch public content from the Delivery API; safe and often exposed in frontend code.
- 🛑 Role-based API Key (Client ID + Secret) - used to manage content; must be kept private and should never be exposed to the client.
The distinction is simple: if exposing a variable can result in unauthorized access or damage, it’s a secret.
Secrets in CI/CD pipelines
In a CI/CD environment like GitHub Actions, secrets are used to safely pass sensitive values into your builds and deployments without storing them in code.
GitHub provides a secure Secrets section in repository settings where you can define these variables. Once stored, their values:
- Can be injected as environment variables in workflows.
- Are write-only - you won’t be able to read them later, only update them.
- Are automatically redacted from CI/CD pipeline logs.
Use this mechanism to store secrets like:
- Private registry credentials
- API keys
- Authentication tokens
Bridging local .env Files and CI/CD secrets
During local development, developers often rely on .env files to load secrets as configuration variables. However, in CI/CD pipelines, these secrets must come from the CI provider’s secret management system (such as GitHub Secrets).
Our aim is to recreate the same environment securely in your pipeline - by injecting secrets into the build process without ever hardcoding them or exposing them in client-facing code.
Using GitHub secrets in CI builds and Docker images
Once your secrets are safely added to your repository, you can inject them into your CI workflows as environment variables or Docker build arguments, depending on how your application expects them.
Adding GitHub secrets to the repository
GitHub provides a secure way to manage sensitive values in each repository under Settings → Secrets and variables → Actions.
Add all required secrets here before continuing.


Injecting secrets via .env files in CI
If your application already relies on a .env file (or some variation), you can generate this file during your CI workflow using the secrets stored in GitHub.
Create a .env with secrets:
- name: Generate local .env file containing secrets
run: |
cat <<EOF > .env
PRIVATE_ENV_VAR=${{ secrets.PRIVATE_ENV_VAR }}
PRIVATE_CLIENT_ID=${{ secrets.PRIVATE_CLIENT_ID }}
PRIVATE_CLIENT_SECRET=${{ secrets.PRIVATE_CLIENT_SECRET }}
EOFOr append secrets to an existing .env file:
- name: Append secrets to local .env file
run: |
echo "PRIVATE_ENV_VAR=${{ secrets.PRIVATE_ENV_VAR }}" >> .env
echo "PRIVATE_CLIENT_ID=${{ secrets.PRIVATE_CLIENT_ID }}" >> .env
echo "PRIVATE_CLIENT_SECRET=${{ secrets.PRIVATE_CLIENT_SECRET }}" >> .envOnce generated, the .env file will be available to any build step that runs in the same job. It will be discarded automatically once the CI job completes.
Passing secrets to Docker as build arguments
If your application needs secrets to be available at Docker build time (to supply in RUN steps for example), you can pass them using the --build-arg flag.
Update your Dockerfile to define build arguments:
ARG PRIVATE_AUTH_SECRET
ARG PRIVATE_CLIENT_ID
ARG PRIVATE_CLIENT_SECRET
ENV PRIVATE_AUTH_SECRET=$PRIVATE_AUTH_SECRET
ENV PRIVATE_CLIENT_ID=$PRIVATE_CLIENT_ID
ENV PRIVATE_CLIENT_SECRET=$PRIVATE_CLIENT_SECRETUpdate your GitHub Actions workflow to pass those secrets:
- name: Build container image
env:
APP_BUILD_IMAGE: ${{ env.APP_IMAGE }}:build-${{ github.run_number }}
APP_LATEST_IMAGE: ${{ env.APP_IMAGE }}:latest
run: |
docker build \
--build-arg PRIVATE_AUTH_SECRET=${{ secrets.PRIVATE_AUTH_SECRET }} \
--build-arg PRIVATE_CLIENT_ID=${{ secrets.PRIVATE_CLIENT_ID }} \
--build-arg PRIVATE_CLIENT_SECRET=${{ secrets.PRIVATE_CLIENT_SECRET }} \
-t ${{ env.APP_BUILD_IMAGE }} \
-t ${{ env.APP_LATEST_IMAGE }} .Next Steps: Push to Registry and Deploy
After building the container image, you should push it to your container registry. Ensure that your registry is not publicly accessible, as public images can be pulled by anyone.
In GitHub, container images appear under Packages in your repository and are labelled as either Public or Private.
Deploying to Contensis Blocks
If you're deploying to Contensis Blocks, you'll need to:
- Add your private registry credentials to Contensis.
- Integrate the Contensis Block Push GitHub Action into your CI workflow to deploy the image.
You can include the push step as part of your existing build job or as a separate deployment job.
Developer Tips
- Never include private secrets in code that is served to the client - for example, in components that are rendered on both the server and client side
- Restrict the use of private secrets to server-side operations only
- Never commit private secrets to source control, unless explicitly agreed upon for a specific, secured use case.
- Be aware that build tools may inline
process.env.*or special global variables during build time, which can expose secret values in final client-side bundles if used incorrectly. - Avoid pushing container images to public registries if they include private secrets, as this creates a significant risk of data leakage
- Understand that leaks can happen - if a secret is exposed, immediately revoke any public access, inform your team, and rotate any affected keys or credentials as a top priority.