Secrets
Dagger has first-class support for "secrets", such as passwords, API keys, SSH keys and so on. These secrets can be securely used in Dagger functions without exposing them in plaintext logs, writing them into the filesystem of containers you're building, or inserting them into the cache.
Here is an example, which uses a secret in a Dagger function chain:
export API_TOKEN="guessme"
- System shell
- Dagger Shell
- Dagger CLI
- Go
- Python
- TypeScript
dagger <<'EOF'
container |
from alpine:latest |
with-secret-variable MY_SECRET env://API_TOKEN |
with-exec -- sh -c 'echo this is the secret: $MY_SECRET' |
stdout
EOF
container |
from alpine:latest |
with-secret-variable MY_SECRET env://API_TOKEN |
with-exec -- sh -c 'echo this is the secret: $MY_SECRET' |
stdout
dagger core container \
from --address=alpine:latest \
with-secret-variable --name="MY_SECRET" --secret="env://API_TOKEN" \
with-exec --args="sh","-c",'echo this is the secret: $MY_SECRET' \
stdout
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
func (m *MyModule) ShowSecret(
ctx context.Context,
token *dagger.Secret,
) (string, error) {
return dag.Container().
From("alpine:latest").
WithSecretVariable("MY_SECRET", token).
WithExec([]string{"sh", "-c", `echo this is the secret: $MY_SECRET`}).
Stdout(ctx)
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
@function
async def show_secret(
self,
token: dagger.Secret,
) -> str:
return await (
dag.container()
.from_("alpine:latest")
.with_secret_variable("MY_SECRET", token)
.with_exec(
[
"sh",
"-c",
("echo this is the secret: $MY_SECRET"),
]
)
.stdout()
)
import { dag, object, func, Secret } from "@dagger.io/dagger"
@object()
class MyModule {
@func()
async showSecret(token: Secret): Promise<string> {
return await dag
.container()
.from("alpine:latest")
.withSecretVariable("MY_SECRET", token)
.withExec(["sh", "-c", `echo this is the secret: $MY_SECRET`])
.stdout()
}
}
Secret arguments can be sourced from multiple providers: the host environment, the host filesystem, the result of host command execution, and external secret managers 1Password and Vault.
Caching
When a Secret
is included in other operations, the layer cache entries for those operations will be based on the plaintext value of the secret. If the same operation is run with a secret with the same plaintext value, that operation may be cached rather than re-executed. In the above example, the secret is specified as env://API_TOKEN
, so the cache for the with-exec
will be based on the plaintext value of the secret. If two clients execute the container and have the same API_TOKEN
environment variable value, they may share layer cache entries for the container. However, if two clients have different values for the API_TOKEN
environment variable, the with-exec
will not share cache entries, even though both clients specify the secret as env://API_TOKEN
.
In some cases, users may desire that operations share layer cache entries even if the secret plaintext value is different. For example, a secret may often rotate in plaintext value but not be meaningfully different; in these cases, it should still be possible to reuse the cache for operations that include that secret.
For these use cases, the optional cacheKey
argument to Secret
construction can be used to specify the "cache key" of the secret. Secrets that share the same cacheKey
will be considered equivalent when checking cache of operations that include them, even if their plaintext value differs. See the cookbook for example usage.
Security considerations
- Dagger automatically scrubs secrets from its various logs and output streams. This ensures that sensitive data does not leak - for example, in the event of a crash.
- Secret plaintext should be handled securely within your Dagger workflow. For example, you should not write secret plaintext to a file, as it could then be stored in the Dagger cache.