How to Dynamically Inject Static IP Addresses in Kubernetes Deployments Using Environment Variables


2 views

Many Kubernetes engineers face this dilemma - we need static IPs for LoadBalancer services, but hardcoding them in deployment manifests creates security and maintainability issues. The original YAML example shows the problem clearly:

spec:
  type: LoadBalancer
  loadBalancerIP: 192.0.2.42  # Hardcoded value in Git

Kubernetes doesn't natively support shell-style environment variable expansion in YAML files. This syntax won't work:

spec:
  loadBalancerIP: ${SERVICE_ADDRESS}  # Won't expand

Option 1: Using kustomize for Templating

Kustomize provides a clean way to inject values without additional tools:

# kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: network-config
  literals:
  - SERVICE_ADDRESS=192.0.2.42

# Then reference in your deployment:
spec:
  loadBalancerIP: $(SERVICE_ADDRESS)

Option 2: Helm Values Injection

For Helm users, this becomes straightforward:

# values.yaml
service:
  address: ""

# templates/service.yaml
spec:
  loadBalancerIP: {{ .Values.service.address }}

# Deploy with:
helm install myapp --set service.address=192.0.2.42

Option 3: CI/CD Pipeline Processing

Many teams use sed or envsubst in their pipelines:

# In your deployment script
export SERVICE_ADDRESS=192.0.2.42
envsubst < deployment.yaml.template > deployment.yaml
kubectl apply -f deployment.yaml

For GCP users wanting direct integration:

# Reserve the IP first
gcloud compute addresses create my-service-ip --global

# Then reference by name
annotations:
  networking.gke.io/load-balancer-ip-address: my-service-ip

Remember that IP addresses in ConfigMaps are still visible in plaintext. For maximum security:

  • Use secret management systems like Vault
  • Restrict access to namespace-level secrets
  • Rotate IPs through automation when possible

Hardcoding sensitive information like static IP addresses in Kubernetes deployment manifests poses several issues:

  • Security risks when committing to version control
  • Environment-specific configurations that shouldn't be baked into the deployment spec
  • Reduced portability across different clusters/environments
# Problematic hardcoded example
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.100

While Kubernetes offers ConfigMaps and Secrets, they aren't directly usable for loadBalancerIP fields. Here are the limitations:

  • loadBalancerIP must be specified at spec level, not as environment variables
  • ConfigMap values can't be directly interpolated into the spec
  • Some cloud providers require pre-provisioned IPs

Here are practical approaches to solve this problem:

1. Templating with kustomize

# kustomization.yaml
configMapGenerator:
- name: service-config
  literals:
  - SERVICE_ADDRESS=192.168.1.100

# deployment.yaml
spec:
  type: LoadBalancer
  loadBalancerIP: # Will be replaced by kustomize

Apply with:

kustomize build . | kubectl apply -f -

2. Helm Templating

# values.yaml
service:
  address: "192.168.1.100"

# templates/deployment.yaml
spec:
  type: LoadBalancer
  loadBalancerIP: {{ .Values.service.address }}

3. CI/CD Pipeline Substitution

Many CI systems support environment variable substitution:

# deployment.yaml
spec:
  type: LoadBalancer
  loadBalancerIP: __SERVICE_ADDRESS__

# In your CI script
sed -i "s/__SERVICE_ADDRESS__/$SERVICE_ADDRESS/g" deployment.yaml
kubectl apply -f deployment.yaml

For Google Cloud, you can directly reference named addresses:

# First create the address
gcloud compute addresses create my-service-ip --global

# Then reference it in your YAML
spec:
  type: LoadBalancer
  loadBalancerIP: $(gcloud compute addresses describe my-service-ip --global --format='value(address)')
  • Never commit actual IPs to source control
  • Use infrastructure-as-code tools (Terraform, Pulumi) to manage IP allocation
  • Consider using DNS names instead of IPs when possible
  • Implement proper secret management for all sensitive configuration