Standard Angular builds bake environment variables into the code at build time. This means if you have Dev, Staging, and Prod environments, you have to build the app three times.
That's a waste of time. Instead, we should build a single Docker image and swap configurations at runtime. Here is how to do it.
1. The Strategy
We will store environment-specific configurations in JSON files and use an Entrypoint script in Docker to swap them based on an environment variable.
Step 1: Create Config Files
In your src/config folder, create:
app-config.dev.jsonapp-config.prod.json
In your src/ folder, create a default app-config.json for local development.
Step 2: Update angular.json
Ensure these files are included in your build assets:
"assets": [
"src/favicon.ico",
"src/assets",
"src/app-config.json",
"src/config"
]
2. The Angular Logic
We need to load the JSON file before the app starts.
The Config Service
Create a service to fetch the JSON file using HttpClient.
@Injectable({ providedIn: 'root' })
export class ConfigService {
private config: any;
constructor(private http: HttpClient) {}
loadConfig() {
return firstValueFrom(this.http.get('./app-config.json'))
.then(data => this.config = data);
}
get settings() {
return this.config;
}
}
The App Initializer
Use APP_INITIALIZER in your app.config.ts (Standalone) or AppModule to block the bootstrap process until the config is loaded.
export function initApp(configService: ConfigService) {
return () => configService.loadConfig();
}
// In providers:
{
provide: APP_INITIALIZER,
useFactory: initApp,
deps: [ConfigService],
multi: true
}
3. The Docker Magic
Now, we create a Docker image that can adapt to its environment.
entrypoint.sh
This script runs when the container starts. It checks the $ENVIRONMENT variable and overwrites the main config file.
#!/bin/bash
if [[ $ENVIRONMENT == "Prod" ]]; then
cp /usr/share/nginx/html/config/app-config.prod.json /usr/share/nginx/html/app-config.json
else
cp /usr/share/nginx/html/config/app-config.dev.json /usr/share/nginx/html/app-config.json
fi
nginx -g 'daemon off;'
Dockerfile
Use a multi-stage build to compile the app and serve it with Nginx.
# Build Stage
FROM node:lts AS build
WORKDIR /app
COPY . .
RUN npm install && npm run build
# Run Stage
FROM nginx:latest
COPY --from=build /app/dist/your-app-name/browser /usr/share/nginx/html
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
4. Running the Container
Now you can build your image once:
docker build -t my-angular-app .
And run it anywhere by just changing the environment variable:
# Run for Production
docker run -e ENVIRONMENT=Prod -p 8080:80 my-angular-app
# Run for Dev
docker run -e ENVIRONMENT=Dev -p 8080:80 my-angular-app
Summary
- One Build: Saves CI/CD time.
- Nginx: High-performance serving.
- Environment Agnostic: Move the same artifact from Dev to Prod with zero code changes.
Happy coding! π
Top comments (0)