By this tier you likely have several Git repos - compose files, Terraform, Ansible - and every one of them wants secrets somewhere: API tokens, database passwords, the Proxmox API token from Infrastructure as Code. It's tempting to treat "it's just my homelab, nobody else sees this repo" as a reason not to bother securing them properly. That reasoning holds up less than it feels like it should.

Why plaintext secrets are a problem even solo

  • Git history is forever - deleting a secret from the current version of a file doesn't remove it from history; anyone (or any future you) with git log -p access can still find it, and a .git folder accidentally pushed to a public GitHub repo has leaked plenty of home lab credentials this way.
  • Backups copy secrets too - every backup of your compose repo (see Backups 101) now also contains every plaintext password that was ever in it, on every backup medium, off-site included.
  • "Just me" doesn't stay true - a repo you share to ask for help debugging, a laptop you sell or hand down, a cloud Git host account that gets compromised - any of these turns "solo" into "whoever has access now," retroactively, for everything already committed.

The options, honestly compared

  • Plaintext .env files, gitignored - what Docker Compose Basics recommended for Beginner, and it's fine there. The problem is it doesn't survive contact with IaC: Terraform variables, Ansible vars, and GitOps repos all want secrets committed somewhere to be reproducible, and "just don't commit it" doesn't compose across several repos and tools without a real system behind it.
  • HashiCorp Vault - the full solution: a dedicated secrets server with access policies, audit logging, dynamic short-lived credentials, secret rotation. Genuinely powerful, and genuinely more infrastructure than most homelabs need - it's another service to run, back up, and unseal after every restart, solving problems (fine-grained access policies across a team, dynamic credential issuance) a one-person homelab mostly doesn't have.
  • SOPS + age - encrypts individual values (or whole files) with a key you control, so the encrypted result is safe to commit directly alongside the rest of your IaC. age is a simple, modern encryption tool; SOPS (a CNCF Sandbox project, originally created at Mozilla) wraps it to encrypt just the values in a YAML/JSON file, leaving keys readable so diffs stay meaningful. This is the practical middle ground for a solo homelab: real encryption, no server to run.
  • git-crypt - similar idea, transparently encrypts specified files in the repo using GPG or a symmetric key. Simpler mental model than SOPS (whole files, not per-value) but less convenient when you want to review a diff of what changed in an otherwise-readable config file.

Recommendation for a solo homelab: SOPS + age. It's a single static binary, no server, and fits directly into the Git-centric workflow the rest of this tier already uses.

A minimal SOPS + age example

age-keygen -o key.txt
# Note the public key printed, e.g. age1qyqs...

.sops.yaml:

creation_rules:
  - path_regex: secrets/.*\.yaml$
    age: age1qyqs...your-public-key-here
sops secrets/proxmox.yaml
  • sops opens an editor with a plaintext view; on save, it encrypts the values back to disk before you commit.
  • Keep key.txt (the private key) out of the repo entirely - a password manager or a separate, non-Git-tracked location. It's the one file that, if lost, makes every secret you encrypted unrecoverable, and if leaked, decrypts everything.
  • Ansible and Terraform both have SOPS integrations (community.sops for Ansible; the carlpett/sops provider for Terraform) so encrypted values can be referenced directly in playbooks and IaC files without a manual decrypt step.

⚠️ Risk: losing key.txt with no backup means permanent loss of every secret encrypted under it - back it up somewhere separate from the repo it protects, ideally in the password manager you already use for everything else.

Next: Security Hardening.