Execute commands with secrets from keyring, not from disk.
secret-tool-run is a bash utility that runs commands with environment secrets loaded from your system's secure keyring (through secret-tool), eliminating the need to store .env files on disk. Perfect for developers who want to keep credentials off the filesystem while maintaining a smooth development workflow.
Instead of this (storing secrets on disk):
# ❌ Dangerous: secrets exposed on filesystem
cat .env # DATABASE_PASSWORD=super_secret
python app.pyDo this (secrets from keyring):
# ✅ Secure: secrets loaded from keyring, never persisted to disk
secret-tool-run python app.pyUnder the hood: secret-tool-run retrieves your secrets from the system keyring (encrypted and managed by the OS) through secret-tool, creates a temporary file with secure permissions only your process can read, passes it to your command, and deletes it immediately after—leaving no trace on disk.
- Linux with a keyring service (GNOME Keyring, KWallet, etc.)
- bash (4.0+)
- secret-tool from
libsecret-toolspackage
Installs secret-tool-run user locally to ~/.local/bin:
curl -fsSL https://go.hugobatista.com/ghraw/secret-tool-run/main/install.sh | shDownload the repository and run the installer:
git clone https:/go.hugobatista.com/gh/secret-tool-run.git
cd secret-tool-run
./install.shThe installer will:
- Check for dependencies
- Let you choose between system-wide (
/usr/local/bin) or user-local (~/.local/bin) installation - Set up the
secret-tool-runcommand - Verify the installation
secret-tool-run [OPTIONS] COMMAND [ARGS...]| Option | Description |
|---|---|
--file FILE, -f FILE |
Secrets file path (default: .env) |
--app APP, -a APP |
Keyring app identifier (default: current folder name) |
--help, -h |
Show help message |
secret-tool-run uv run pywrangler devWhat happens:
- Loads
.envfrom keyring for current folder - Creates temporary
.envfile - Runs
uv run pywrangler devwith secrets available - Deletes
.envafter command completes
secret-tool-run hatch run devPerfect for running development servers where you need environment variables but don't want them persisted on disk.
secret-tool-run --file .secrets act --secret-file .secretsWhat happens:
- Uses custom file name
.secretsinstead of.env - Loads or prompts for secrets under that filename
- Runs
actwith the secrets file - Cleans up
.secretsafter execution
This is especially useful for testing GitHub Actions workflows locally while keeping production secrets secure.
# Development environment
secret-tool-run --app myproject-dev npm start
# Production environment
secret-tool-run --app myproject-prod npm startEach --app name is a separate keyring entry, allowing you to manage different secret sets (dev, staging, prod) for the same project.
secret-tool-run docker-compose upGreat for docker-compose files that source .env for configuration.
secret-tool-run env | grep SECRETS_FILEThe SECRETS_FILE environment variable contains the absolute path to the secrets file created by secret-tool-run.
secret-tool-run act --secret-file @SECRETS@What happens:
- Detects
@SECRETS@token in arguments - Loads secrets from keyring into memory
- Creates file descriptor at
/dev/fd/9(no disk write) - Replaces
@SECRETS@with/dev/fd/9 - Runs
actwhich reads secrets from the file descriptor - FD automatically closes - no cleanup needed
Perfect for:
- GitHub Actions local testing with
act - Docker with
--env-file - Any tool that can read from file descriptors
Won't work for:
- Shell sourcing (
source $SECRETS_FILE) - Tools that verify file exists with stat checks
- Tools that need to read the file multiple times
secret-tool-run docker run --env-file @SECRETS@ myimageSecrets are loaded from keyring and passed to Docker without ever touching the disk. The @SECRETS@ token automatically enables zero-disk-I/O mode.
Create a .keep file to prevent automatic deletion of the secrets file:
touch .env.keep
secret-tool-run your-command
# .env will remain after executionThis is useful for:
- Debugging secrets content
- Running multiple commands without reloading
- IDE integration where the editor expects a persistent file
# Use a different file name
secret-tool-run --file .env.production npm run build
# Use a path in a different directory
secret-tool-run --file /tmp/my-secrets ./deploy.shYour command receives the SECRETS_FILE environment variable pointing to the secrets file:
secret-tool-run bash -c 'echo "Secrets are at: $SECRETS_FILE"'You can use this in scripts that need to know the file location explicitly.
For maximum security, use the @SECRETS@ token in your command to pass secrets via file descriptor without writing to disk:
secret-tool-run act --secret-file @SECRETS@How it works:
- secret-tool-run detects the
@SECRETS@token in your command arguments - Loads secrets from keyring into memory only
- Creates file descriptor at
/dev/fd/9(no disk write) - Replaces
@SECRETS@token with/dev/fd/9in all arguments - Your command reads from the FD as if it were a file
- No temp file created, no cleanup needed
- FD automatically closes when command completes
Security benefits:
- Zero disk I/O - secrets never touch the filesystem
- No directory entry visible in
ls - Automatic cleanup (pipe closes on exit)
- No permission race conditions
- No accidental
.keepfile keeping secrets around - Simple, explicit syntax - just use
@SECRETS@where you need it
Compatibility:
✅ Works with these tools:
secret-tool-run act --secret-file @SECRETS@
secret-tool-run docker run --env-file @SECRETS@ imageReplaced tokens work just like file paths:
secret-tool-run mycommand --config @SECRETS@ --output results.txt
# All @SECRETS@ tokens are replaced with /dev/fd/9On first use (when secrets aren't in keyring):
- secret-tool-run prompts: "Paste your secrets content..."
- Paste your
.envcontent (KEY=VALUE format) - Press
Ctrl-Dto finish (orCtrl-Cto cancel) - Secrets are encrypted and stored in system keyring
- Future runs load automatically
- Keyring encryption: Secrets stored in your system's encrypted keyring service
- File permissions: Temporary files created with
600permissions (owner read/write only) - Short-lived exposure: Files on disk exist only during command execution
- File descriptor mode: Use
@SECRETS@token for zero disk I/O (most secure option) - No git commits: Temporary files are created/deleted, reducing risk of accidental commits
- Session isolation: Each terminal session can use different secrets with
--appflag
- Use
@SECRETS@token in your command for zero disk writes (when your tool supports it) - Use encrypted home directories
- Ensure your keyring is properly locked when not in use
- Be cautious running secret-tool-run on shared systems
The command may require a regular file instead of a file descriptor. Try without the @SECRETS@ token:
# If this fails:
secret-tool-run mycommand --file @SECRETS@
# Try this instead:
secret-tool-run mycommandCheck if secrets are actually stored:
secret-tool search app "$(basename $PWD)"If nothing appears, the keyring store failed. Try storing manually:
secret-tool store --label "Test" app "my-test"
# Paste your secret, press Ctrl-D
secret-tool lookup app "my-test"Check for a .keep file:
ls -la .env.keepRemove it to restore auto-cleanup:
rm .env.keep# List secrets for current folder
secret-tool search app "$(basename $PWD)"
# Delete specific entry
secret-tool clear app "$(basename $PWD)"
# Or for a specific app name
secret-tool clear app "myproject-prod"If your command crashes before secret-tool-run's cleanup trap runs, manually remove:
rm .env # or your custom secrets file nameRun the uninstall script:
./uninstall.shThis will:
- Remove the
secret-tool-runbinary - Optionally help you clear keyring entries
To manually clear all secret-tool-run secrets from keyring:
# List all entries
secret-tool search app ""
# Remove specific ones
secret-tool clear app "your-app-name"