Last updated on 28th September 2026

Encrypting Secrets in Git with git-crypt

Why encrypt files inside a repository?

For most secrets, the right answer is to keep them out of Git entirely — .env files excluded by .gitignore, with the real values injected at deploy time. If that covers your situation, start with our guide to keeping API keys out of your Git repository instead; it is the simpler and safer default.

But some things genuinely need to live in the repository:

  • Config that is version-controlled by nature — encrypted values that must change in lockstep with the code that reads them, so a rollback restores the matching config.
  • Certificates, keystores, and signing keys that a build step needs and that must be pinned to a specific commit.
  • Small teams without a secrets manager, where standing up Vault or a cloud KMS is more operational overhead than the project warrants.
  • Repositories handed to clients or contractors, where some files should be readable by everyone and others by a named few.

git-crypt handles exactly this case. It encrypts chosen files transparently: they are plaintext in your working directory and ciphertext in every commit, push, and clone. People without the key can still clone the repo, read the unencrypted files, and contribute — they simply see binary blobs where the secrets are.

Is git-crypt the right tool?

Approach Secrets live in repo? Best for Main drawback
.gitignore + DeployHQ config files No Almost everyone Config not versioned with code
git-crypt Yes, encrypted Per-file access control; binary secrets No key revocation
dotenvx Yes, encrypted .env values specifically Env vars only, not arbitrary files
Vault / cloud KMS No Larger teams, audit requirements Real operational overhead

The row that usually decides it: git-crypt encrypts arbitrary files — a .p12, a keystore, a service-account JSON — where dotenvx encrypts environment variable values. If your secret is not a key/value pair, git-crypt is the one that fits.

Installing git-crypt

macOS

brew install git-crypt

Debian / Ubuntu

sudo apt install git-crypt

Debian stable currently ships 0.7.0; testing and unstable ship 0.8.0. If you need the newest release, build from source.

From source

git clone https://github.com/AGWA/git-crypt.git
cd git-crypt
make
sudo make install PREFIX=/usr/local

Build dependencies are make, g++ (C++11, gcc 4.9 or newer), and libssl-dev (openssl-devel on RHEL/CentOS). At runtime git-crypt needs Git 1.7.2 or newer — 1.8.5+ is recommended — and OpenSSL.

Confirm the install:

git-crypt version

If you do not have Git set up yet, start with how to install Git.

Initialising a repository

From the root of an existing repository:

git-crypt init

This generates a symmetric key and stores it at .git/git-crypt/keys/default. The key lives inside .git/, so it is never committed and never pushed — which also means a fresh clone cannot read your encrypted files until you explicitly share the key.

You can maintain several independent keys in one repository — useful when different groups should unlock different files:

git-crypt init -k clientkey

Choosing which files to encrypt

git-crypt decides what to encrypt from .gitattributes. Create one in the repository root:

secretfile filter=git-crypt diff=git-crypt
*.key filter=git-crypt diff=git-crypt
secretdir/** filter=git-crypt diff=git-crypt

The pattern syntax is the same as .gitignore, so the rules covered in ignoring unwanted files apply here too.

This is the one mistake that matters. Upstream puts it plainly: "Make sure your .gitattributes rules are in place before you add sensitive files, or those files won't be encrypted!" git-crypt encrypts on the way into a commit. A secret committed before the rule existed is sitting in your history in plaintext, and adding the rule afterwards does not retroactively encrypt it. If that has already happened, treat it as a leak: rotate the credential first, then clean the history using the approach in removing large files from Git history.

Note also that .gitattributes itself must not be encrypted — Git needs to read it to know what to decrypt. Keep it in plaintext and protect it in review: anyone who can quietly delete a line from it can cause the next commit of that file to land unencrypted.

Verifying what is actually encrypted

Never assume the rules matched. Check:

git-crypt status

Useful flags:

Flag Effect
-e List only encrypted files
-u List only unencrypted files
-f / --fix Re-stage files whose encryption state does not match the current rules

git-crypt status -e before your first push is the check worth building a habit around. You can also confirm from the other direction — ask Git what the committed blob actually looks like:

git show HEAD:secretfile | head -c 16 | xxd

An encrypted blob begins with git-crypt's magic header rather than your plaintext.

Sharing access with your team

Symmetric key

The blunt approach: export the key and hand it over through a channel that is not the repository.

git-crypt export-key ~/secret-repo-key

The recipient clones as normal, then:

git-crypt unlock ~/secret-repo-key

This is fine for one or two people. It scales badly: every holder has the same key, and there is no way to take it back from one person without rotating for everyone.

GPG collaborators

The better option for a real team. Add someone by GPG user ID:

git-crypt add-gpg-user user@example.com

This commits a copy of the repository key encrypted to that person's GPG public key, under .git-crypt/. They then clone and run:

git-crypt unlock

No key file changes hands — git-crypt finds the GPG-encrypted copy in the repo and decrypts it with their private key.

Flag Effect
-k KEY_NAME Add the user to a named key rather than the default
-n / --no-commit Stage the change without committing it
--trusted Accept the GPG key even if it is not marked trusted in your keyring

Locking and unlocking

git-crypt lock     # re-encrypt the working directory
git-crypt unlock   # decrypt using GPG

lock is worth knowing about for practical reasons rather than security ones: locking before you share your screen, hand over a laptop, or point a backup tool at the directory means the plaintext is not sitting on disk.

What git-crypt does not protect

This is the section most tutorials skip, and it is the part that determines whether git-crypt is safe for your use case. Upstream is explicit about the limits.

Not encrypted at all:

  • File names. A file called production-aws-credentials.key advertises itself perfectly well while encrypted.
  • Commit messages. "fix prod db password" leaks the same information the file does.
  • Symlink targets, gitlinks, and other metadata.

Leaked even for encrypted files:

  • File length, approximately.
  • Whether two files are identical — encryption is deterministic, which is what lets Git diff and merge these files at all, but it means identical plaintext produces identical ciphertext.

Operational limits:

  • There is no key revocation. This is the big one. If someone leaves the team, you cannot un-share the key. They keep the ability to decrypt every version of every file they ever had access to, including history. Genuinely removing their access means rotating the underlying secrets and rewriting history — so plan for rotation from the start rather than treating it as an emergency procedure.
  • Encrypted files do not compress, so a repository heavy with encrypted content grows faster than you would expect.
  • git apply will not work on patches touching encrypted files unless the patch itself is encrypted.
  • Some third-party Git GUIs do not handle git-crypt's filters reliably. Upstream flags this directly; verify your team's tooling before committing to it.

The practical reading: git-crypt is good at confidentiality of file contents against someone who has the repository. It is not an access-control system, and it is not a substitute for a secrets manager when you need revocation or an audit trail.

Deploying a repository that uses git-crypt

Here is the part worth thinking through before you adopt it. A deployment system clones your repository — and a clone without the key contains ciphertext. Your build will happily copy an encrypted blob to production and your application will fail to parse it.

There are two honest ways to handle this, and for most teams it is the second.

Unlock during the build. If the secrets must be decrypted in the pipeline, you have to get the key into the pipeline — which means you have moved the problem rather than solved it, and you now have a key sitting in your CI configuration that itself has no revocation story. It is workable with a build pipeline step, but understand the trade you are making.

Keep production secrets out of the pipeline entirely. Use git-crypt for what it is genuinely good at — sharing secrets between developers, versioned alongside the code — and deliver production values separately through DeployHQ config files, which are stored encrypted and written to the server at deploy time without ever entering your repository. The two approaches complement each other rather than competing, and this combination avoids the bootstrapping problem completely.

For the wider picture on securing a release pipeline, see our practical security tips for smarter deployments, and for .env handling specifically, managing secrets with .env files and config.

Quick reference

I want to... Command
Set up git-crypt in a repo git-crypt init
Create a second, named key git-crypt init -k KEY_NAME
Mark files for encryption Add path filter=git-crypt diff=git-crypt to .gitattributes
See which files are encrypted git-crypt status -e
Fix files whose state is stale git-crypt status -f
Export the symmetric key git-crypt export-key FILENAME
Unlock with a key file git-crypt unlock /path/to/key
Unlock with GPG git-crypt unlock
Add a collaborator by GPG ID git-crypt add-gpg-user USER_ID
Re-encrypt the working tree git-crypt lock
Check the installed version git-crypt version

Under the hood, git-crypt encrypts with AES-256 in CTR mode, using a synthetic IV derived from the SHA-1 HMAC of the file. That deterministic construction is what makes encrypted files diffable and mergeable in Git — and is also why identical files produce identical ciphertext.


Once your secrets are handled properly, DeployHQ takes care of getting your code to your servers — with config files delivering production credentials securely at deploy time, so they never touch your repository. Start deploying for free.