Problem
Vue.js applications rely on .env
files for specifying application settings. This is fine if the application runs
- in development mode via
npm run serve
- in production mode with the internal Node.js webserver via
npm run serve
- in production mode as packaged applications via
npm run build
.
As soon as the application gets packaged and is distributed into a container (e.g. Docker), there is no way to set dynamically application settings during the container's start-up. The reason for this is that the Vue.js application effectively runs on the client and is only distributed through the webserver. Having different application settings per environment would require a specific container for each environment. An application setting which differs from environment to environment could be the URL of the application's backend API endpoint.
Solution
To make your application settings configurable, the application settings must be externalized into its own config.js
file. This config.js
file is then loaded by Vue.js. If any application setting is present in there, it will override the default setting.
The generation of the config.js
could be done in a few different ways:
-
Dynamically generate the
config.js
file through a script: This can either be accomplished through any scripting language like Python and PHP or by using ngx_lua and itscontent_by_lua_block
statement. A drawback for this approach is that the underlying container gets polluted with dependencies you probably don't want to have. -
Mounting the
config.js
file from the container's host: Depending upon your environment you could either use Docker's-v
argument to specify a customconfig.js
or use Kubernetes'volumeMounts
to mount a secret which has theconfig.js
content in it. Both approaches do work but they are feeling a little bit clumsy. -
Generate the
config.js
during container's startup: During container startup, a small shell script is executed which checks if required system environment variables exist and, if so, writes them back to theconfig.js
file. When using Docker, the-e
flag can be used, in Kubernetes you can use theenv
section.
This document describes approach 3.. We are using 1 and 2 as starting points.
Runtime
public/config.js
file
Introduce the Create a new file public/config.js
in your Vue.js application. In this, each application setting is defined:
// will be overwritten during container startup
window.APPLICATION_SETTING_API_ENDPOINT_URL = null;
window.APPLICATION_SETTING_2 = null;
// ...
window.APPLICATION_SETTING_N = null;
public/config.js
file
Load the custom In your public/index.html
include the config.js
file:
<!DOCTYPE html>
<html lang="">
<head>
<!-- ... -->
<script src="config.js" type="text/javascript"></script>
</head>
<body>
<!-- ... -->
</html>
window.*
settings into the Vue.js application
Inject the When using TypeScript, you'll have to add the following to your src/application.config.ts
file:
// gain access to the *window* global
declare const window: any
// prefer the `window.` application setting over `environment`.
export const API_ENDPOINT_URL = (window.APPLICATION_SETTING_API_ENDPOINT_URL ? window.APPLICATION_SETTING_API_ENDPOINT_URL : environment.APPLICATION_SETTING_API_ENDPOINT_URL);
export const APPLICATION_SETTING_2 = (window.APPLICATION_SETTING_2 ? window.APPLICATION_SETTING_2 : environment.APPLICATION_SETTING_2);
// ...
export const APPLICATION_SETTING_N = (window.APPLICATION_SETTING_N ? window.APPLICATION_SETTING_N : environment.APPLICATION_SETTING_N);
Container startup
config.js
based upon an existing config.js
file
Create a new The following bash script reads the existing, unpopulated config.js
file and fills in each application setting if the environment variable is present. The script does not use any heavy sed
or awk
features by purpose.
#!/bin/bash
SRC=${1:-}
if [ -z $SRC ] || [ ! -e "$SRC" ]; then
echo "Source file '$SRC' does not exist"
exit 1
fi
OUTPUT="// generated"
while IFS="" read -r line || [ -n "$line" ]; do
use_line=$line
if [[ $line =~ "window." ]]; then
ENVIRONMENT_VARIABLE_NAME=`echo $line | sed -re 's/window\.(.*)\s+\=(.*);/\1/g'`
value=${!ENVIRONMENT_VARIABLE_NAME}
if [[ ${value} && ${value-x} ]]; then
use_line="window.$ENVIRONMENT_VARIABLE_NAME = '$value';"
fi
fi
OUTPUT="$OUTPUT\n$use_line"
done <$SRC
echo -en $"$OUTPUT"
Put it beside your Dockerfile
as create_config_js.sh
.
Create runtime configuration during container build.
In your Dockerfile
, the create_config_js.sh
has to be copied to the target image:
WORKDIR /
COPY create_config_js.sh .
RUN chmod +x create_config_js.sh
COPY docker_entrypoint.sh .
RUN chmod +x docker_entrypoint.sh
EXPOSE 80
CMD ["/bin/sh", "docker_entrypoint.sh"]
Update your docker_entrypoint.sh
to execute configuration generation
SRC=/usr/share/nginx/html/public/config.js
# quoting is required to keep line breaks
NEW_CONFIG="$(./create_config_js.sh $SRC)"
echo "$NEW_CONFIG" > $SRC
# start webserver, e.g. apache or nginx
nginx -g daemon off;