Skip to main content
orun integrates with SOPS to let you commit encrypted secrets alongside your manifests without exposing sensitive values in your repository. The age private key lives only on the node — it is never stored in the manifest repository. When the node applies manifests, orun decrypts secrets at runtime using the local key and passes plaintext values to containers as environment variables.

How secrets work

orun supports two ways to use encrypted secrets:
  • Whole-file: Create a secrets.yaml file in the root of your manifest repository, encrypt the entire file with SOPS, and commit the encrypted result. orun decrypts the file at apply time and makes the key-value pairs available as environment variables to your deployments.
  • Inline: Place individual ENC[AES256_GCM,...] values directly in a Deployment’s spec.env map. orun detects these patterns and decrypts them in place before starting the container.
At apply time, orun runs a two-phase decryption process:
  1. If secrets.yaml exists in the repository root, orun decrypts it first to cache the SOPS data key.
  2. orun walks every Deployment’s env map and decrypts any inline ENC[...] values using the cached data key.

Key file location

orun resolves the age private key from the following sources, in priority order:
  1. The path passed via the --age-key-path flag to orun start.
  2. The SOPS_AGE_KEY_FILE environment variable on the node.
  3. The default path: /opt/orun/keys/age.key.
Place your age private key at /opt/orun/keys/age.key on each node, or set SOPS_AGE_KEY_FILE to a custom path.

Setup steps

1

Generate an age key pair

Install age and generate a key pair on a secure machine.
age-keygen -o age.key
The public key is printed to stdout. Keep the private key (age.key) secure — you will copy it to each node.
2

Add the public key to your SOPS config

Create or update .sops.yaml in the root of your manifest repository with the public key from the previous step.
creation_rules:
  - age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
3

Create and encrypt your secrets file

Create a secrets.yaml file with your plaintext secrets:
database_password: supersecret
api_key: abc123
Encrypt it with SOPS using your age public key:
sops --encrypt --age age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p secrets.yaml > secrets.yaml.enc
Rename the output to secrets.yaml and delete the plaintext file before committing.
4

Commit the encrypted file

Commit the SOPS-encrypted secrets.yaml to your manifest repository. The file is safe to store in version control — without the age private key, the contents cannot be decrypted.
git add secrets.yaml .sops.yaml
git commit -m "Add encrypted secrets"
git push
5

Copy the private key to each node

Copy the age private key to each node at the default path or a custom path you configure.
ssh root@203.0.113.10 "mkdir -p /opt/orun/keys"
scp age.key root@203.0.113.10:/opt/orun/keys/age.key
Set restrictive permissions so only the orun process can read the key:
ssh root@203.0.113.10 "chmod 600 /opt/orun/keys/age.key"

Inline encrypted values

Instead of a whole-file secrets.yaml, you can place SOPS-encrypted values directly in a Deployment’s env map. Generate an encrypted value with SOPS and paste the ENC[...] string as the field value:
apiVersion: run.orcra.dev/v0alpha
kind: Deployment
metadata:
  name: my-app
spec:
  image: my-app:latest
  env:
    DATABASE_PASSWORD: ENC[AES256_GCM,data:abc123...,tag:xyz...,type:str]
    LOG_FORMAT: json
orun decrypts ENC[...] values before passing them to the container. Plain values like LOG_FORMAT: json are passed through unchanged.
Inline decryption requires the data key to be cached first. orun loads it by decrypting secrets.yaml in phase one. If secrets.yaml is absent and no data key is cached from a previous cycle, inline ENC[...] values cannot be decrypted.

Nested secrets and key flattening

If your secrets.yaml uses nested YAML structures, orun flattens nested keys into dot-separated paths when injecting them as environment variables. For example:
database:
  password: supersecret
  host: db.internal
Becomes the environment variables database.password and database.host.

Reference

ConstantValueDescription
DefaultAgeKeyPath/opt/orun/keys/age.keyDefault path orun looks for the age private key on the node
AgeKeyFileEnvSOPS_AGE_KEY_FILEEnvironment variable that overrides the default key path
Never commit unencrypted secrets to your manifest repository. If the age private key is absent or unreadable on the node, orun logs a warning and leaves encrypted values in place. Containers that receive raw ENC[...] strings as environment variable values will behave unexpectedly.