skip to Main Content

I’ve got a set up using this tutorial: https://codinglatte.com/posts/angular/using-os-environment-variables-in-angular-with-docker/ to pass docker environment variables into a docker container.

I am using an angular custom webpack for this.

However, when I open my docker container and open the app, it seems my docker variables are not passed into the angular container.

How can I correctly pass docker env variables into my Angular App?

Here is my Dockerfile for the angular application:

FROM node:12 AS compile-image

WORKDIR /opt/ng

COPY package.json ./

RUN npm install

COPY . ./

RUN node_modules/.bin/ng build --prod

FROM nginx

VOLUME [ "/usr/share/nginx/html/data" ]

WORKDIR /

COPY --from=compile-image /opt/ng/dist/YomiApp /usr/share/nginx/html

and my docker-compose.yml that I use to open up the Angular container and the backend server for the app:

version: "3"

services:
  webapp:
    image: nlanson/myAngularImage:latest
    environment:
      TZ: Australia/Sydney
      DATABASE_URI: "http://localhost:6969"
    volumes:
      - "./data:/usr/share/nginx/html/data" #Data is mounted to the directory where the Angular App is running.
    ports:
      - "8080:80"
    container_name: webapp
  server:
    image: nlanson/backendImage:latest
    environment:
      TZ: Australia/Sydney
    volumes:
      - "./data:/data"
    ports:
      - "6969:6969"
    container_name: server
    

Here is my custom-webpack.config.js file where my environment variables are listed:

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      $ENV: {
        DATABASE_URI: JSON.stringify(process.env.DATABASE_URI),
      }
    })
  ]
};

and the service that uses the environment variable:

import { environment } from '../../environments/environment'

@Injectable({
    providedIn:root
})
export class DatabaseService {
    URI = environment.DATABASE_URI
}

Maybe it is because the build for my Angular Image is multi stage??

Thanks

2

Answers


  1. The environment variables that are used by your webapp container need to be passed to the container during the build process, not initialization.
    These environment variables are used by RUN node_modules/.bin/ng build --prod. But these environment variables are not set for that moment, they will be set by docker-compose after the image is been built. You need to set those variables in Dockerfile.

    Login or Signup to reply.
  2. Your approach will always fail as the webpack script is run once during building of the container. So you need some code that is run at every container start which modifies som code.

    My current approach is:

    1. On container startup: In Dockerfile – Run bash script as entrypoint:
    # Docker code for building the image
    
    # Transfer env variables to be able to read them in the javascript code
    COPY docker-env-js-to-script.sh /
    RUN ["chmod", "+x", "/docker-env-js-to-script.sh"]
    ENTRYPOINT ["/docker-env-js-to-script.sh"]
    CMD ["node", "server.js"]
    
    1. reading all env variables starting with APP_:
    #!/bin/sh
    set -euo pipefail
    
    # Capture all environment variables starting with APP_ and make JSON string from them
    ENV_JSON="$(jq --compact-output --null-input 'env | with_entries(select(.key | startswith("APP_")))')"
    
    # Escape sed replacement's special characters: , &, /.
    # No need to escape newlines, because --compact-output already removed them.
    # Inside of JSON strings newlines are already escaped.
    ENV_JSON_ESCAPED="$(printf "%s" "${ENV_JSON}" | sed -e 's/[&/]/\&/g')"
    
    sed -i "s/<noscript id="env-insertion-point"></noscript>/<script>var env=${ENV_JSON_ESCAPED}</script>/g" /app/index.html
    
    exec "$@"
    
    1. Add these to the index.html file by replacing <noscript id="env-insertion-point"></noscript> (added in the body of index.html) with the env variables as a json.
    2. In javascript, in the environment file will then use following code to get the variable from default or from environment:
    export const environment = {
      get apiAddress() {
        if (window.env && window.env.APP_API_BASE_URL) {
          return window.env.APP_API_BASE_URL;
        } else {
          return defaultEnvironment.apiBaseUrl;
        }
      }
    };
    

    By doing this we can define --env APP_API_BASE_URL=api.company.com when starting the container the first time.

    The main downside of this approach is that the browser will probably cache index.html file. This has not been a problem for us. But it might be a problem for you so keep that in mind.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search