Secrets in a Workspace
Devsy can inject sensitive values into a workspace without putting plaintext in devcontainer.json, command-line arguments or dotenv files. Values come from two places:
- Devsy-managed secrets, stored in your OS keyring or in an encrypted local file.
- External sources, such as a SOPS-encrypted file. Devsy reads them when needed and never copies them into its own store.
Both use the same delivery paths: environment variables for lifecycle commands, in-memory files under /run/secrets, and build secrets.
Devsy-managed secrets
Secrets are scoped to the active context. With the default auto backend, Devsy uses the OS keyring when there is one (Keychain, Credential Manager, or Secret Service on Linux). Otherwise it uses an age-encrypted secrets.enc file in the Devsy config directory. Only metadata such as names and timestamps is stored in plaintext.
devsy secret set DB_PASSWORD # hidden prompt
printf '%s' "$MY_VALUE" | devsy secret set DB_PASSWORD --stdin # for scripts
devsy secret set TLS_KEY --from-file ./tls.key
devsy secret list # never prints values
devsy secret get DB_PASSWORD
devsy secret delete DB_PASSWORDIn Desktop, Workspace Variables manages secrets and environment variables. Secret values are never shown.
External sources (SOPS)
Devsy reads SOPS-encrypted YAML, JSON and dotenv files without the sops executable. Files must contain flat top-level key/value pairs:
DATABASE_PASSWORD: ENC[...]
API_TOKEN: ENC[...]Register a local file as a named source. Devsy checks that it can decrypt the file first, and stores only the name, type, path and format override:
devsy secret source add sops project ./secrets.enc.yaml
devsy secret source list
devsy secret source remove projectA source cannot be removed while a context secret attachment still references it.
Sources declared by a repository
A repository can declare sources in customizations.devsy in its devcontainer.json:
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"customizations": {
"devsy": {
"secretSources": [
{ "name": "project", "type": "sops", "path": "./secrets.enc.yaml" }
],
"secrets": ["sops:project/DATABASE_PASSWORD"]
}
}
}Paths are relative to the repository root. Paths that escape it, including through symlinks, are rejected. This works for a local checkout (devsy workspace up .) and a remote repository, where Devsy reads the files from the same Git revision.
Credentials for SOPS
Devsy uses SOPS key discovery. For age, set SOPS_AGE_KEY or SOPS_AGE_KEY_FILE, or use the standard age key location. For AWS KMS, GCP KMS, Azure Key Vault and PGP, use the same configuration you would use with SOPS.
Referencing secrets
An unqualified name is a Devsy-managed secret: DB_PASSWORD. An external secret is source-qualified: sops:project/DB_PASSWORD. workspace up fails if any reference cannot be resolved.
Environment variables
--secret is repeatable and exposes the value to lifecycle commands during that workspace up. Later terminal sessions do not see it; use an attached secret for that.
devsy workspace up . --secret DB_PASSWORD
devsy workspace up . --secret sops:project/DATABASE_PASSWORD,target=DB_PASSWORDtarget= renames the variable. Repository secrets bindings have the same scope.
Files
type=mount writes the value to an in-memory file under /run/secrets:
devsy workspace up . --secret sops:project/TLS_KEY,type=mount,target=tls.keyThe file is /run/secrets/tls.key. Providers without an in-memory mount return an error.
Build secrets
devsy workspace up . --build-secret sops:project/NPM_TOKENThe build secret ID is the key (NPM_TOKEN), not the full reference. Use it in a Dockerfile with RUN --mount=type=secret,id=NPM_TOKEN ....
Attach a secret to a context
An attached secret is injected during workspace setup and into every new Devsy-managed SSH or terminal session, including devsy workspace ssh and the Desktop terminal:
devsy secret attach DB_PASSWORD
devsy secret attach sops:project/API_TOKEN
devsy secret detach DB_PASSWORDDevsy stores only the reference. Running sessions keep their current environment. After detaching, run workspace up or recreate the workspace so new sessions lose the value.
If names collide, a session uses the first of: explicit session environment (workspace ssh --set-env), attached secret, the container's own environment.
Where each kind is available
| Source | Lifecycle environment | New SSH or terminal sessions | File mount | Image build |
|---|---|---|---|---|
| Attached secret | Yes | Yes | No | No |
--secret NAME | Yes | No | No | No |
--secret NAME,type=mount | No | No | /run/secrets/<target> | No |
--secrets-file | Yes | No | No | No |
Repository secrets binding | Yes | No | No | No |
--build-secret NAME | No | No | No | BuildKit only |
Environment variables are inherited by child processes. Use type=mount for a narrower scope.
Storage backends
Metadata is in secrets.yaml. Each secret records its backend when created and stays there if you change the preference later. Backends:
keyring: the OS keyring.file: the age-encryptedsecrets.enc.auto: usekeyringwhen available, otherwisefile. Applies to new secrets.
Set the preference for a context, or for one command with DEVSY_SECRETS_BACKEND:
devsy context set -o SECRETS_BACKEND=filePassphrase for the file store
The file store is protected by either an automatically managed key or a passphrase. The mode applies to the whole file, across all contexts.
devsy secret protection status
devsy secret protection set-passphrase
devsy secret protection change-passphrase
devsy secret protection remove-passphraseDevsy looks for the passphrase in this order. The first credential found is used, and a wrong one is an error:
- Explicit input from the UI or process.
DEVSY_SECRETS_PASSPHRASE.DEVSY_SECRETS_PASSPHRASE_FILE.- A remembered credential in the OS keyring.
- An interactive prompt, where supported.
For automation, run devsy secret protection set-passphrase once, then point DEVSY_SECRETS_PASSPHRASE_FILE at a file containing the passphrase (up to 64 KiB). Devsy strips one trailing newline. On Unix the file must be owner-only, such as 0600.
Optionally remember the passphrase in the OS keyring. The CLI and Desktop share it:
devsy secret protection remember
devsy secret protection forgetforget removes the saved credential; secrets stay encrypted. In Desktop, use Manage security on the Secrets tab. Changing protection asks for confirmation in a native dialog.
Recovery
If you lose the passphrase and have no remembered credential, reset the file store as a last resort:
devsy secret protection reset-file-storeThe command lists every file-backed secret across all contexts and asks you to type RESET (or pass --yes). It moves secrets.enc to a quarantine file, removes the file-store metadata, and removes the remembered credential. Keyring secrets and env.yaml are untouched. Recreate the lost secrets afterwards.
Managed environment variables
For non-sensitive settings, devsy env stores plaintext values in env.yaml, separate from secrets, and works while the secret store is locked. Do not store sensitive values here.
devsy env set LOG_LEVEL=debug
devsy env set REGION --value us-east-1
devsy env list
devsy env get LOG_LEVEL
devsy env delete LOG_LEVELInject them at startup. The optional right-hand side renames the variable:
devsy workspace up ... --env LOG_LEVEL --env REGION=AWS_REGION--env takes managed environment variables only. Use --secret for sensitive values.
Attach a variable to the context to inject it on every start or recreate:
devsy env attach LOG_LEVEL
devsy env detach LOG_LEVELAn explicit --env with a target overrides the attachment for that run. Attaching or detaching affects the next start, not a running workspace. Deleting a value also removes its attachment.