Migrating from variables to secrets
Move sensitive values out of variables blocks and into Loom's secrets system so credentials never appear in workflow YAML, compiled output, receipts, or logs.
Time estimate: 15–30 minutes for a typical workflow.
Who should use this guide
You have an existing Loom workflow that stores sensitive values (passwords, tokens, keys) as plain-text variables. You want to migrate those values to the secrets system for automatic redaction, file-first injection, and provider-backed resolution.
If you are setting up secrets for the first time with no existing variables to migrate, start with Getting started with secrets instead.
What changes
| Aspect | variables (before) | secrets (after) |
|---|---|---|
| Value in workflow YAML | Plain text | Provider reference URI only |
Visible in loom compile output | Yes | No |
| Visible in receipts and logs | Yes | Redacted to [REDACTED:SECRET_<NAME>] |
| Default injection mode | Direct env var | Temp file path (file: true) |
| Provider dependency | None | 1Password, KeePass, or environment passthrough |
What stays the same: job structure, script blocks, stage assignments, and non-sensitive variables are unchanged. You are only moving the sensitive keys.
Prerequisites
- Loom CLI installed (
loom --versionreturns a version). - An existing workflow with sensitive values in
variablesblocks. - Access to a secrets provider: 1Password vault, KeePass database, or host environment variables.
Step 1: Identify sensitive variables
Search your workflow YAML for values that would cause damage if leaked:
- Passwords and database connection strings
- API tokens and bearer tokens
- Signing keys and private keys
- Webhook URLs containing embedded secrets
Before — credential hardcoded in variables:
deploy:
stage: ci
target: linux
variables:
DATABASE_PASSWORD: "s3cret_p@ssw0rd"
APP_ENV: "production"
script:
- ./scripts/deploy.sh
DATABASE_PASSWORD is the migration target. APP_ENV is non-sensitive and stays in variables.
Step 2: Choose a provider
| Provider | Best for | URI scheme | Auth requirement |
|---|---|---|---|
| 1Password | Teams with centralized vault management | op://<vault>/<item>/<field> | OP_SERVICE_ACCOUNT_TOKEN env var |
| KeePass | Local or offline encrypted storage | keepass://<alias>#<path>:<field> | LOOM_KEEPASS_DB_* env vars |
| Environment | CI runners that already inject secrets | env://<VAR_NAME> | Variable exported in shell |
For detailed provider setup, see the provider-specific guides:
Step 3: Store values in the provider
Move each sensitive value into your chosen provider before updating the workflow. The ref URI must point to a real entry.
1Password:
export TOKEN_VALUE="s3cret_p@ssw0rd"
loom secrets op item create \
--vault Engineering \
--item-path services/loom/deploy \
--field password \
--value-from-env TOKEN_VALUE
KeePass:
export TOKEN_VALUE="s3cret_p@ssw0rd"
loom secrets keepass item create \
--item-path services/loom/deploy \
--field password \
--value-from-env TOKEN_VALUE
Environment passthrough (env://): export the variable in your shell or CI runner configuration. No storage step needed — the ref reads directly from the host environment.
Step 4: Update workflow YAML
Move each sensitive key from variables to a secrets block. Replace the plain value with a ref URI pointing to the provider entry you created.
After — credential referenced through a provider:
deploy:
stage: ci
target: linux
variables:
APP_ENV: "production"
secrets:
DATABASE_PASSWORD:
ref: op://Engineering/services/loom/deploy/password
script:
- ./scripts/deploy.sh
A key cannot appear in both variables and secrets on the same job. Remove the key from variables before adding it to secrets.
Step 5: Update scripts for file injection
The default injection mode is file: true. After migration, the environment variable holds a file path instead of the raw value. Update any script that reads the variable directly:
# Before (direct value from variables)
curl -u "admin:${DATABASE_PASSWORD}" https://db.example.com/health
# After (file-injected secret — read with cat)
curl -u "admin:$(cat "$DATABASE_PASSWORD")" https://db.example.com/health
If your tooling requires the raw value in the environment variable and cannot read from a file, set file: false:
secrets:
NPM_TOKEN:
ref: env://NPM_TOKEN
file: false
Tradeoff: direct injection (file: false) is more exposed to shell tracing (set -x). If CI_DEBUG_TRACE=true is set and any secret uses file: false, Loom hard-fails with SECRETS_UNSAFE_DEBUG_TRACE.
Step 6: Validate and run
loom check
loom run --local --workflow .loom/workflow.yml
loom check catches schema violations before execution: key collisions between variables and secrets, invalid ref URIs, and naming errors.
During the run, confirm:
- The job starts and the secret resolves without
SECRETS_*errors. - Console output shows the file path (e.g.
/tmp/loom-secret-DATABASE_PASSWORD-0), not the raw value. - Any accidental exposure of the secret value is redacted to
[REDACTED:SECRET_DATABASE_PASSWORD].
Common pitfalls
| Mistake | Symptom | Fix |
|---|---|---|
Same key in variables and secrets | Schema validation error at loom check | Remove the key from variables |
Script reads $VAR directly after migration | Script receives a file path instead of a value | Use cat "$VAR" or set file: false |
| Provider auth not configured | SECRETS_PROVIDER_UNAVAILABLE at runtime | Export OP_SERVICE_ACCOUNT_TOKEN or LOOM_KEEPASS_DB_* env vars |
Typo in ref URI | SECRETS_REF_INVALID or SECRETS_REF_NOT_FOUND | Verify URI against loom secrets op item list or loom secrets keepass item list |
CI_DEBUG_TRACE=true with file: false | SECRETS_UNSAFE_DEBUG_TRACE | Disable debug trace or switch secret to file: true |
Migration checklist
- Sensitive values identified in workflow YAML
- Values stored in a secrets provider (1Password, KeePass, or env)
-
variablesentries replaced withsecretsentries andrefURIs - Scripts updated to handle file-injected values (
cat "$VAR") -
loom checkpasses -
loom run --localsucceeds with secrets resolved and output redacted - No plain-text credentials remain in workflow files
What to read next
- Getting started with secrets — end-to-end setup for new secrets from scratch.
- Workflows secrets overview — YAML shape, spec fields, and validation rules.
- Secrets overview — injection modes, error codes, and provider reference.
- Secrets security — threat model, controls, and incident response.