0

I have angular applications

  • lib (this contains __environments, assets and common css, services etc)
  • app_1

both are run independently and lib is published into a npm package and increment the version with latest changes app_1 will consume the lib package in its package.json file and now i am Dockerizing both the apps

I need help to serve env file to each environments, how to archive this?

My requirement is to serve one docker image for app_1 that contains dist that is running on a pod, on run time i need to update the env file for different environments

/-app
   -assets (__environments, fonts, images)
   -lib (languages)
   -app_1 (dist)
5
  • It seems like you have one application and one library. You wouldn't create a separate container or image from the library, but when your application's Dockerfile RUN npm ci it will pick up the library. If there's only one deployable, does that simplify your issue? Commented Jun 11 at 18:24
  • Your app runs in a browser. Without a web server, there's no way to get it from the container to the browser. What do you use as your web server? Nginx? Node? Something else? Commented Jun 11 at 19:43
  • @HansKilian yes i use nginx but the main goal i need to achieve here is one pod that is finalised for release should take care of environment switching itself, i have multiple prod environments in different regions and staging servers. React js has this option to consume the process.env variables at runtime. I need a solution to the same in angular Commented Jun 12 at 3:36
  • @DavidMaze I have all the environment files in lib and my app_1 -> pod also has assets and all __environments in it on run time i need to switch it dynamically not at the build time. Commented Jun 12 at 3:41
  • I've never heard that React can use runtime environment variables when served using Nginx. Do you have a link? Commented Jun 12 at 7:11

1 Answer 1

0

I recently achieved this by reading my environment variables from a json file. In my case I wanted to run the same docker image in different environments (Dev, QA, Prod), I believe you want to achieve something similar.

Firstly you need a json file, /src/assets/config.json with the data that you want to change during runtime.

{
  "APP_API_URL": "http://localhost:3000/api"
}

Then you will have to create a service for reading your json config file, src/app/core/config/environment.service.ts.

The get function will be used to read a specific property from your config (eg: APP_API_URL).

The ENVIRONMENT injectionToken will be used on your services like ProductService, CategoryService, etc..



import { Injectable, InjectionToken  } from '@angular/core';

export interface Environment {
  APP_API_URL: string;
}

interface EnvironmentToken {
  load(): Promise<Environment>;
  get(key: keyof Environment): string | undefined;
}

@Injectable({
  providedIn: 'root',
})
export class EnvironmentService {
  private config?: Environment;

  load(): Promise<Environment> {
    return fetch('assets/config.json')
      .then(res => res.json())
      .then((data: Environment) => (this.config = data));
  }

  get(key: keyof Environment): string | undefined {
    return this.config?.[key];
  }
}

export const ENVIRONMENT = new InjectionToken<EnvironmentToken>(
  'App Environment Config'
);

Now update app.config.ts to load your config when the app is initialized, src/app/app.config.ts

import { inject, provideAppInitializer } from '@angular/core';
import { ENVIRONMENT, EnvironmentService } from './app/core/config/environment.service';

function initApp() {
  const environmentService = inject(ENVIRONMENT);

  return environmentService.load();
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideAppInitializer(initApp),
    { 
      provide: ENVIRONMENT, 
      useValue: new EnvironmentService()
    },
    ...
  ]
};

You can read the value from your service: src/app/features/product/services/product.service.ts

import { HttpClient, HttpParams } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';

import { ENVIRONMENT } from '../../../core/config/environment.token';

@Injectable({
  providedIn: 'root',
})
export class ProductService {
  private readonly http = inject(HttpClient);
  private readonly env = inject(ENVIRONMENT);
  private readonly url = this.env.get('APP_API_URL');

  getProducts() {
    return this.http.get<any[]>(this.url! + '/product');
  }
}

Now you need to create a docker-entrypoint.sh file on the same path as your Dockerffile with the following contents:

#!/bin/sh

ENV_FILE=/usr/share/nginx/html/assets/config.json

echo "{" > $ENV_FILE

# This will loop through all environment variables with a prefix of APP_
printenv | grep '^APP_' | while IFS='=' read -r key value; do
  echo "  \"${key}\": \"${value}\"," >> $ENV_FILE
done

# This will remove a trailing comma
sed -i '$ s/,$//' $ENV_FILE

echo "}" >> $ENV_FILE

exec "$@"

Lastly, in your Dockerfile you will have this under nginx stage:

FROM nginx:alpine

COPY ./nginx.conf /etc/nginx/conf.d/default.conf

COPY --from=build /usr/src/app/dist/my-app/browser /usr/share/nginx/html
COPY --from=build /usr/src/app/docker-entrypoint.sh /docker-entrypoint.sh

EXPOSE 80

ENTRYPOINT ["/docker-entrypoint.sh"]

# The Chainguard nginx image already runs nginx by default — no need to specify CMD.
CMD ["nginx", "-g", "daemon off;"]

That's all.

Now you can run your container with different variables. Make sure to prefix them with APP_ for this to work properly.

I hope this helps.

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

Comments

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.