Production Operations on a Single Server · 5/10

Manage Production Secrets and Environment Files Safely

What will this help you do?

Separate ordinary configuration from secrets. Choose where to store .env files and service credentials, restrict access, avoid logging secrets, and plan rotation and incident response.

Who is it for?

Readers working through this problem and checking each step.

Before you start

Check the scope of this guide, then prepare the environment and materials it actually calls for.

Updated

On this page

Environment variables are a delivery method, not a security rating

Moving a password from code into .env is progress, but the file is not automatically safe. It can enter Git, backups, an image build context, a process environment, or debug logs. Separate information into public configuration, internal but non-sensitive configuration, and secrets that must be rotated promptly if exposed. Only the last group needs secret-handling rules.

Database passwords, session-signing keys, third-party API tokens, SMTP passwords, and private keys are common secrets. Domain names, log levels, and public site URLs generally are not. Hiding every setting can make troubleshooting harder without improving security.

Keep a secret inventory without storing the values

For each secret, record its name, purpose, consuming service, owner, source, creation date, rotation schedule, and revocation method. Do not put the actual value in the inventory. This tells you which processes are affected if one API token leaks and reveals credentials that remain active after their use has ended.

Use separate credentials for development, testing, and production. Avoid making a long-lived key from a personal account into a permanent server asset. Prefer a service account and the least privileges it needs over a full administrator key.

Set explicit file locations and permissions

For systemd, /etc/myapp/myapp.env can be read through EnvironmentFile. Root can manage the file while the application group receives read access only. Keep a Compose env_file outside publicly served or loosely shared locations, or restrict its permissions carefully. Exclude secret files from Git and the image build context.

bash
sudo install -d -m 750 -o root -g myapp /etc/myapp
sudo install -m 640 -o root -g myapp /private/path/myapp.env /etc/myapp/myapp.env
sudo -u myapp test -r /etc/myapp/myapp.env

Do not paste values into shared chat, shell history, or screenshots. Use an access-controlled editing method and inspect temporary files and command history afterward. If backups include secrets, encrypt and restrict those backups too.

Check what containers can expose

Do not bake production secrets into a Dockerfile with ARG or ENV; values can remain in image layers or build records. Compose environment variables can carry ordinary configuration. For highly sensitive material, consider a restricted file mount or a platform secrets mechanism. A process with permission inside the container can still read what it is given, so least privilege and isolation remain necessary.

docker compose config may expand variables. Do not upload its output to a public issue. Health checks and error responses must not echo complete connection strings either.

Design rotation so two keys can briefly coexist

Replacing a database password or signing key in one step may disconnect old processes or sign out everyone. If the system supports two valid keys during a transition, add the new one, deploy and verify, then revoke the old one. If it does not, schedule a maintenance window: back up, update the server side, restart consumers in order, and confirm the old key no longer works.

Rotating a session-signing key may invalidate existing sessions; plan for that user impact. For a database, a new account can sometimes be created and connections moved before the old account is removed. Update the inventory when rotation finishes, not just the environment file.

If a leak is suspected, revoke first

Limit exposure, revoke or rotate the credential, check for unusual use, close the leak path, and then review how it happened. Removing a string from Git history is not enough: old commits and clones may retain it. Treat a real exposed credential as compromised.

Keep a timeline of discovery, affected services, revocation, and any suspicious activity. Do not copy the secret into the incident report while investigating. Check public repositories, CI logs, and image registries as possible exposure paths.

What counts as done?

Production secrets are absent from the repository and images; services read restricted files with minimal privilege; development and production credentials differ; logs do not echo values; and every secret has an owner and revocation method. Rehearse at least one rotation. Next, test whether the system is truly recoverable with a database backup and restore.

Sources

  1. Best practices for working with environment variables in Docker ComposeDocker Docs
  2. systemd.execsystemd

Next steps

Continue with the next useful task; you do not need to read everything at once.

  1. Deploy Open WebUI with Docker and Persistent Storage →
  2. Install Ollama on Linux and Expose It Safely →
  3. Deploy Dify with Docker Compose: A Production-minded Guide →
RESOURCES I USE · REFERRAL

Two practical services to consider only when needed

This is not an automated ranking, and neither service is necessary for everyone. These are services I use, with the use case and limitations kept visible.

Referral disclosure: these links contain my referral information. I may receive a platform benefit if you sign up or order, at no additional charge from AIOOS. Check the order page for current pricing, availability, regions and terms. Read the full affiliate disclosure