Your app reads its settings from its environment. Alf has two kinds:
NODE_ENV=production or LOG_LEVEL=info.Both are stored encrypted. PORT is always set for you.
Open the service's Variables & secrets tab, edit, and Save changes. Nothing restarts yet: a banner lists what changed ("STRIPE_KEY (secret) added · LOG_LEVEL changed") with Deploy changes and Discard. Deploying restarts the containers with the new values (the same image or commit, no rebuild).
So you can change several things and send them out together. Project cards show "changes to deploy" while some are waiting.
From the command line: alf env set KEY=value sets and deploys in one go (--stage leaves it waiting), alf env push sets a .env file's lines on the service (passwords and keys as secrets, asking first), alf env pull writes the variables to a .env file, and alf run -- <command> runs your code on your machine with them, its databases connected.
Every deployment says what it did with them, in the Deployments tab and in what the deploy command and agents get back:
| Deploy | Waiting changes |
|---|---|
Deploy button, pushed code, an agent's deploy | go live with it ("Deployed with 2 staged changes…"), unless you ask to keep them staged (--keep-staged) |
| A rollback, a settings change | stay staged ("…still staged and NOT in this deployment") |
| A candidate | run in the candidate, and go live when it's promoted |
Paste a .env file adds its KEY=value lines as variables. Move anything sensitive to secrets afterwards.
Use the project network's names in your values:
REDIS_URL=redis://cache:6379
Values are taken as they are (one variable can't refer to another), so a connection string with a password goes in as a whole secret:
DATABASE_URL=postgres://app:the-password@db:5432/app (a secret)
A variable can point at another service in the same project instead of repeating its value:
DATABASE_URL = postgres://app:${{db.PASSWORD}}@${{db.HOST}}:${{db.PORT}}/app
db is the other service's address part. The point is that the password lives in one place: change it on the database, deploy the app, and the app has the new one — nobody has to remember the second copy.
Alongside whatever that service has set, four things about it are always readable:
HOST | its name inside the project |
PORT | the port it listens on |
URL | https://… if it's public, otherwise http://host:port inside the project |
NAME | what it's called |
A service can point at itself for those four — that's how it learns its own address (${{web.URL}}) — but not at its own variables.
Two things worth knowing. A reference resolves to what the other service has stored, and no further: if that value is itself a reference, it's left alone. And a reference to a service that isn't in the project is refused when you save it, rather than turning up in the container as the text you typed.