Concept

Secret Injection: How It Works

Secret injection is the process of delivering credentials to your application at the moment it needs them — without storing those credentials in source code, configuration files, or build artifacts. The goal is simple: your application gets the secrets it needs, and nothing else on the system can access them.

What Is Secret Injection?

Every application needs credentials to connect to databases, call APIs, sign tokens, and decrypt data. The question is how those credentials get from wherever they are stored into the running process.

Secret injection is the act of populating your application's environment with these values at startup time. Instead of reading credentials from a file on disk, the application receives them through its process environment — the same process.env interface your code already uses, but populated from a secure source rather than a plaintext file.

Your application code stays the same
// Whether secrets come from a .env file or runtime injection,
// your code reads them the same way
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.STRIPE_SECRET_KEY;
const jwtSecret = process.env.JWT_SECRET;

The difference is in how those values got into process.env. A .env file requires plaintext on disk. Runtime injection does not.

Methods of Secret Injection

There are four common approaches to getting secrets into a running application. Each has different security properties and operational tradeoffs:

MethodHow It WorksTradeoffs
Environment variables (.env file)A plaintext file is read at startup by a library like dotenv. Values are parsed and set on process.env.Simple to set up. Insecure — plaintext on disk, easy to commit, no access control.
File mountingSecrets are written to a file (often in a tmpfs or volume mount) and the application reads the file at startup.Used by Kubernetes Secrets and Docker Swarm. The file still exists on the file system, even if it’s in memory-backed storage.
Sidecar / init containerA separate process fetches secrets from a vault and either writes them to a shared volume or exposes them via a local API.Common in Kubernetes with Vault Agent. Adds infrastructure complexity. The sidecar itself needs credentials to authenticate.
Runtime CLI injectionA CLI tool fetches secrets from encrypted storage and spawns your application as a child process with secrets set in the environment.No file on disk. No sidecar. Secrets exist only in the child process’s memory. Requires the CLI to be installed.

The Problem with File-Based Approaches

Whether it's a .env file, a mounted Kubernetes Secret, or a file written by a sidecar — any approach that puts secrets in a file shares the same fundamental weaknesses:

  • 1.Secrets persist on disk. Even after your application reads them, the file remains. Any process with file system access can read it — including malicious dependencies, debugging tools, and AI coding agents that index your project directory.
  • 2.Stale files accumulate. Old .env.backup, .env.production, and .env.old files pile up over time. Each one is an unencrypted copy of your credentials that may not match your gitignore rules.
  • 3.Permission management is coarse. File permissions operate at the user/group/other level. You cannot restrict access to individual secrets within a file, and you cannot audit which process read which value.
  • 4.Rotation requires file updates. When a secret changes, every file that contains it must be updated. In a team environment, this means notifying every developer to pull a new file or restart their sidecar.

Zero-Disk Injection with secr

secr run takes a different approach. Instead of writing secrets to a file, it fetches them from encrypted storage, sets them as environment variables, and spawns your application as a child process. The secrets exist only in the child process's memory — never on the file system.

Here is what happens when you run secr run -- npm start:

  1. The CLI reads your .secr.json config to determine the project, organization, and environment.
  2. It authenticates with the secr API using your stored session token.
  3. It fetches the encrypted secrets for the target environment.
  4. Secrets are decrypted in memory on the client side.
  5. The CLI spawns npm start as a child process with the decrypted values set as environment variables.
  6. Your application reads process.env as usual. No file is written. No dotenv library is needed.
Terminal
# That's it. One command replaces your entire .env workflow.
secr run -- npm start

# Loaded 18 secrets → server running on :3000

Works with Every Language and Framework

Because secr run injects secrets as standard environment variables, it works with any language, runtime, or framework that reads from the process environment. No SDK or library integration required.

Node.js

Terminal
# Express, Fastify, Next.js, Hono — any Node.js application
secr run -- node server.js
secr run -- npm start
secr run -- next dev
secr run -- tsx watch src/index.ts

Python

Terminal
# Django, Flask, FastAPI — any Python application
secr run -- python app.py
secr run -- uvicorn main:app --reload
secr run -- gunicorn wsgi:application
secr run -- python manage.py runserver

Go

Terminal
# Any Go application that reads os.Getenv()
secr run -- go run main.go
secr run -- ./myapp
secr run -- air  # live reload

Ruby, PHP, Rust, and everything else

Terminal
# Ruby / Rails
secr run -- rails server
secr run -- bundle exec puma

# PHP / Laravel
secr run -- php artisan serve

# Rust
secr run -- cargo run

# Any command that reads environment variables
secr run -- your-command-here

You can also specify the environment explicitly:

Terminal
# Development (default)
secr run -- npm start

# Staging
secr run --env staging -- npm test

# Production
secr run --env production -- node dist/server.js

Injection in CI/CD

The same injection pattern works in continuous integration and deployment pipelines. secr provides purpose-built integrations for the most common platforms:

GitHub Actions

The secr composite action pulls secrets and exports them as environment variables for the duration of the job. Values are automatically masked in logs.

Vercel

The @secr/vercel package runs before next build and writes secrets to .env.local where the framework expects them. No manual env var configuration in the Vercel dashboard.

Netlify

The @secr/netlify-plugin injects secrets into process.env during the onPreBuild phase. Your build step receives all the credentials it needs.

Docker

Use secr run as your Docker CMD or ENTRYPOINT to inject secrets at container startup. Never bake credentials into an image layer.

.github/workflows/deploy.yml
steps:
  - uses: actions/checkout@v4

  - name: Inject secrets
    uses: secr-dev/secr-action@v1
    with:
      token: ${{ secrets.SECR_TOKEN }}
      org: my-org
      project: my-app
      environment: production

  - run: npm ci
  - run: npm run build
  - run: npm test

Start injecting secrets securely

npm i -g @secr/cli

secr login

secr init

secr run -- npm start