Security guide · Romania

How do I keep .env files and API keys out of git in production?

An API key that lands in a commit is public from the moment you push: bots scan new repositories within minutes. Deleting it from the repo does not recall it, because git history and forks keep it. The only fix is rotation.

Updated:

Short answer

  1. Manage secrets by keeping them out of code: .env is listed in .gitignore, and the real values live in the environment variables of your CI platform and your server.
  2. In Docker, never put secrets in ARG or ENV in the Dockerfile, because they stay in the image layers; use Docker secrets or --mount=type=secret instead.
  3. Scan every commit with gitleaks, in pre-commit and in CI, so a secret never reaches the history.
  4. If a secret did reach git, rotate it immediately and only then rewrite the history with git filter-repo.
First action after a leak
rotate the secret, do not just delete it from the repo
Where not to put secrets in a Dockerfile
ARG and ENV, they stay in the image layers
GDPR deadline to notify a breach
72 hours to ANSPDCP

Why deleting it from the repo fixes nothing

Git keeps every version of every file. A commit that deletes .env leaves the previous version reachable through history, in every clone, fork or mirror. Attackers' scanners extract cloud keys, payment tokens and database passwords from new commits within minutes. Even in a private repository, everyone who ever had access holds a copy. The order is fixed: rotate the secret first, then clean the history with git filter-repo, then look for the cause.

Where secrets live in each environment

Locally they sit in .env, ignored by git, with a committed .env.example that lists the keys without values. In CI they sit in the platform's secret variables, masked in logs. On the server they go in a systemd EnvironmentFile with 600 permissions or in a secrets manager read at startup. In Docker, a secret written in ENV appears in docker inspect and in every image layer; mount it as a file through Docker secrets or, at build time, with --mount=type=secret.

What Romanian law says

A leaked key that gives access to personal data is a security breach under GDPR: Article 33 requires notifying ANSPDCP within 72 hours of discovery, and Article 32 requires the technical measures that would have prevented it. A leaked SPV access token or e-Factura certificate means a third party can read or issue invoices in your company's name. For entities registered with the DNSC under OUG 155/2024, managing cryptography and access is among the measures required by Article 11 of OUG 155/2024.

Where a secret lives in each environment and what is forbidden
EnvironmentCorrect placeForbidden
Local, development.env ignored by git, .env.example without valuesReal values in .env.example or in code
CI/CDSecret variables of the platform, masked in logsSecrets in pipeline files inside the repo
Docker build--mount=type=secretARG or ENV carrying secret values
Docker runtimeDocker secrets mounted as a fileENV in the Dockerfile or in a committed docker-compose file
TeamVault, SOPS + age, the platform's managerChat, e-mail, shared documents

Commands and configuration

.gitignore and scanning before commit
# .gitignore
.env
.env.*
!.env.example

# pre-commit
gitleaks protect --staged --redact
Docker: a build-time secret with no trace in the image
# Dockerfile
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci --omit=dev --ignore-scripts

# build
docker build --secret id=npm_token,src=./npm_token.txt -t example.ro/app .
systemd: environment file with restrictive permissions
# /etc/example/app.env (chmod 600, owner root)
DATABASE_URL=postgres://app:...@127.0.0.1/example_db

# in the systemd unit
EnvironmentFile=/etc/example/app.env

Practical examples

Web agency in Timisoara

A developer committed .env with a payment processor's production key to a public portfolio repository. Within hours there were attempted transactions from other countries. The key was rotated at once, and the history was cleaned afterwards.

Accounting firm in Bucharest with an e-Factura integration

The SPV access token was written as ENV in the Dockerfile and ended up in the firm's image registry, open to every employee. It was moved to Docker secrets and rotated, and access to the registry was narrowed.

Common mistakes

  • Believing the problem is solved once .env is removed from the repo, when the secret stays in history and must be rotated.
  • Putting secrets in ARG or ENV in the Dockerfile and pushing them to the image registry with every push.
  • Using the same key in every environment and every project.

How 4hosting helps

  • Hardening & Compliance explicitly covers access, secrets and keys: moving them out of code into a secrets manager and rotating them on a schedule.
  • CI/CD & Monitoring sets up pipelines with secrets held in the platform and gitleaks scanning; Docker & Kubernetes builds images with no secrets in their layers.
  • The DevOps hours included each month, 1–8 on VPS, 5–10 on dedicated servers depending on the plan and 10 on GPU servers, are requested from the client panel, in the “DevOps Services” section; a secrets audit usually fits inside them.

Related questions

  • I pushed an API key to GitHub. What do I do, in order?

    Rotate the key at the provider and immediately check the usage log for foreign activity. Then rewrite history with git filter-repo and force-push, telling collaborators to re-clone. Finally add gitleaks to pre-commit and CI. If the key gave access to personal data, assess your notification duty.

  • Are environment variables safe on a server?

    They are acceptable if the file they come from has 600 permissions and belongs to root, and the process runs as a dedicated user. They remain visible in /proc to root and in crash dumps. For critical secrets, a secrets manager with read-at-startup and auditing is better.

  • How are Docker secrets different from ENV?

    ENV is written into the image and container metadata, visible through docker inspect and in every image layer. Docker secrets mount the secret as an in-memory file under /run/secrets, only in the containers that declare it, without appearing in the image or in logs.

Request a security review

A production server starts getting attacked within minutes of receiving a public IP address, however small the project is. This guide ranks the controls by impact, from SSH access to incident response, with the Romanian rules that apply along the way. Every topic has its own page with a short answer, examples and ready-to-use configuration.