Change config on a live app
A deployed app reads its .env once, when its containers are created, and caches the result. So every change here has two halves, the file and the restart, and the quickest tool is the one that never forgets the second half.
Before you start
The project is deployed, as First deploy to a fresh VPS leaves it. The .env of a deployed app lives in the project's directory on the server and nowhere else. It is not in the image and not in the repository, so a release never changes it.
You are in the project directory, and the server knows your key.
$ ssh deploy@203.0.113.10 trueThe env:* commands take the environment as their last argument, and without one they mean production. This page names it every time. Where the host comes from is the same for all three: deploy.environments.<environment> in grim.json, then deploy, then your own ~/.grim/servers.json.
The ritual, in order
Six steps, and you rarely need all of them. The first, the third and the fifth change something on a server. Each restart takes the site away for a few seconds.
Change one value
grim env:setreplaces one line in the server's.env, or adds it, and recreates the containers that read the file. There is no confirmation and no flag for the restart. It always happens.$ grim env:set MAIL_HOST=smtp.example.com production Recreating PHP services (app, horizon, scheduler)...The summary names the project, the host and the key, and says whether the key was
updatedoradded. The value is not printed. A project with Reverb gets that container recreated too, although the line does not mention it. For a password, prefer step three: what you type here stays in your shell history.Do not add
-vtogrim env:setorgrim env:pull. Verbose mode echoes every SSH command's output, and one of those commands reads the file. The whole production.envscrolls through your terminal, and through any recording of it.Ask the app what it sees
The file is one thing and the running app another.
grim remote:artisanasks the app itself. Ask for the config key, not the variable: the app caches its configuration at start, andenv()returns nothing after that.$ grim remote:artisan tinker --execute="echo config('mail.mailers.smtp.host');" smtp.example.comgrim remote:artisantakes no environment. It reaches the one registered first on your machine, which is production in the usual case. For staging, connect yourself:ssh deploy@203.0.113.10, thencd /opt/shop-staginganddocker compose exec app php artisan tinker.Change many values at once
When several values change together, or a line has to go away, make the round trip.
grim env:pullwrites the server's file next to your own as.env.production. You edit it.grim env:pushsends it back, and--restartmakes the app read it.$ grim env:pull production $ $EDITOR .env.production $ grim env:push production --restart Push .env.production (42 variables) to 203.0.113.10:/opt/shop/.env?Read the target in the question before you answer
y. The whole file is replaced, nothing is merged and the server keeps no backup. Pull right before you edit, never from a copy of last week, or a value a colleague set yesterday is gone. Without--restartthe file is on the server and the app keeps its old values until the next release recreates the containers. That is a way to stage a change, and a way to confuse yourself.Throw the copy away
.env.productionholds the database password, the app key and the mail credentials in plain text. A project made by grim ignores the file in git. It still has no reason to stay on a laptop.$ rm .env.production $ git status --shortgit statusshould not list the file. When it does, your.gitignoreis not the one grim wrote. Fix that before the nextgit add.Rotate a secret the whole team shares
Some keys are the same in every project of the team, and the registry keeps them once.
grim secrets:setreplaces the value there. That is all it does. A shared secret reaches a project only whengrim installwrites it into the local.env, and it never reaches a server by itself. So rotating is three jobs: the registry, every deployed environment, and every checkout.$ grim secrets:set GEOIP_API_KEY=abc123 $ grim secrets:list $ grim env:set GEOIP_API_KEY=abc123 production $ grim env:set GEOIP_API_KEY=abc123 staging $ grim install✓ Secret GEOIP_API_KEYand the registry's status is the first answer. The list shows keys and descriptions, never values.grim installwrites every shared secret over the same key in the local.env, so a value you set by hand under a shared name does not survive it. Install also builds and starts the local stack. When that is more than you want today, put the new value into your local.envby hand and go to step six.Apply an edit on a stack you run yourself
Optional. On your own machine, and on a shared dev box, the
.envis a file you edit directly. The rule about the restart is the same, andgrim reloadis the restart.$ grim reloadIt recreates every container of the stack and clears the config cache. It is not for a production server. There the restart belongs to
grim env:setandgrim env:push --restart.
What you have now
The server's .env carries the new values and the PHP containers were recreated on them: app, and horizon, scheduler and reverb where the project has them. nginx and Redis were left running.
Nothing in the repository changed, and nothing in the image. The next release keeps the file as it is. Another environment of the same project has its own .env in its own directory, and got only what you sent there by name.
A shared secret now has the same value in the registry, on the servers you named and in your checkout. Your colleagues get it the next time they run grim install.
When it does not work
“No host for "shop" (production): set deploy.environments.production.host in grim.json or run "grim server:add-project".”
Neither grim.json nor your servers.json knows a server for that environment. Write the host into grim.json. Then everyone who clones the project can use the env:* commands without registering anything.
“Invalid .env key: mail-host”
A key may hold letters, digits and underscores, and may not start with a digit. Nothing was changed on the server. Invalid format. Use: grim env:set KEY=VALUE means there was no = at all.
“.env.production not found.”
There is nothing to push. The pull in step three was skipped, or it ran for another environment. The argument picks the file and the server together, so grim env:push staging looks for .env.staging.
The value is on the server and the app still uses the old one
The push ran without --restart. Push again with it. Clearing the config cache by hand does not help: the container built its cache from the old file when it started, and only a new container reads the new one.
The push was “Aborted.” in a script
Without a terminal the question answers itself with no, and the command still exits successfully. A pipeline needs --force. grim secrets:delete behaves the same way and has no --force at all, so under --no-interaction it deletes nothing and says nothing.
“Failed to set secret: HTTP 403 Forbidden”
The registry knows you, and your account may not manage shared secrets. Ask whoever administers the registry. The servers can still be updated with grim env:set.
Questions
Can grim env:set remove a variable?
No. grim env:set KEY= sets it to an empty string, and the app then sees that instead of the default from its config file. To delete the line, make the round trip in step three.
How do I take a shared secret out of the registry?
grim secrets:delete GEOIP_API_KEY asks once and deletes it. Every .env that already holds the value keeps it, on laptops and on servers. When the credential must stop working, revoke it at the service that issued it.
Next recipe
Debug production
Look at the server, read what the app logged, search the log on the server, question the app through artisan, turn the log level up for a while, and go inside the container when nothing else answers.
Read itSpells used here
grim env:set
Change one server variable
Set a single KEY=VALUE in the .env of a deployed environment and recreate the containers so the app runs on it at once.
grim env:pull
Fetch a server's .env
Copy the .env of a deployed environment into the project as .env.<environment>, to read it or to edit and push back.
grim env:push
Send a .env back
Upload your edited .env.<environment> over the .env of a deployed environment, and optionally recreate the containers that read it.
grim secrets:set
Store a shared secret
Save a credential in the registry once, so grim install writes it into the .env of every project set up after that.
grim reload
Apply a changed .env
Recreate every container of the project and clear the config cache, so the app reads the .env as it is now.
grim remote:artisan
Run artisan on the server
Run one artisan command inside the app container of the deployed project, from your own terminal.