Skip to content

Env-var encryption

Every environment variable a service uses is stored in the services.env_json column. Before writing, Pier encrypts the JSON with AES-256-GCM using a 32-byte key from /opt/pier/.env:

PIER_SECRET=<32 bytes base64>

Encrypted values carry an ENC: prefix so Pier can distinguish them from legacy plain-text rows.

If an attacker exfiltrates pier.db alone, they get nothing — the ciphertext is useless without the key.
If an attacker exfiltrates only .env, there is no data to decrypt.
An attacker needs both to read secrets.

This is the same model Coolify uses (Laravel APP_KEY + encrypted cast). Simpler than a Vault, and appropriate for a single-tenant self-hosted tool.

On first run, if /opt/pier/.env does not exist, Pier generates a fresh PIER_SECRET and writes it with chmod 600. A one-time snapshot of the database is saved as pier.db.pre-encryption in case you need to roll back to plain-text mode for debugging.

Rotation is rare. When needed:

  1. Stop Pier.
  2. Run the rotation helper (planned): pier rotate-secret.
  3. Start Pier.

Until the CLI helper ships, export env vars through the UI, rotate PIER_SECRET manually, and re-import.

  • Service names, images, ports, and domains — these are not secrets.
  • Deployment metadata and logs — surface data, not credentials.
  • The Traefik dynamic config files — they contain hostnames, not secrets.