Featured image

Table of Contents Link to heading

password-store (commonly invoked as pass) is the Unix-philosophy answer to credential management: passwords are stored as GPG-encrypted files, organised in a directory tree, with Git providing synchronisation and version history. There are no proprietary formats, no cloud lock-in, and no dependencies beyond tools that are standard in any Linux environment. The entire store is auditable, portable, and backed by well-understood cryptographic primitives.

This guide covers setup, day-to-day operations, cross-device synchronisation, and the failure modes worth knowing before they happen to you in production.

Installing pass and Generating a GPG Keypair Link to heading

sudo apt install pass
gpg --full-generate-key

Choose RSA 4096-bit with a defined expiry (2 years is a reasonable default). The passphrase protecting the private key is your last line of defence if the key file is ever exfiltrated — make it strong.

List your generated private keys:

gpg --list-secret-keys --keyid-format=long

Example output:

/home/user/.gnupg/pubring.kbx
------------------------------
sec   rsa4096/1A2B3C4D5E6F7G8H 2025-03-15 [SC]
      9X8Y7Z6W5V4U3T2S1R0P1A2B3C4D5E6F7G8H
uid  [ultimate] Duc Mai <ducmai.network@gmail.com>
ssb   rsa4096/2B3C4D5E6F7G8H9J 2025-03-15 [E]

Initialise your password store with the key fingerprint:

pass init 1A2B3C4D5E6F7G8H
Warning

Generate a GPG revocation certificate immediately and store it somewhere separate from the private key — ideally offline. If you lose access to the private key without a revocation cert, all passwords encrypted to that key become permanently inaccessible, and you cannot notify others that the key should no longer be trusted.

gpg --gen-revoke 1A2B3C4D5E6F7G8H > revoke.asc

Storing and Retrieving Passwords Link to heading

Add a Password Link to heading

pass insert banks/online-banking

pass prompts for the password twice (to confirm), then encrypts it to the configured GPG key and stores it at ~/.password-store/banks/online-banking.gpg. The directory structure is yours to define — use it to organise credentials by category, service, or environment.

Retrieve a Password Link to heading

Display to stdout:

pass banks/online-banking

Copy directly to clipboard (clears after 45 seconds by default):

pass -c banks/online-banking
Tip
The clipboard option (-c) is preferable to stdout for interactive use — it avoids the password appearing in terminal history or being visible on screen. For automation or scripting contexts, stdout is appropriate since the output is being consumed programmatically rather than displayed.

Remove a Password Link to heading

pass rm banks/online-banking

If Git integration is active, the deletion is committed automatically, giving you a record that the entry existed and when it was removed.

Bulk Export for Migration or Audit Link to heading

Bulk export is occasionally necessary for migration or audit purposes. The recommended approach is a script that decrypts entries to a secure temporary file rather than piping cleartext to disk or stdout directly.

Install and Run the Script Link to heading

curl -O https://raw.githubusercontent.com/duc-mt/dotfiles/refs/heads/master/bin/export_password_store
chmod +x export_password_store
./export_password_store

The script decrypts all entries in the store and writes them to a temporary file created by mktemp, providing a single cleartext snapshot you can reference or import into another system.

Understand mktemp in the Script Link to heading

mktemp /tmp/example.XXXXXX

mktemp creates a uniquely named temporary file, returning a path like /tmp/example.a1b2c3. The six X characters are replaced with a random string, ensuring no two concurrent invocations produce the same filename. This prevents race conditions and predictable filename attacks.

If XXXXXX is omitted, mktemp may fail or fall back to platform-specific behaviour. Always include the suffix template. See the Linux manual page for full details.

Warning
The export output contains cleartext credentials. Delete the temporary file immediately after use. Do not write it to a shared filesystem, cloud-synced directory, or any path accessible to other users. Treat the export file with the same access controls as the GPG private key itself.

Synchronising the Password Store Across Devices with Git Link to heading

Git integration turns pass into a multi-device credential store. Every pass insert, pass edit, and pass rm operation is automatically committed, giving you a full history of when credentials were added, changed, or removed.

Initialise a Git Repository Link to heading

Create a bare repository on your server or use a private remote:

git init --bare "$HOME/.password-store"

Wire the local store to Git and push:

pass git init
pass git branch -M master
pass git remote add origin https://github.com/duc-mt/password-store
pass git push origin master
Note
The .gpg files pushed to the remote are ciphertext — they’re safe to store in a private Git repository. Anyone who obtains the repository without your GPG private key gets encrypted blobs they cannot decrypt. That said, the filenames and directory structure are stored in plaintext, which reveals the categories and services you have credentials for. Keep the repository private.

Set Up a Password Store on a New Machine Link to heading

# 1. Import your GPG keys
gpg --import pubkey.asc
gpg --allow-secret-key-import --import privkey.asc

# 2. Clone the repository
git clone https://github.com/duc-mt/password-store "$HOME/.password-store"

# 3. Trust your imported keys
gpg --edit-key 1A2B3C4D5E6F7G8H

Inside the gpg --edit-key session, use trust and select 5 (ultimate trust) for your own key. Without this step, every pass invocation will warn about an untrusted key.

Synchronise going forward:

pass git push
pass git pull

Common Failures and How to Resolve Them Link to heading

GPG “No Secret Key” Error Link to heading

gpg: decryption failed: No secret key

This error means GPG cannot find a private key capable of decrypting the target file. Verify that the correct key is imported:

gpg --list-secret-keys --keyid-format=long

If the key is listed but trust level is unknown or undefined, set it to ultimate as described above. If the key is not listed at all, re-import from your backup.

Tip
Cross-check the key ID in the error output against the fingerprint used to initialise the password store (~/.password-store/.gpg-id). A mismatch here means the store was initialised with a different key than the one currently imported.

Git Sync Issues Link to heading

If pass git push or pass git pull fail, diagnose the remote configuration first:

git remote -v
git status

Authentication failures on push/pull typically indicate an expired token, missing SSH key, or changed remote URL. Ensure your SSH agent has the correct key loaded or that your HTTPS credential helper is configured for the repository host.

Conclusion Link to heading

password-store with GPG and Git is a robust, auditable, and portable credential management system that requires no proprietary software, no cloud subscription, and no trust in a third-party service. The security model is transparent: credentials are protected by the strength of your GPG key and its passphrase, and the backup and synchronisation path is Git over HTTPS or SSH.

The operational discipline required — maintaining GPG key backups, generating revocation certificates, managing Git remotes — is real, but it’s the same discipline required for any serious use of asymmetric cryptography. Get those fundamentals right, and pass is a credential store you can rely on across machines, across years, and across jobs.