Modify process.env for a running NodeJS process
Warning: this might not solve the problem you have. For me, I wanted to change the config
on a HTTP server without restarting it and although I’ll show you how to edit
process.env
, if you have other vars that read values from process.env
then they won’t
be updated.
If you have a running process and you need to change values in process.env
, I’ll show
you the steps. There’s a screencast and a text version of the same steps.
Screencast
Text version
Starting a demo process
For this example we need a process to attach to. We’ll just use a nodejs REPL:
BLAH=11 node
Find the PID of the node process
We need the process ID (PID) of the nodejs process. One way to find it is with
$ ps -e | grep node
1234 pts/21 00:00:01 node
For this example, we’ll use 1234
as the value for the PID.
Option 1: connect a local debugger to the process
If you have a shell on the machine that’s running the process, you can use the CLI
debugger. Note: if the current user isn’t root or the user that started the process,
you’ll have to sudo
the command.
node inspect -p <pid>
# so for our example
node inspect -p 1234
Once in the debugger, jump into a REPL with:
debug> repl
Option 2: enable remote debug mode for the nodejs process
Even though the process wasn’t started as debug, we can trigger debug mode with the USR1
signal. Note: if the current user isn’t root or the user that started the process, you’ll
have to sudo
the command.
kill -s SIGUSR1 <pid>
# so for our example
kill -s SIGUSR1 1234
You won’t get any output from the command, but if you can see the process logs (or the output of the repl process), you’ll see two lines printed about debugging enabled
Debugger listening on ws://127.0.0.1:9229/357cd2bb-f87a-4eb6-8107-ef3b36b4b042
For help, see: https://nodejs.org/en/docs/inspector
The server most likely doesn’t expose the 9229
debug port, but we can workaround that by
using port forwarding through an SSH tunnel. If we assume this is the command you use to
ssh to the machine:
ssh -i /some/key.pem [email protected]
…then you’d modify that command to forward the 9229
port on the remote machine to port
30001
on your local machine:
ssh -i /some/key.pem -nNT -L 30001:localhost:9229 [email protected]
…then you can connect the Chrome, vscode, etc debugger on your local machine to port 30001.
Check the value of the env
Before we change the value, let’s check what it is.
> process.env.BLAH
'11'
Edit the value
It’s just the syntax you’d always use.
process.env.BLAH = '22'
Check the change took effect
Run the “Check the value” step again.
Disconnect
If you’re in the CLI debugger, use Control+D
to exit the repl and the debugger.
Things I’m not sure of:
- how to disable the debugger when we’re done
- is there any performance hit for leaving the debugger enabled?
How to automate the debug commands
The process debug uses JSON over websockets to communicate. If we know the commands and can send them into the websocket, we can write a script to automated it.
Unfortunately you can’t use curl
to send data through a websocket, you can only check
the initial handshake works. To actually send data, we need something that understands
websockets. I use wscat here but there are others
(websocat for example).
Thanks to this GitHub comment we have a way to see what messages are sent when using the debugger interactively. To use this in the example above, you’d run the debugger with
NODE_DEBUG=inspect node inspect -p 1234
…and then send something in the REPl. For our command to check the value of the env var, we’ll see the message and response are:
> {"id":10,"method":"Runtime.evaluate","params":{"expression":"process.env.BLAH\n","objectGroup":"node-inspect","generatePreview":true}}
< {"id":10,"result":{"result":{"type":"string","value":"11"}}}
Then if we change the var, the message and response are:
> {"id":10,"method":"Runtime.evaluate","params":{"expression":"process.env.BLAH = '22'\n","objectGroup":"node-inspect","generatePreview":true}}
< {"id":10,"result":{"result":{"type":"string","value":"22"}}}
Now we have a tool to send data into the websocket and we know the message to send, we can automate it with
wscat \
-c ws://127.0.0.1:9229/357cd2bb-f87a-4eb6-8107-ef3b36b4b042 \
-x '{"id":10,"method":"Runtime.evaluate","params":{"expression":"process.env.BLAH = \"55\"\n","objectGroup" :"node-inspect","generatePreview":true}}'