Environment Variables

Managing production secrets and configuration

Where Variables Live

Production variables are stored on the server, per application, in /opt/frankendeploy/apps/<app>/shared/.env.local. The file is mounted read-only into the app and worker containers, survives deploys and rollbacks, and is never part of your repository.

Commands are run from the project directory: the application is the one in frankendeploy.yaml, the server is the argument.

What FrankenDeploy Sets for You

VariableValue
APP_ENVprod
APP_DEBUG0
SERVER_NAME:8080 (the port Caddy proxies to)
DATABASE_URLGenerated and injected with a managed database (database.managed: true). With an external database, set it yourself
SYMFONY_TRUSTED_PROXIES, TRUSTED_PROXIESPrivate subnets, so Symfony trusts Caddy’s X-Forwarded-* headers (real client IP, HTTPS). Not injected when you define either one yourself. See Behind the Proxy

The env.prod section of frankendeploy.yaml is not applied in production: it only feeds the generated compose.prod.yaml. Everything the app needs at runtime goes through the commands below.

Setting Variables

# Set a variable
frankendeploy env set prod MAILER_DSN="smtp://user:pass@smtp.example.com:587"

# Apply it right away with a rolling restart (see below)
frankendeploy env set prod MAILER_DSN="smtp://..." --reload

Without --reload, the change applies at the next deployment.

Secrets: use --from-stdin

Passing a secret as KEY=value leaves it in your shell history. With --from-stdin, the value never touches it:

# Interactive: hidden prompt
frankendeploy env set prod APP_SECRET --from-stdin

# Scripted: pipe the value
openssl rand -hex 32 | frankendeploy env set prod APP_SECRET --from-stdin

deploy refuses to start when APP_SECRET is missing (or DATABASE_URL with an external database), and offers to generate APP_SECRET for you in interactive mode.

Listing and Reading

frankendeploy env list prod            # all variables, sensitive values masked
frankendeploy env get prod DATABASE_URL

Values of keys containing SECRET, PASSWORD, PASS, KEY, TOKEN, DSN or DATABASE_URL are masked in env list and in --verbose output; env get shows the full value.

Removing

frankendeploy env remove prod OLD_VARIABLE

Bulk Operations

Push a .env file

Variables of a local file are merged into the server’s .env.local (existing keys are overwritten, others kept):

frankendeploy env push prod .env.prod
frankendeploy env push prod .env.prod --reload

Only files named .env* are accepted.

Pull from the server

frankendeploy env pull prod
# writes .env.prod.backup (permissions 600) in the current directory

The file is named after the server (.env.<server>.backup). Do not commit it.

Zero-Downtime Updates

--reload restarts the application without dropping a request, with the same mechanism as a deploy:

  1. A new container starts with the updated .env.local, on the same image
  2. FrankenDeploy waits for its health check
  3. Traffic switches to it (rename-based swap)
  4. The old container is stopped

If the new container never becomes healthy, it is removed and the old one keeps serving.

Required Variables

VariableNotes
APP_SECRETRequired by Symfony. Checked before every deploy
DATABASE_URLInjected automatically with a managed database. Required, and checked, with an external one
MAILER_DSN, MESSENGER_TRANSPORT_DSN, …Whatever your config/packages/*.yaml reference through %env()%

Security Notes

  • Every write (env set, env push, env remove, credentials generated by a deploy) enforces chmod 600 on the server file
  • The file is mounted read-only into the containers
  • Secrets are never written to frankendeploy.yaml or transmitted through Git
  • Sensitive values are masked in env list and in verbose logs (same detection list for both)
  • The file is stored in clear on the server disk, protected by its permissions: anyone with root or SSH access to the server can read it. Encrypting it there would not help, since the key would sit next to it. Use your provider’s disk encryption for backups and snapshots
  • The Symfony secrets vault works as usual: commit the vault, set only SYMFONY_DECRYPTION_SECRET with env set --from-stdin

Workflow Example

# 1. Production secrets, once
openssl rand -hex 32 | frankendeploy env set prod APP_SECRET --from-stdin
frankendeploy env set prod MAILER_DSN --from-stdin

# 2. Deploy
frankendeploy deploy prod

# 3. Later, change a value without downtime
frankendeploy env set prod FEATURE_FLAG=enabled --reload