Secrets Engine and docker pass are bundled with Docker Desktop.
Runtime secret injection is now available in Docker CE as an experimental,
early-access feature. It requires Docker Engine (dockerd) 29.2.0 or
higher.
Download the latest packages for your Linux distribution from the releases, then install them:
# Replace with the files you downloaded (matching your distro and arch).
sudo apt install ./DockerSecretsEngine-linux-amd64-ubuntu2404.deb \
./DockerSecretsEnginePlugins-linux-ubuntu2404.deb
systemctl --user daemon-reload
systemctl --user enable --now docker-secrets-engine.service
Recommended:
- dbus — required for the keyring backends.
- gnome-keyring or kwallet — secret storage backend.
systemctl --user disable --now docker-secrets-engine.service
sudo apt remove docker-secrets-engine-plugins docker-secrets-engine
[!WARNING] Docker CE support is experimental and may change between releases. Do not rely on it for production workloads yet. Also see known limitations.
Secrets Engine lets you reference secrets in docker run / docker compose
and have Docker resolve and inject the real values at runtime.
Key idea: you pass a pointer, not the secret.
se:// reference like se://foo.docker pass, which
stores secrets securely in your local OS keychain (or from a custom provider plugin).This means you don’t need:
.env files)compose.yamlStore the secret in your OS keychain:
docker pass set foo=bar
Run a container using a secret reference (the value se://foo is not the secret itself):
docker run --rm -e foo=se://foo busybox sh -c 'echo "$foo"'
Compose example:
services:
app:
image: your/image
environment:
API_TOKEN: se://foo
Secrets Engine supports realms: a simple namespacing scheme that helps to organize secrets by purpose, environment, application, or target system, and then retrieve them using glob-style patterns.
A realm is a prefix in the secret key. For example:
docker/auth/hub/mysecretdocker/auth/ghcr/tokendocker/db/prod/passwordBecause the realm is part of the key, you can query or operate on groups of secrets using patterns. For example, to target all Docker auth-related secrets:
docker/auth/**This makes it easy to:
prod/, staging/, dev/)docker/
auth/
hub/
mysecret
ghcr/
token
db/
prod/
password
[!TIP] Treat realms like paths - predictable structure makes automation and access control much easier.
[!NOTE] Missing a plugin? Help us pick the next provider — vote 👍 for your favorite (or request one) on the plugin backends epic.
Use the client module in your project:
go get github.com/docker/secrets-engine/client
Use the client to fetch a secret:
c, err := client.New()
if err != nil {
log.Fatalf("failed to create secrets engine client: %v", err)
}
// Fetch a secret from the engine
// We are using an exact match here, so only one or zero results will return.
secrets, err := c.GetSecrets(context.Background(), client.MustParsePattern("my-secret"))
if errors.Is(err, client.ErrSecretNotFound) {
log.Fatalf("no secret found")
}
// fallback to generic error
if err != nil {
log.Fatalf("failed fetching secrets: %v", err)
}
fmt.Println(secrets[0].Value)
Use the plugin module in your project:
go get github.com/docker/secrets-engine/plugin
A plugin needs to implement the Plugin interface:
var _ plugin.Plugin = &myPlugin{}
type myPlugin struct {
m sync.Mutex
secrets map[plugin.ID]string
}
func (p *myPlugin) GetSecrets(_ context.Context, pattern plugin.Pattern) ([]plugin.Envelope, error) {
p.m.Lock()
defer p.m.Unlock()
var result []plugin.Envelope
for id, value := range p.secrets {
if pattern.Match(id) {
result = append(result, plugin.Envelope{
ID: id,
Value: []byte(value),
CreatedAt: time.Now(),
})
}
}
return result, nil
}
func (p *myPlugin) Run(ctx context.Context) error {
// add long-running tasks here
// for example, OAuth tokens can be refreshed here.
<-ctx.Done()
return nil
}
Create a Go binary that use your plugin interface implementation and runs it through the plugin SDK:
package main
import (
"context"
"fmt"
"log/slog"
"github.com/docker/secrets-engine/plugin"
)
type myLogger struct{}
func (m *myLogger) Errorf(format string, v ...any) {
slog.Error(fmt.Sprintf(format, v...))
}
func (m *myLogger) Printf(format string, v ...any) {
slog.Info(fmt.Sprintf(format, v...))
}
func (m *myLogger) Warnf(format string, v ...any) {
slog.Warn(fmt.Sprintf(format, v...))
}
func main() {
config := plugin.Config{
Version: plugin.MustNewVersion("v0.0.1"),
Pattern: plugin.MustParsePattern("myrealm/**"),
// custom logger
Logger: &myLogger{},
}
secrets := map[plugin.ID]string{
plugin.MustParseID("myrealm/foo"): "bar",
}
p, err := plugin.New(&myPlugin{secrets: secrets}, config)
if err != nil {
panic(err)
}
// Run your plugin
if err := p.Run(context.Background()); err != nil {
panic(err)
}
}
To verify your plugin works, run the binary and it should connect to the Secrets Engine.
As a quick test we can retrieve secrets using curl, when running standalone
the default socket is daemon.sock and with Docker Desktop it is engine.sock.
Below we will query the Secrets Engine in standalone mode.
curl --unix-socket ~/Library/Caches/docker-secrets-engine/daemon.sock \
-X POST http://localhost/resolver.v1.ResolverService/GetSecrets \
-H "Content-Type: application/json" \
-d '{"pattern": "myrealm/**"}'
The value of a secret is always encoded into base64.
When using Go's json.Unmarshal it will automatically convert it back into
a slice of bytes []byte.
To manually decode it, you can pipe the value into base64, using the
flags appropriate for your platform:
# macOS / BSD
echo "<base64 string>" | base64 -D
# GNU/Linux (coreutils)
echo "<base64 string>" | base64 --decode
# or
echo "<base64 string>" | base64 -d
These apply to the experimental Docker CE integration described above. We are actively working to address them.
$SUDO_UID, i.e. the user who ran sudo apt install / sudo dnf install)
into /etc/docker/nri/conf.d/10-secrets-engine.conf. If the UID
cannot be determined at install time, the config is left unset and the integration stays inert until it is
configured manually.Brought to you courtesy of our legal counsel. For more context, see the NOTICE document in this repo.
Use and transfer of Docker may be subject to certain restrictions by the United States and other governments.
It is your responsibility to ensure that your use and/or transfer does not violate applicable laws.
For more information, see https://www.bis.doc.gov
docker/secrets-engine is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
$ claude mcp add secrets-engine \
-- python -m otcore.mcp_server <graph>