Security & DevOps

Encrypted Git Repositories Compared: git-crypt, git-secret, SOPS, and age

May 25, 2026 9 min read Haven Team

The advice not to commit secrets to git is correct. The advice that follows — "use a secret manager" — is incomplete. There are real situations where you need configuration to ship alongside its code, in the same repository, on the same branch, encrypted. Four tools dominate that space. They look superficially similar and differ in important ways.


The use case is concrete: you have an infrastructure-as-code repository (Terraform, Ansible, Kubernetes manifests, a deploy script bundle) that needs API tokens, TLS keys, database passwords. You want the truth about those secrets to live in the same place as the code that consumes them. You want diffs to be reviewable. You want a single rotation operation to update everywhere. You do not want to commit cleartext.

The four tools that have survived: git-crypt (file-level transparent encryption), git-secret (gpg-wrapped file encryption with explicit add/reveal), SOPS (field-level encryption inside structured files), and age (a modern file encryption primitive that the other tools sometimes use under the hood).

git-crypt

Designed by Andrew Ayer and shipped in 2013. git-crypt encrypts specified files transparently at the git smudge/clean filter layer. You configure a .gitattributes file that marks paths as encrypted (e.g. secrets/* filter=git-crypt diff=git-crypt). On commit, the files are encrypted with AES-256-CTR using a symmetric key derived from the repository key. On checkout, they're decrypted automatically.

Access is granted by adding GPG public keys via git-crypt add-gpg-user. Anyone whose GPG key has been added can clone and immediately see plaintext. Anyone without can clone the repo and see encrypted blobs.

Strengths: completely transparent once configured. Files look normal in your editor. Diffs work. Strengths fade on large team rotations — revoking access means re-keying the entire repository and force-pushing rewritten history.

git-secret

A shell-script wrapper around GPG. You list files to encrypt in .gitsecret/paths/mapping.cfg; you commit the .secret encrypted versions; you exclude the originals with .gitignore. Reveal requires explicit git secret reveal.

No transparent filter. The mental model is "encrypted blobs are first-class, cleartext is local-only." Some teams prefer this because there's no surprise — what's in the repo is what you committed.

Maintenance has slowed in recent years. The tool still works but receives little active development. If you're starting today, the other three are more vital codebases.

SOPS

Mozilla's Secret OPerationS, now maintained by Open Source Security Foundation. SOPS does something the others don't: it encrypts only the values in a structured file (YAML, JSON, ENV, INI, binary), leaving the keys in plaintext. This means a SOPS-encrypted Kubernetes secret still looks like a Kubernetes secret, with sensible diffs when you change one value.

Key material can come from anywhere SOPS understands: AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault, PGP, and (since 2022) age. The same encrypted file can have multiple key references, so a CI runner can decrypt via KMS while a developer decrypts via age.

SOPS is the obvious choice for any Kubernetes-adjacent workflow because of how cleanly it composes with kubectl, Helm, and tools like Flux's secret decryption pipeline.

SOPS + age is the modern default

For a small team running infrastructure-as-code, SOPS encrypting structured files with age recipients is the path of least resistance in 2026. It avoids the operational overhead of GPG, integrates with KMS when you graduate to cloud providers, and produces diffs that humans can review.

age

Filippo Valsorda's modern replacement for GPG-as-file-encrypter. age — pronounced "ah-gay" — is a single binary with a small, opinionated design: X25519 + ChaCha20-Poly1305, scrypt-based password support, SSH key support, no subkeys, no algorithm negotiation, no web of trust.

age is not itself a git tool. It's a file encryption primitive that the other tools (SOPS in particular) consume. Used directly, you'd encrypt a secret file with age -r <recipient> secret.yaml > secret.yaml.age and commit the .age file. Decryption: age -d -i ~/.age/key secret.yaml.age.

It pairs naturally with SSH keys via age-ssh: a team that already manages SSH key access can use those keys as age recipients without setting up a parallel GPG keyring. This is the operational simplification that makes age stand out.

Honest Comparison

Property git-crypt git-secret SOPS age
Transparent in working tree Yes No No No
Per-value encryption File-level File-level Field-level File-level
Key backends GPG only GPG only KMS / PGP / age / Vault age / SSH
Active maintenance Low Stale High High
Diff friendliness Custom diff helper No diffs Native No diffs
Easy member rotation Hard Hard Easy Easy

The "Easy Rotation" Distinction

The single biggest operational difference is what happens when someone leaves the team. With git-crypt and git-secret, the encrypted blobs in your repo's history are still encrypted with a key the departing person held. They can clone the repo and decrypt every secret that was ever in it. The only complete remediation is to rotate every secret and either rewrite git history or accept that the old encrypted blobs remain forever readable.

SOPS and age don't change this fundamental problem — once you've shared a secret, anyone who saw the cleartext has it. But because SOPS encrypts each value independently and supports multiple recipients per file, removing a person is a re-encryption of current files only, with the departing person's recipient key removed. The git history remains decryptable to them, but going forward they're out. That's an acceptable threat-model trade for most teams.

The hardest part of repository secret management isn't encryption. It's the day someone leaves and the secrets they once read need to be rotated. Tools that make rotation cheap get used; tools that make rotation expensive grow stale and then bite you.

What Haven Uses

Haven's config/secrets.yaml is SOPS-encrypted. We chose SOPS specifically because the secrets are structured (database creds, API tokens, OAuth keys) and field-level diffs matter for review. The recipient list is age keys, which we manage alongside developer SSH keys — no separate GPG keyring to maintain.

For one-off encrypted files (e.g. credential exports we're temporarily moving between hosts), we use age directly. For repository-wide transparency (every file in a directory always encrypted), we'd use git-crypt — but in practice, the situations where we want that are also situations where we don't actually want those files in git at all.

The Meta-Recommendation

Use SOPS with age recipients, plus KMS recipients for CI/production decryption, for structured-secret-in-IaC workflows. Use age directly for ad-hoc encrypted file movement between trusted machines. Use git-crypt only if you have a specific reason to want transparent encryption and you accept the rotation pain. Skip git-secret unless you're maintaining an existing repo that uses it.

And for anything that's not best modeled as "config alongside code" — long-lived production secrets, database passwords that rotate daily, ephemeral OAuth tokens — use an actual secret manager. Password managers for human secrets, HashiCorp Vault or cloud KMS for service secrets, and let your repo describe the references, not the values.

Try Haven free for 15 days

Encrypted email and chat in one app. No credit card required.

Get Started →