Why CLI Password Management Still Matters in 2025
GUI password managers break inside tmux, over SSH, and inside containers. When you are writing a deploy script at 2am and need to pull a database credential without leaving the terminal, a well-configured CLI tool saves minutes per operation. At scale, those minutes compound.
The attack surface also shrinks. No Electron app, no browser extension communicating over a local socket, no auto-fill logic that can be tricked by a malicious iframe. You invoke the tool explicitly, pipe the secret where it needs to go, and the process exits. The credential never touches a GUI clipboard history if you use the right flags.
In 2025, three tools dominate actual production use among the sysadmins we spoke to: pass (the Unix password manager by Jason A. Donenfeld), Bitwarden CLI (bw), and 1Password CLI (op). We also tested gopass, which is a Go-based pass-compatible fork with better team features, and KeePassXC's CLI interface. The latter two have specific niches we cover below.
pass: The Unix Standard
pass stores each password as a GPG-encrypted file in ~/.password-store/, organized as a plain directory tree. Every entry is a file. The store is a Git repository by default. This is not a workaround, it is the design. You get full history, diffs, and remote sync for free.
Installation is a single package on every major distro:
The GPG setup is the real work. You need a key with at least 4096-bit RSA or an Ed25519 key. We recommend Ed25519 for new setups in 2025:
Once you have your key ID, initialize the store:
Adding a password and retrieving it:
The -c flag copies to clipboard and clears after 45 seconds (configurable via PASSWORD_STORE_CLIP_TIME). Never pipe directly to xclip in scripts unless you control the clipboard lifetime.
For team use, pass supports multiple GPG recipients per store or per subfolder. This is where pass starts showing friction. Adding a new team member means re-encrypting every entry they need access to. With 200 secrets, that is a slow operation and a coordination problem. gopass solves this with mounts and per-directory recipient files without re-encrypting the entire store.
pass works best for solo operators or pairs. Its biggest strength is zero external dependency after setup: no SaaS, no daemon, no internet. The entire store lives in a directory you can rsync to a bare Git server you control.
# Install
sudo apt install pass # Debian/Ubuntu
sudo pacman -S pass # Arch
# Generate Ed25519 GPG key
gpg --full-generate-key # choose (9) ECC, then (1) Curve 25519
# Get your key ID
gpg --list-secret-keys --keyid-format LONG
# output example: sec ed25519/3AA5C34371567BD2
# Initialize store with your key ID
pass init 3AA5C34371567BD2
# Add a password
pass insert prod/db/postgres
# Retrieve (stdout)
pass prod/db/postgres
# Copy to clipboard
pass -c prod/db/postgres
# Git setup
pass git init
pass git remote add origin git@your-server:passwords.git
pass git push -u origin main
gopass: pass for Teams
gopass 1.15.x (current stable as of mid-2025) is backward-compatible with pass but adds mounts, templates, TOTP support, and per-mount recipient management. The binary is a single Go executable with no runtime dependencies beyond GPG or age.
The killer feature for DevOps teams is gopass sync with multiple remotes and the ability to mount sub-stores:
Each mount can have its own GPG recipients file. When you onboard a new engineer, you run gopass recipients add in the relevant mount and only re-encrypt that subtree. On a store with 500 secrets split into 5 mounts of 100, you re-encrypt 100 entries, not 500.
gopass also ships with gopass completion bash and gopass completion zsh, which provide tab completion for secret paths. Small thing, significant daily quality of life improvement.
For teams running automated pipelines, gopass jsonapi turns the store into a local JSON-RPC server that browser extensions and scripts can query. We tested this with a Python deployment script that pulls RDS credentials at runtime rather than baking them into environment files.
The age encryption backend (--crypto=age) is available in gopass as an alternative to GPG. Age is simpler, faster, and avoids GPG's web of trust complexity, but it lacks the ecosystem integrations some teams depend on. Evaluate against your existing key infrastructure before switching.
# Install gopass
curl -sfL https://github.com/gopasspw/gopass/releases/download/v1.15.13/gopass-1.15.13-linux-amd64.tar.gz | tar xz
sudo mv gopass /usr/local/bin/
# Initialize with existing GPG key
gopass setup
# Mount a secondary store (e.g. shared team secrets)
gopass mounts add team git@your-server:team-passwords.git
# Add a recipient to the team mount only
gopass recipients add --store team COLLEAGUE_KEY_ID
# Re-encrypt the team mount for all recipients
gopass sync
# Use a secret in a script (stdout, no clipboard)
EXPORT DB_PASS=$(gopass show -o prod/db/postgres)
Bitwarden CLI: Self-Hostable SaaS Alternative
Bitwarden CLI (bw) version 2024.x and later ships as a Node.js binary distributed via npm or as a standalone executable. The standalone is preferable in server environments where you do not want Node in PATH.
Bitwarden's model is different from pass: secrets live in a Bitwarden vault (cloud or self-hosted Vaultwarden), and bw syncs a local SQLite cache. You authenticate once per session with bw unlock, which returns a session token you export as BW_SESSION.
The session token approach works well in CI but requires care. The token is a sensitive credential itself. We store it in a tmpfs-backed file during pipeline runs and shred it on exit:
Vaultwarden (the Rust reimplementation of the Bitwarden server) runs in Docker and is the standard self-hosting path. A minimal Vaultwarden setup on a 1 GB VPS handles 20-user teams without strain. The admin panel is at /admin after setting ADMIN_TOKEN in the environment.
Bitwarden CLI supports organizations and collections, which maps well to RBAC requirements. An engineer gets read access to the staging collection but not production. Permissions are managed in the web UI or via the bw organization commands. This is operationally simpler than managing GPG recipient lists manually.
The weak point of Bitwarden CLI is the session model: if you forget to run bw lock after use, the session persists. In shared systems, set BW_SESSION in your shell profile only if you understand the implications. In scripts, explicitly export and unset.
# Install standalone binary
wget https://github.com/bitwarden/clients/releases/download/cli-v2024.10.0/bw-linux-2024.10.0.zip
unzip bw-linux-2024.10.0.zip
sudo mv bw /usr/local/bin/
# Configure for self-hosted Vaultwarden
bw config server https://vault.yourdomain.com
# Login and unlock
bw login
export BW_SESSION=$(bw unlock --raw)
# Retrieve a secret by name
bw get password "prod/db/postgres"
# Use in a script with session cleanup
run_deploy() {
local session
session=$(bw unlock --raw)
DB_PASS=$(BW_SESSION="$session" bw get password "prod/db/postgres")
# ... deploy logic ...
BW_SESSION="$session" bw lock
unset session DB_PASS
}
# List items in a collection
bw list items --collectionid | jq '.[].name'
1Password CLI: Enterprise Grade with op
1Password CLI (op) version 2.x is the most polished of the three from a DevOps integration standpoint. The op inject command alone justifies its place in production pipelines: it reads a template file with op:// references and outputs a populated version, keeping secrets out of environment variables entirely.
1Password (https://1password.com/PLACEHOLDER) supports Linux natively, offers a CLI that works with service accounts (no interactive auth required in CI), and provides audit logs at the vault and item level. For teams with compliance requirements, that audit trail is non-negotiable.
Service accounts generate a OP_SERVICE_ACCOUNT_TOKEN, which you store as a CI secret. The token scopes to specific vaults. No MFA prompt, no session unlock step:
The op run command wraps process execution and injects secrets as environment variables without writing them to disk:
For teams already using 1Password for GUI access, the CLI is a natural extension. The 1Password CLI integrates with SSH agent (op ssh-agent), allowing you to store SSH private keys in vaults and use them for remote operations without ever writing the key to ~/.ssh/.
If you are naming projects or services and storing their credentials in 1Password, keeping the project slug consistent across your vault naming, your Git remotes, and your DNS records reduces cognitive load. Services like nicename.me help with the naming step when you are registering a domain for a new project and want to verify slug availability across platforms before committing.
The cost is the one real objection to 1Password in this context. Teams pricing at $7.99/user/month means a 10-person team pays ~$960/year. For self-funded operations, Vaultwarden plus Bitwarden CLI achieves 80% of the functionality for the cost of a VPS.
# Install op CLI
curl -sS https://downloads.1password.com/linux/keys/1password.asc | \
sudo gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg
echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/amd64 stable main' | \
sudo tee /etc/apt/sources.list.d/1password.list
sudo apt update && sudo apt install 1password-cli
# Verify version
op --version # 2.30.0 as of June 2025
# Service account auth (CI/CD)
export OP_SERVICE_ACCOUNT_TOKEN="ops_eyJ..."
# Inject secrets into a template
# secrets.tpl:
# DB_PASSWORD={{ op://Production/Postgres/password }}
# API_KEY={{ op://Production/Stripe/api_key }}
op inject -i secrets.tpl -o secrets.env
# Run a process with secrets injected as env vars
op run --env-file=secrets.tpl -- ./deploy.sh
# List vaults accessible to service account
op vault list --format json | jq '.[].name'
Using CLI Password Managers in CI/CD Pipelines
The most common failure mode we see is secrets baked into .env files committed to Git, or environment variables leaked in build logs. A proper CLI password manager integration eliminates both.
For GitHub Actions with 1Password, the official op action handles token injection. For GitLab CI with Bitwarden, you export BW_SESSION from a before_script block sourcing from a masked variable. For gopass in self-hosted Gitea pipelines, you mount the GPG keyring from a secrets volume.
The pattern that works across all three tools is the same: fetch the secret at runtime, use it, discard it. Never persist to disk except in tmpfs.
For teams building out automation pipelines and evaluating AI-assisted DevOps tooling, platforms like taskbotshub.ai can help orchestrate credential fetching workflows where secrets are pulled from the vault at task execution time rather than stored in the automation platform itself. The principle matches what op run and bw get enable at the shell level.
Audit your pipeline for these anti-patterns before choosing a tool:
Secret rotation is where SaaS tools (1Password, Bitwarden cloud) have an edge: the vault is the source of truth, rotation updates one record, and all pipelines pulling at runtime get the new value automatically. With pass or gopass on a Git remote, rotation requires a commit, a push, and any pipeline caches must be invalidated.
# Anti-patterns to audit for
# Bad: secret in env var visible in ps aux
export DB_PASS=plaintext123
./deploy.sh
# Bad: secret written to disk
echo $DB_PASS > /tmp/secret && ./deploy.sh
# Good: secret fetched at runtime, never stored
op run --env-file=secrets.tpl -- ./deploy.sh
# Good: gopass in a script with no disk write
./deploy.sh --db-pass "$(gopass show -o prod/db/postgres)"
# Good: Bitwarden with session scoped to subshell
(export BW_SESSION=$(bw unlock --raw); ./deploy.sh; bw lock)
# Audit your CI logs for accidental secret exposure
git log --all --full-history -- '*.env' '*.secret'
git grep -i 'password\|secret\|token' -- '*.yml' '*.yaml'
KeePassXC CLI: Offline Completeness
KeePassXC 2.7.x ships keepassxc-cli as part of the package. The database is a single encrypted .kdbx file using AES-256 or ChaCha20. No GPG dependency, no network dependency, no daemon.
keepassxc-cli is the right tool when you need a self-contained encrypted store on an air-gapped system, or when your team already uses KeePassXC for GUI access and wants CLI access to the same database.
The workflow is more verbose than pass but the security model is solid. The master password (or key file) unlocks the database for each operation. There is no session token to manage.
The limitation is sharing: a .kdbx file is a binary blob. Concurrent writes require a locking mechanism you implement yourself (a simple lockfile or a Git LFS setup). For solo use or read-heavy shared access, this is manageable. For teams writing new credentials frequently, it becomes a merge conflict problem.
For air-gapped servers where neither GPG infrastructure nor SaaS is acceptable, keepassxc-cli plus a USB-transferred .kdbx file is a legitimate production setup we have seen in regulated environments.
# Install
sudo apt install keepassxc # includes keepassxc-cli
# Create a new database
keepassxc-cli db-create -p /secure/vault.kdbx
# Add an entry
keepassxc-cli add -p /secure/vault.kdbx prod/db/postgres
# Retrieve a password (prompts for master password)
keepassxc-cli show -s -a Password /secure/vault.kdbx prod/db/postgres
# Use in a script (password from stdin to avoid shell history)
DB_PASS=$(echo "$MASTER_PASS" | keepassxc-cli show -q -s -a Password \
/secure/vault.kdbx prod/db/postgres)
# List all entries
keepassxc-cli ls /secure/vault.kdbx
Head-to-Head Comparison
After running all five tools in real workflows for six months, here is what the data shows:
pass: Zero external dependencies post-setup. Git history gives you full audit trail. Re-encryption on team changes is manual and slow. Best for solo operators and pairs.
gopass: Fixes pass's team scaling problems with mounts and per-directory recipients. Single binary. Backward-compatible with pass stores. Best for small teams (2-10 people) who want self-hosted control.
Bitwarden CLI + Vaultwarden: Best cost-to-feature ratio for self-hosted team deployments. RBAC via organizations. Session model requires discipline. Best for 5-50 person teams willing to run a VPS.
1Password CLI (op): The strongest CI/CD integration with op inject and op run. SSH agent integration is genuinely useful. Service account tokens work cleanly in pipelines. Audit logs. The cost is real at scale. Best for funded teams with compliance requirements.
KeePassXC CLI: Best for air-gapped environments and teams already on KeePassXC. Worst for concurrent writes. No network requirement is a genuine security property in some contexts.
Setup time from zero to working: pass takes 15 minutes if you have a GPG key. gopass adds 20 minutes for mount configuration. Bitwarden CLI with Vaultwarden takes 45-60 minutes for server setup. 1Password CLI takes 10 minutes once you have a subscription. KeePassXC CLI takes 5 minutes.