Can I use nginx environment variables within static files that nginx serves?
If I use a environment variable in the nginx config, and nginx is configured to serve only static files (html,js,css - e.g. a AngularJs app), is there any way I can use the environment variable within a JS file that nginx serves? Or is the only way to do this to run a non-static server, e.g. io.js, php, etc.
Btw you can't use environment variables natively in nginx's configuration.
When I talk about environment vars in the nginx config I mean like in this post: How can I use environment variables in Nginx.conf where they use env APP_WEB_1_PORT_5000_TCP_ADDR;
and $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"};
Explain exactly your use case
My specific use case is that I have a nginx-powered AngularJS app in a docker container. The app is a "Single Page Application" that consumes a API running on another system altogether. Currently I run a different docker container between production and staging because the app has some config that is different, for example the Google-Analtyics code. This environment-specific data is kept in a config.js
file and the values are currently hardcoded, one value for the master
branch in git, and different value for the staging
branch. I want to change the design so I can use the same container for both production and staging. I want to pass a ENV var into the container when I run it (docker run -e GACODE=UA-12345-6 ...
) and have nginx use the ENV var (via env GACODE;
and $ENV{"GACODE"}
so the config.js
file can use the GoogleAnaltyics codes that are passed in, rather than hardcoding them). I don't know whether this is possible or not (hence the question ;)). Using only nginx makes my container a single-process, whereas if I have to use io.js then I'll need multiple linked containers and more moving parts is more complex).
Solution 1:
I've been looking into resolving the same issue as the OP and this post came up in Google search, so thought I'd add a potential solution.
This post outlines how you can expose an environment variable within nginx config: https://blog.doismellburning.co.uk/environment-variables-in-nginx-config/
And you can return content without having a file in the file system: Reply with 200 from Nginx config without serving a file
Putting these two things together we end up with the following:
env MY_ENV_VAR;
# Snip
http {
# Snip
server {
# Snip
location ~ ^/config.js$ {
add_header Content-Type text/javascript;
set_by_lua $env_var 'return os.getenv("MY_ENV_VAR")';
return 200 'const MY_ENV_VAR = \'$env_var\'';
}
}
}
This has worked for me in a test environment. It's a little cumbersome (not sure it's better than auto-generating a file on Docker launch that contains the vars you need).
Hope that helps.
Solution 2:
I experimented with using sub_filter and nginx environment variables but concluded that it isn't possible.
For example, this shows my experiments and shows using ENVs passed to nginx does not work within a server
block:
env TOMTEST1; # OK - makes $ENV{"TOMTEST1"} available but NOT in server block.
http {
server {
# set $TOMTEST1 $ENV{"TOMTEST1"}; # KO - DOES NOT WORK - NGINX WONT START
set $TOMTEST2 'tomtest2 Var'; # OK - THIS DOES WORK OK
sub_filter 'TOM_TEST2' $TOMTEST2; # OK - but not useful to me.
sub_filter 'TOM_TEST3' 'tomtest3 Var'; # OK - but not useful to me.
sub_filter_once off; # Don't stop at the first match, replace all of them
sub_filter_types "text/javascript" "application/json"; # Apply to these mime types in addition to text/html
# Serve static files
location / {
try_files $uri /index.html =404;
}
...
where the static config.json file that I'm serving has various test strings like the following:
{
"environment": "local",
"test1": "$ENV{"TOMTEST1"}",
"test3": "TOM_TEST2",
"test4": "TOM_TEST3",
}
As @AD7six mentioned, the way to proceed is have a script that runs before nginx starts to produce a valid config file from placeholders. But this begs the question, if a script is going to run before nginx starts, I may as well set the contents of my config.json
file in that script and not bother with sub_filter
at all.
Solution 3:
sub_filter
If you want a simple string-replace, you can use sub_filter. For example:
server {
sub_filter "REPLACE_THIS" "with this";
sub_filter_once off; # Don't stop at the first match, replace all of them
sub_filter_types "text/javascript" "application/json"; # Apply to these mime types in addition to text/html
# Everything else
}
However it's not possible to read an environment variable in nginx config - you can of course script writing the nginx config file any way you need/want to produce a valid config file and then reload nginx.