Using Secrets with Deployed Workflows

When your workflow is deployed, it cannot access your local .env file or environment variables. Instead, secrets must be stored in the Vault DON—a decentralized, secure secret storage system that your deployed workflows can access at runtime.

This guide explains how to manage secrets for deployed workflows using the cre secrets CLI commands.

Prerequisites

Before managing secrets for deployed workflows, ensure you have:

  1. CRE CLI installed: See the Installation Guide
  2. Authentication: You must be logged in with cre login
  3. Owner address configured: Your workflow-owner-address must be set in your project configuration

How secrets work with deployed workflows

The workflow is similar to local development, but with a critical difference in where secrets are stored:

  1. Declare: Define secret identifiers in a YAML file
  2. Store: Push secrets to the Vault DON using cre secrets create
  3. Use: Your deployed workflow accesses secrets from the Vault using runtime.GetSecret()

Key difference from simulation:

  • Local simulation: Secrets read from your environment variables or .env file on your machine
  • Deployed workflows: Secrets retrieved from Vault DON by the workflow

Step-by-step guide

Step 1: Create a secrets YAML file

Create a YAML file at the root of your project that declares the secrets you want to store.

Example production-secrets.yaml:

secretsNames:
  API_KEY:
    - API_KEY_VALUE

  DATABASE_URL:
    - DATABASE_URL_VALUE

Structure:

  • secretsNames — Top-level key containing all secrets
  • Each secret has:
    • Key (e.g., API_KEY) — The identifier your workflow code will use
    • Value — An array containing the environment variable name that holds the actual value

Step 2: Provide secret values as environment variables

Set the actual secret values as environment variables. These can be provided in two ways:

Option A: Export in your shell

export API_KEY_VALUE="your-actual-api-key"
export DATABASE_URL_VALUE="postgresql://user:pass@host:5432/db"

Option B: Use a .env file

Create a .env file (or add to your existing one):

# .env
API_KEY_VALUE=your-actual-api-key
DATABASE_URL_VALUE=postgresql://user:pass@host:5432/db

The cre CLI will automatically load variables from .env when you run the commands.

Step 3: Upload secrets to the Vault DON

Use the cre secrets create command to upload your secrets to the Vault:

cre secrets create production-secrets.yaml --target production-settings

What happens:

  1. The CLI reads your YAML file and environment variables
  2. It registers the request onchain (for authorization)
  3. It submits the secrets to the Vault DON
  4. The secrets are stored securely and associated with your owner address

Example output:

{"level":"info","owner":"<your-owner-address>","digest":"041eb7a8...","time":"2025-10-22T00:14:56+02:00","message":"IsRequestAllowlisted query succeeded"}
{"level":"info","digest":"041eb7a8...","deadline":"2025-10-23T22:14:56Z","time":"2025-10-22T00:14:59+02:00","message":"AllowlistRequest submitted"}
Digest allowlisted; proceeding to gateway POST
Secret created: secret_id=API_KEY, owner=<your-owner-address>, namespace=main
Secret created: secret_id=DATABASE_URL, owner=<your-owner-address>, namespace=main

Step 4: Use secrets in your workflow code

Your workflow code uses the same API to access secrets, whether running in local simulation or deployed to a workflow DON. The CRE runtime automatically retrieves secrets from the appropriate source.

Fetching Secrets (TypeScript)
1 import { cre, type Runtime } from "@chainlink/cre-sdk"
2 ​
3 const onCronTrigger = (runtime: Runtime<Config>): string => {
4 // Fetch the secret from the Vault DON (uses default "main" namespace)
5 const secret = runtime.getSecret({ id: "API_KEY" }).result()
6 const apiKey = secret.value
7 ​
8 runtime.log(`Using API key: ${apiKey.substring(0, 4)}...`)
9 ​
10 // Use the secret in your workflow logic
11 // ...
12 ​
13 return "Success"
14 }

Important:

  • The secret identifier ("API_KEY") must match what you declared in your YAML file
  • Secrets are fetched at runtime from the Vault DON
  • The namespace parameter is optional—defaults to "main" if omitted
  • The same code works for both simulation (reads from .env) and production (reads from Vault)

Step 5: Verify secrets are stored

You can list all secrets stored in the Vault for your owner address:

cre secrets list --target production-settings

Example output:

{"level":"info","owner":"<your-owner-address>","digest":"225d8b6f...","time":"2025-10-22T19:10:12-05:00","message":"IsRequestAllowlisted query succeeded"}
{"level":"info","digest":"225d8b6f...","deadline":"2025-10-25T00:10:12Z","time":"2025-10-22T19:10:16-05:00","message":"AllowlistRequest submitted"}

Digest allowlisted; proceeding to gateway POST: owner=<your-owner-address>, requestID=f9148fcb-3e4e-45bf-bbde-2124ddd577e4, digest=0x225d8b6f...
Secret identifier: secret_id=API_KEY, owner=<your-owner-address>, namespace=main
Secret identifier: secret_id=DATABASE_URL, owner=<your-owner-address>, namespace=main

Managing secrets lifecycle

Updating secrets

To update existing secrets, use the cre secrets update command:

# Update your environment variable with the new value
export API_KEY_VALUE="new-api-key-value"

# Update the secret in the Vault
cre secrets update production-secrets.yaml --target production-settings

Example output:

{"level":"info","owner":"<your-owner-address>","digest":"10854ac2...","time":"2025-10-22T19:12:32-05:00","message":"IsRequestAllowlisted query succeeded"}
{"level":"info","digest":"10854ac2...","deadline":"2025-10-25T00:12:32Z","time":"2025-10-22T19:12:40-05:00","message":"AllowlistRequest submitted"}

Digest allowlisted; proceeding to gateway POST: owner=<your-owner-address>, requestID=7433514f-4008-46dd-822a-633732b64ec9, digest=0x10854ac2...
Secret updated: secret_id=API_KEY, owner=<your-owner-address>, namespace=main
Secret updated: secret_id=DATABASE_URL, owner=<your-owner-address>, namespace=main

Deleting secrets

To remove secrets from the Vault:

Step 1: Create a deletion YAML file (secrets-to-delete.yaml):

secretsNames:
  - API_KEY
  - DATABASE_URL

Step 2: Run the delete command:

cre secrets delete secrets-to-delete.yaml --target production-settings

About namespaces

When you look at CLI outputs, you'll notice secrets are organized by namespaces. A namespace is simply a way to group related secrets together.

Using with multi-sig wallets

All cre secrets commands support the --unsigned flag for multi-sig wallet operations. This generates raw transaction data instead of sending transactions directly.

For complete multi-sig setup and usage, see Using Multi-sig Wallets.

Troubleshooting

"Secret not found" error in deployed workflow

Problem: Your workflow throws a "secret not found" error when calling runtime.GetSecret().

Solution:

  1. Verify the secret exists: cre secrets list --target production-settings
  2. Check that the secret ID in your code matches exactly
  3. Recreate the secret if necessary: cre secrets create ...

"Timeout expired" error

Problem: The CLI returns a timeout error when creating/updating secrets.

Solution: The onchain authorization has expired. Re-run the command to create a new authorization.

Different secrets for simulation vs. production

Problem: You want different secret values when simulating vs. running in production.

Solution:

  • For simulation: Store values in your local .env file
  • For production: Use cre secrets create with different values
  • The secret IDs stay the same—only the values differ

Learn more

Get the latest Chainlink content straight to your inbox.