Storing Binary Secrets in Environment Variables

Environment variables only hold text strings, but Base64 encoding lets you store binary secrets like TLS keys and certificates as env var values. Learn the pattern and risks.

ZERO UPLOAD · ALL LOCAL
  1. Select DECODE (default) to paste Base64 and get the original text, or switch to ENCODE to convert text to Base64.
  2. For text: type or paste into the text area — the result appears instantly.
  3. For files: drop a file onto the drop zone or click it to browse — any file up to 500 MB works.
  4. Use Copy to grab the output, or Download as .txt to save long Base64 strings.
  5. Note: Base64 is encoding, not encryption. Anyone can decode it with no key.

What this page covers

  • Linux single-line encode base64 -w 0 cert.pem, since GNU base64 wraps by default
  • macOS single-line encode base64 cert.pem, since macOS base64 does not wrap by default
  • Portable newline strip pipe through tr -d '\n' when -w isn't available (BSD base64 lacks it)
  • Cleanup pattern trap 'rm -f /tmp/cert.pem' EXIT ensures a decoded temp file is removed even on script failure
INPUT
FILE INPUT

Drop a file here

or click to select a file · any format · max 500 MB

OUTPUT

Storing Binary Secrets in Environment Variables

When a deployment platform only accepts strings, Base64 lets binary secrets survive the handoff.

TLS certificates, ECDSA private keys, AES key files, and PKCS#12 bundles are binary , they cannot be stored in an environment variable directly without corruption.

Base64 encoding converts binary secrets to a string that survives environment variable assignment, shell expansion, and process spawning intact. The consuming application decodes the value at startup and writes it to a temporary file or passes it directly to the crypto library.

How binary secrets are stored in env vars

The pattern for storing binary secrets in environment variables has three steps: Base64-encode the binary file, assign the encoded string to an environment variable, and decode it in the application at startup before use. This approach is necessary because environment variables are text-only and cannot hold raw binary data, so any binary secret must be encoded before it can be injected through the environment.

Producing a single-line secret value

For a TLS certificate: base64 -w 0 cert.pem > cert.b64, then set TLS_CERT=$(cat cert.b64) in the deployment environment. The -w 0 flag on Linux prevents line wrapping, because environment variables cannot contain newline characters reliably across all shells and process managers.1 Building on this, some deployment platforms (AWS Lambda, Docker, Kubernetes) accept Base64 environment variables natively and decode them before passing to the process. A common mistake is to use the base64 command without checking whether it supports -w, since BSD base64 on macOS does not recognise that flag. A portable alternative is to pipe through tr -d '\n' to strip newlines from the output of any base64 implementation, which produces a single-line string on both GNU and BSD systems without requiring platform-specific flags.

This matters because a wrapped secret fails in the most confusing way possible: the assignment succeeds but truncates at the first newline, so the decoded certificate is half a PEM and every connection that depends on it fails at startup with no obvious error. The symptom looks like a key mismatch or a permission problem, which sends engineers down the wrong debugging path entirely and wastes hours before someone inspects the actual bytes.

Common pitfalls and variants

Newlines break environment variable handling in most shells and process managers because the assignment syntax treats a newline as the end of the value. A multi-line PEM certificate encoded with standard wrapping (76 characters per line) contains embedded newlines that cause shell assignment errors. Use base64 -w 0 (Linux) or base64 (macOS, which does not wrap by default) to produce a single-line string.

Furthermore, some CI/CD systems mask environment variables that contain line breaks, silently delivering an empty string to the process. Test by printing the first 20 characters of the decoded value after startup , do not log the full secret. Another subtle issue is that some shells interpret special characters in the encoded string during variable assignment. Wrapping the value in single quotes prevents shell expansion of characters like $ and ! that can appear in the Base64 alphabet, ensuring the assigned value matches the encoded output exactly.2

Security and best practice

Base64 in environment variables is a transport convenience, not a security measure. Any process that can read the environment (ps aux on Linux, or any process running under the same user) can read the encoded secret and decode it instantly. This means the encoding provides zero confidentiality, and the only real protection comes from restricting access to the environment of the target service to only the processes that require it.3

Restricting access to encoded values

Restrict environment variable access by running sensitive services under dedicated users with minimal permissions. For production workloads, prefer secrets managers (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager) that inject secrets at runtime without storing them in the environment at all. Rotate secrets on a schedule; treat Base64-encoded secrets as equivalent in sensitivity to the plaintext binary they encode. The moment you store a Base64-encoded TLS private key in an environment variable, any process that can read the environment block, inspect /proc on Linux, or call Get-Process on Windows can extract and decode it in milliseconds, which is why production workloads should inject secrets through a dedicated secrets manager rather than through environment variables. This equivalence means that a Base64-encoded connection string containing a database password is just as sensitive as the plaintext connection string, and it should be protected with the same access controls and rotation policies.

CI/CD platform patterns for Base64 secret injection

On GitHub Actions, store the single-line Base64 string as an encrypted secret under repository Settings. Reference it in a workflow step with ${{ secrets.TLS_CERT_B64 }} and decode it in the step body: echo "${{ secrets.TLS_CERT_B64 }}" | base64 -d > cert.pem. GitHub Actions masks the encoded value in log output, but the decoded content still appears if you print it directly. Decode into a file and pass the file path to downstream commands; avoid logging the decoded bytes.

Keeping platform masking reliable

On GitLab CI, the masked variable feature requires the variable value to match a specific character set. Multi-line PEM content fails the masking requirement because it contains newline characters. Base64 encoding the PEM produces a single-line string that GitLab can mask reliably. In CircleCI, context variables follow the same pattern: store the encoded single-line value, decode in the job step, and reference the decoded file path in subsequent commands rather than passing the decoded value through environment variables.

Startup decoding versus use-time decoding

Decoding a Base64 environment variable at application startup and writing the result to a temporary file makes the secret available to libraries that expect a file path as input. The pattern is common for TLS certificates: decode $TLS_CERT to /tmp/cert.pem during the container init command, pass the file path to your web server configuration, then delete the file after the server loads it into memory. The trap 'rm -f /tmp/cert.pem' EXIT shell construct ensures cleanup even when the startup script fails partway through.4

Decoding at the moment of use, by calling the decode function each time the application needs the secret, keeps no temporary file on disk at rest. When you just need to confirm what a stored environment value decodes to before wiring up either pattern, decode it locally without writing to disk to inspect the actual secret. For most secrets, startup decoding is simpler because certificate-loading APIs across languages accept a file path as input. For secrets that must never touch the filesystem, in-memory decoding at use time is the correct choice and avoids the window of vulnerability between writing and deleting the temporary file.

When to use this

Use Base64 environment variables for binary secrets in container-based deployments where a secrets manager is unavailable, in CI/CD pipelines for TLS certificates and signing keys, and for lightweight local development. Use a dedicated secrets manager in production.5

Examples

Store a TLS certificate as an environment variable

Before
# cert.pem contains multi-line PEM content
After
# Encode without line wrapping (Linux)
export TLS_CERT=$(base64 -w 0 cert.pem)

# Decode at startup in Python
import base64, os
cert_pem = base64.b64decode(os.environ['TLS_CERT'])

On macOS, use base64 cert.pem , macOS base64 does not wrap by default.

Inject a binary secret in a Docker Compose file

Before
services:
  app:
    image: myapp
After
services:
  app:
    image: myapp
    environment:
      - TLS_KEY=${TLS_KEY_BASE64}
    command: sh -c 'echo $TLS_KEY | base64 -d > /tmp/key.pem && exec myapp'

Avoid writing secrets to the container filesystem in production , use tmpfs or in-memory files.

Sources
  1. 1.

    GNU Core Utilities, "base64-invocation," gnu.org, accessed June 2026. https://www.gnu.org/software/coreutils/manual/html_node/base64-invocation.html

  2. 2.

    GNU Bash Reference Manual, "Single Quotes," gnu.org, accessed June 2026. https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html

  3. 3.

    "Protecting process's envvars from exposure," unix.stackexchange.com, accessed June 2026. https://unix.stackexchange.com/questions/176853/protecting-processs-envvars-from-exposure

  4. 4.

    Michael Kerrisk, "trap(1p) - Linux manual page," man7.org, accessed June 2026. https://man7.org/linux/man-pages/man1/trap.1p.html

  5. 5.

    "Secrets Management," OWASP Cheat Sheet Series, cheatsheetseries.owasp.org, accessed June 2026. https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html

FAQ