13

at work I need to make it possible to change the environmet variables at runtime, from an Azure web service, through docker and nginx. I tried this, this and some similar solutions, but I couln't get any of them to work.

I also couldn't find any solution online or any article/thread/post that explained if this is even possible, I only always find the text that vite statically replaces the env variables at build time.

During our CI/CD pipeline vite gets the env variables but our Azure admins want to be able to configure them from Azure, just for the case of it.

Does anyone know if this is possible and or maybe has a solution or some help, please ? :)

11 Answers 11

31

We Can Change the envirement variable of vite at runtime when running our docker container with nginx Follow the below steps

Step-1: In .dockerignore file put your .env file so that the environment variables are not get exposed

Let's say you have following variable in .env file

VITE_API_URL=http://localhost:5000
VITE_KEY=jo2i3jkj3kj

Step-2: Create a .env.production file don't put this file in .dockerignore file.

Inside the .env.production write your env vars like this it can be prefixed with any characters like here I have prefixed them with MY_APP_

VITE_API_URL=MY_APP_API_URL
VITE_APP_KEY=MY_APP_KEY

We are using a prefix MY_APP_

Step-3: Create a env.sh file in root directory

Inside the env.sh file put this code

#!/bin/sh
for i in $(env | grep MY_APP_) // Make sure to use the prefix MY_APP_ if you have any other prefix in env.production file varialbe name replace it with MY_APP_
do
    key=$(echo $i | cut -d '=' -f 1)
    value=$(echo $i | cut -d '=' -f 2-)
    echo $key=$value
    # sed All files
    # find /usr/share/nginx/html -type f -exec sed -i "s|${key}|${value}|g" '{}' +

    # sed JS and CSS only
    find /usr/share/nginx/html -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|${key}|${value}|g" '{}' +
done

Step-4: Create a Dockerfile put the following code

# Stage 1: Build Image
FROM node:18-alpine as build
RUN apk add git
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Stage 2, use the compiled app, ready for production with Nginx
FROM nginx:1.21.6-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY /nginx.conf /etc/nginx/conf.d/default.conf
COPY env.sh /docker-entrypoint.d/env.sh
RUN chmod +x /docker-entrypoint.d/env.sh

Step-5: Run below command to create a docker image

docker build -t image-name

Step-6: To Start the container use the below command to start and add env-vars at runtime

docker run --rm -p 3000:80 -e MY_APP_API_URL=api_url -e MY_APP_KEY=key image-name

When we run the above command all the env vars which start with prefix MY_APP_ it will replace the value which we provided using -e flag this is how we can add the env-vars in vite react app at runtime

Hope my solution works in your case Thank you!

Sign up to request clarification or add additional context in comments.

2 Comments

This is excellent. Very neat. Thank you for sharing.
Thank you – first proper solution to this problem that I've seen!
8


It is not possible to dynamically inject Vite env variables. But what is possible, is to change the window object variables (assign them on runtime).
WARNING!!! DO NOT EXPOSE ANY SENSITIVE VARIABLES THROUGH THE WINDOW OBJECT. YOUR FRONT-END APPLICATION SOURCE IS VISIBLE TO ANYONE USING IT

Steps:

  1. Create your desired env files and place them in <rootDir>/public. Let's call them env.js and env-prod.js.

  2. Inside your env.js and env-prod.js You want to assign your desired variables using var keyword. Also, you will have to reference these values in your source like window.MY_VAR to be able to use them.

  3. Create a script tag inside your <rootDir>/index.html like this:
    <script type="text/javascript" src="./env.js"></script>.
    IMPORTANT!!! type="text/javascript" is important, because if You specify module, Vite will include your env.js source inside your minified index.js file.

  4. Vite config (optional):

  plugins: [react(), tsConfigPath()],
  build: {
    emptyOutDir: true, // deletes the dist folder before building
  },
});
  1. How to serve the env files on runtime. Create a node server which will serve your frontend application. But before serving the env.js file, depending on our process.env.ENVIRONMENT you can now choose which env.js to serve. Let's say my node server file is stored at <rootDir>/server/server.js:
const express = require("express");
const path = require("path");

const app = express();

const env = process.env.ENVIRONMENT || "";

console.log("ENVIRONMENT:", env);

const envFile = path.resolve("public", env ? `env-${env}.js` : "env.js");

const indexFile = path.resolve("dist", "index.html");

app.use((req, res, next) => {
  const url = req.originalUrl;
  if (url.includes("env.js")) {
    console.log("sending", envFile);
    // instead of env.js we send our desired env file
    res.sendFile(envFile);
    return;
  }
  next();
});

app.use(express.static(path.resolve("dist")));
app.get("*", (req, res) => {
  res.sendFile(indexFile);
});

app.listen(8000);

  1. Serve your application build while running node ./server/sever.js command in your terminal.

  2. Finally:
    my env.js contains var RUNTIME_VAR = 'test'
    my env-prod.js contains var RUNTIME_VAR = 'prod'
    After I set my process.env.ENVIRONMENT to prod. I get this file served: enter image description here

Comments

6

My Solution is that it schould work with the links from my question. I use this approach and it works, the only thing that needs to be thought of is to use a different variable name/prefix (e.g. "APP_...") so vite doesn't change them at build time. I created a config file wich resolves the variable, for example if the app is in production than it uses the new Variable "APP_.."(which comes injected from nginx/ docker) or use "VITE_..."-variable if "APP_.." is undefined.

1 Comment

Thanks man! I've been googling this all day and only thanks to you I found the solution.
3

I ended up just replacing the variable in js before the container started.

Straight forward, No code changes.

Dockerfile

FROM nginx:alpine
COPY dist/ /usr/share/nginx/html
COPY docker-entry.sh /docker-entry.sh
RUN chmod +x /docker-entry.sh
EXPOSE 80
ENTRYPOINT ["/docker-entry.sh"]
CMD ["nginx", "-g", "daemon off;"]

docker-entry.sh

#!/bin/sh

# replacing Vite's static env vars with injected one
vars=$(printenv | grep '^VITE_' | awk -F= '{print $1}')
find "/usr/share/nginx/html" -type f -name "*.js" | while read file; do
    for var in $vars; do
        echo "Replacing $var in $file"
        sed -i "s/\($var:\"\)[^\"]*\"/\1$(printenv "$var")\"/g" $file
    done
done

exec "$@"

Make sure to have template .env when doing vite build
else the .js output won't have anything to replace.

Comments

3

I came up with a solution and published it as packages to the npm registry.

With this solution, you don't need to change any code:

// src/index.js
console.log(`API base URL is: ${import.meta.env.API_BASE_URL}.`);

It separate the build step out into two build step:

During production it will be statically replaced import.meta.env with a placeholder:

// dist/index.js
console.log(
  `API base URL is: ${"__import_meta_env_placeholder__".API_BASE_URL}.`
);

You can then run the package's CLI anywhere to replace the placeholders with your environment variables:

// dist/index.js
console.log(
  `API base URL is: ${{ API_BASE_URL: "https://httpbin.org" }.API_BASE_URL}.`
);
// > API base URL is: https://httpbin.org.

Here is the documentation site: https://import-meta-env.org/.

Feel free to provide any feedback.

1 Comment

This worked well for me using react + vite + docker, thanks for the package !
0

here is the Dockerfile

FROM node:alpine3.14 AS buildJS
WORKDIR /var/www/html
COPY . .
RUN apk add --no-cache yarn \
    && yarn && yarn build

FROM nginx:stable-alpine
WORKDIR /var/www/html
COPY --from=buildJS /var/www/html/dist .
COPY ./docker/conf/nginx.conf /etc/nginx/conf.d/default.conf
COPY ./docker/conf/config.json /etc/nginx/templates/config.json.template

ENTRYPOINT []

CMD sleep 5 && mv /etc/nginx/conf.d/config.json config.json & /docker-entrypoint.sh nginx -g 'daemon off;'

I'm building the project in the first stage without any envs, in the second stage I'm copying the files and then creating the config.json file based on envs that are passed at run time with envstub feature of nginx.

then from the project I called the config.json file and load the envs from there but be careful you can not import it because imports will be resolved at build time instead you have to get it with fetch or axios or any equivalents

Comments

0
// ./docker-compose.yml
version: '3'
services:
 front:
  /* some params */
  build:
   dockerfile: ./Dockerfile
   context: ./front
  env_file: .env // its important, no need environment

// ./front/Dockerfile - do not use

// ./.env
// https://vitejs.dev/guide/env-and-mode.html
// VITE_* prefix is needed
VITE_SOME_VAR=value 

// ./**/some_script.ts
// Vite + Vue
console.log('expected: ', import.meta.env.VITE_SOME_VAR) // expected: value

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
0

It's a little late so I am posting this answer for anyone who comes across this question in the future. IMO, the better solution would be similar to what Jitsi uses in their docker images. The idea is to add a blank config.js file in the public folder and add it to <rootDir>/index.html as a script (as mentioned in step 3 of this answer). Now, you can simply create your own config.js file and mount it to the running container. This will allow you to change the config whenever you wish to, and not alter any parts of the container that you don't want.

NOTE: User var to declare variables so that they are accessible. Also, add the script in a place where it can be included before the main bundle.

Comments

-1

First create .env file in project root,then define a variable in .env

e.g:VITE_APP_any = 'any'

and then add following line to vite.config.js :

export default defineConfig(({ command, mode }) => {

  const env = loadEnv(mode, process.cwd(), ""); //this line

  return { 
.
.
.

For usage can use following line

import.meta.env.VITE_APP_any

Or

process.env.VITE_APP_any

1 Comment

Question is about changing variables at runtime, this answer is only for dev
-2

I found another way.

You can add your envs to secrets. In my case, using GitHub Actions.

On the pipeline, when building your image, add your secrets as arguments,

--build-arg VITE_API_URL=${{ secrets.VITE_API_URL }} \

then in your Dockerfile

ARG VITE_API_URL

ENV VITE_API_URL=$VITE_API_URL

So when you run npm run build , the envs are already there.

1 Comment

The question is about configuration after npm run build, so this unfortunately will not help here.
-3

You can set the variables in YAML format and update them accordingly as per your requirement.

Below is the sample YAML format which we use as a template:

#Set variables once
variables:
  configuration: debug
  platform: x64

steps:

#Use them once
- task: MSBuild@1
  inputs:
    solution: solution1.sln
    configuration: $(configuration) # Use the variable
    platform: $(platform)

#Use them again
- task: MSBuild@1
  inputs:
    solution: solution2.sln
    configuration: $(configuration) # Use the variable
    platform: $(platform)

Check this SO for more insights to understand environment variables hosted in azure web app

1 Comment

Thanks for your help but I need to be able to change the env-Variables inside of the Azure App service configuration. We use Gitlab and in there I currently have multiple env-Files for each server(dev, staging, prod), that works fine. But I need to do something like in the linked articles and I don't get that to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.