Run your own backend

Secrets management for a self-hosted app - without a vault

The short version: you do not need HashiCorp Vault for most apps. Keep secrets out of code and git, inject them at runtime (a restricted env file, a systemd EnvironmentFile, Docker secrets, or an encrypted file with sops/age), scope each one to the service that needs it, rotate them, and scan your repo for leaks. The principles matter more than the tool.

The principles that matter most

  • Never in code or git - a secret in source is a secret everyone with the repo, and its whole history, has.
  • Inject at runtime - read secrets from the environment when the app starts, not baked into the image or the build.
  • Least privilege - each service gets only the secrets it needs, so one compromise is not all of them.
  • Rotatable and scanned - make rotation routine, and scan the repo so a leak is caught early.

Where to actually put them

A restricted env file

An .env (or /etc/myapp.env) at chmod 600, gitignored, loaded at runtime. Simple and good enough for most apps.

systemd EnvironmentFile

Point the unit at an EnvironmentFile so the service gets its secrets without them living in the unit or the repo.

Docker / Compose secrets

Mounted as files, not baked into the image - so a secret is never in an image layer someone can pull.

An encrypted file (sops / age)

Commit secrets encrypted with sops or age and decrypt at deploy - git-friendly, with the key kept separate.

Set it up in three steps

1

Lock down the env file, keep it out of git

chmod 600 /etc/myapp.env          # owner only
echo ".env" >> .gitignore          # never commit it
git check-ignore .env             # confirm it is ignored
2

Inject at runtime

# systemd unit
[Service]
EnvironmentFile=/etc/myapp.env
# or docker compose: env_file: [ .env ]  (mounted, not baked in)
3

Scan for anything already leaked

# scan the repo (and history) for committed secrets, then rotate any you find.
When you do need more

When a vault actually earns its keep

A dedicated secrets manager is worth its complexity once you outgrow files: many services that each need scoped access, dynamic or short-lived credentials (database users created on demand), or a requirement for centralized rotation and audited access. Below that, files plus discipline - out of git, restricted, injected at runtime, rotated - are safer than a half-configured vault.

Whatever you use, scan your repository first - a secret already committed needs to be rotated, because removing it from the latest commit does not remove it from history. The free secrets scanner finds exposed keys, and the .gitignore generator keeps env files out of git in the first place.

How Infraveil handles this

Secrets distributed consistently, least-privilege, recorded

The hard part of secrets is not storing one - it is keeping the same value consistent across every instance, scoped to what each service needs, and rotated without a hunt. On your own servers, Infraveil distributes config and secrets consistently across your services, keeps access least-privilege, and records changes - so rotation is one managed change, not a copy-paste across a dozen configs, and the secrets stay on infrastructure you own.

The same secret consistent across every instance, on infra you own
Access scoped least-privilege per service
Rotation a single managed change, recorded and inspectable

Frequently asked questions

Do I really not need HashiCorp Vault?

For a single app or a few services, no. A restricted env file or systemd EnvironmentFile, secrets kept out of git, and a leak scan cover the essentials. Vault earns its complexity with many services, dynamic credentials, or centralized rotation - reach for it then, not by default.

Is a .env file safe to use?

Yes, if it never reaches git and has tight permissions - gitignored, chmod 600, loaded at runtime not baked into an image. The danger is a .env committed to a repo or copied into an image layer, a very common leak.

How should I rotate secrets?

Generate a new value, update it everywhere it is read, then revoke the old one and confirm nothing still uses it. Keeping the secret in one managed place per environment is what makes rotation a quick change instead of a hunt.

How do I keep secrets out of git?

Gitignore the files that hold them, scan the repo and history, and add a pre-commit or CI check that blocks new secrets. If one was ever committed, rotate it - removing it from the latest commit does not remove it from history.