Managing environment variables in Ionic 2 / 3

Mobile App Development Ionic2 ionic3 dotenv

The Problem

When developing Ionic 2 apps, very often developers want to store different application configuration values in different environments. For example, the backend API URLs in development and production are different. Or, two different Google Map Api keys are used in QA and production environments.

Our Approach at F5 Works

The Recipe

Step 1: Create .env at project root (the KEY=VALUE store)


NOTE: you should git-ignore the .env file

Step 2: Inject .env content into ENV global variable

var dotenvConfig = require('dotenv').config();  
var _ = require('lodash');  
var path = require('path');  
var webpack = require('webpack');  
var ionicWebpackFactory = require(process.env.IONIC_WEBPACK_FACTORY);

function getPlugins() {  
  var plugins = [
    new webpack.DefinePlugin({
      'process.env': _(process.env)
                      .mapValues((v) => (JSON.stringify(v)))
  // for dev builds, use our custom environment
  return [

module.exports = {  
  entry: process.env.IONIC_APP_ENTRY_POINT,
  output: {
    path: '{{BUILD}}',
    publicPath: 'build/',
    filename: process.env.IONIC_OUTPUT_JS_FILE_NAME,
    devtoolModuleFilenameTemplate: ionicWebpackFactory.getSourceMapperFunction(),
  devtool: process.env.IONIC_SOURCE_MAP_TYPE,

  resolve: {
    extensions: ['.ts', '.js', '.json'],
    modules: [path.resolve('node_modules')]

  module: {
    loaders: [
        test: /\.json$/,
        loader: 'json-loader'
        test: /\.ts$/,
        loader: process.env.IONIC_WEBPACK_LOADER

  plugins: getPlugins(),

  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'

NOTE: you can find original webpack config from node_modules/@ionic/app-scripts

  "config": {
    "ionic_webpack": "./config/webpack.config.js"
npm install dotenv --save-dev  
npm install lodash --save-dev  

Step 3: Create and inject AppConfig class

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

declare var process: any;

export class AppConfig {  
  public apiBaseUrl: string;
  public googleMapApiKey: string;

  constructor() {
    this.apiBaseUrl = this._readString('API_URL', 'http://localhost:3000/api/v1');
    this.googleMapApiKey = this._readString('GOOGLE_MAP_API_KEY', 'xxxyyy111');

    console.debug('AppConfig', this);

  private _readString(key: string, defaultValue?: string): string {
    const v = process.env[key];
    return v === undefined ? defaultValue : String(v);
import { NgModule }     from '@angular/core';  
import { AppConfig }    from '../config/app.config';

  providers: [

    // other injectables
export class AppModule {}  

Step 4: Reading values in AppConfig from other components

import { Component, ViewChild } from '@angular/core';  
import { AppConfig } from '../config/app.config';

  templateUrl: 'app.html'
export class MyApp {  
  constructor(public appConfig: AppConfig) {
    console.debug('AppConfig', this.appConfig);

.env for different environment

Now, you can populate different set values of into .env from the machine that build or run Ionic app. For instance, our CI agent in Jenkins has different set of .env values for building apps of different environments.


Instead of having single .env, alternatively we could have json files for each environment.


We used this approach in our Ionic1 projects. However when project grows, it becomes a bit messy as more personal config json files tend to be created. So we choose to experiment the single env approach which Twelve-Factor App suggests.