Shedrack Akintayo

Contents

Published on

Hands-On with Kubernetes v1.34 New EnvFiles Feature

Authors

The Kubernetes v1.34 release introduced an exciting new alpha feature called EnvFiles, which allows containers to load environment variables directly from files using the new fileKeyRef field. This feature eliminates the need for complex ConfigMap or Secret management in scenarios where you need to generate configuration dynamically at runtime.

In this hands-on guide, we'll explore the EnvFiles feature by setting up a real Kubernetes cluster and deploying a Node.js application that demonstrates this new capability.

What is the EnvFiles Feature?

As mentioned in a blog post by the official Kubernetes team, the EnvFiles feature enables a new way to inject environment variables into containers. Instead of pre-creating ConfigMaps or Secrets, you can use init containers to generate environment files on-the-fly, and have your main containers load these variables using the new fileKeyRef syntax.

Key Benefits:

  • Dynamic configuration generation at pod startup
  • Reduced API server load (no additional ConfigMap/Secret resources)
  • Simplified vendor integrations where configuration is generated programmatically
  • Temporary storage in emptyDir volumes (no persistent configuration data)

Prerequisites

Before we begin, ensure you have the following tools installed:

Setting Up the Cluster

The EnvFiles feature is an alpha feature in Kubernetes v1.34, so we need to explicitly enable it using feature gates.

Create K3s Cluster with EnvFiles Support

# Create a k3d cluster with K3s v1.34 and EnvFiles enabled
k3d cluster create envfiles-demo \
  --image rancher/k3s:v1.34.1-rc1-k3s1 \
  --k3s-arg "--kubelet-arg=feature-gates=EnvFiles=true@server:*" \
  --k3s-arg "--kubelet-arg=feature-gates=EnvFiles=true@agent:*" \
  --k3s-arg "--kube-apiserver-arg=feature-gates=EnvFiles=true@server:*"

Note: We're using K3s version v1.34.1-rc1-k3s1 which includes support for the EnvFiles feature gate.

Verify the Cluster

Check cluster version

kubectl version 

The output should be something like:

Client Version: v1.33.3
Kustomize Version: v5.6.0
Server Version: v1.34.1-rc1+k3s1

Verify the node is ready

kubectl get nodes

The output should be something like:

k3d-envfiles-demo-server-0   Ready    control-plane   2m30s   v1.34.1-rc1+k3s1

Node.js Application with Dynamic Configuration

Now let's create a sample Node.js web application that loads multiple environment variables from a file generated by an init container.

Create the Node.js Application Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-envfiles-demo
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: node-envfiles-demo
  template:
    metadata:
      labels:
        app: node-envfiles-demo
    spec:
      initContainers:
      - name: create-config
        image: busybox
        command:
        - sh
        - -c
        - |
          echo "Creating application configuration..."
          echo "PORT=3000" > /config/app.env
          echo "NODE_ENV=production" >> /config/app.env
          echo "DATABASE_URL=postgresql://user:pass@db:5432/myapp" >> /config/app.env
          echo "API_TIMEOUT=30000" >> /config/app.env
          echo "FEATURE_FLAGS=dark_mode,new_ui,beta_features" >> /config/app.env
          echo ""
          echo "Generated configuration file:"
          cat /config/app.env
        volumeMounts:
        - name: config-volume
          mountPath: /config
      containers:
      - name: node-app
        image: node:18-alpine
        env:
        - name: PORT
          valueFrom:
            fileKeyRef:
              path: app.env
              volumeName: config-volume
              key: PORT
        - name: NODE_ENV
          valueFrom:
            fileKeyRef:
              path: app.env
              volumeName: config-volume
              key: NODE_ENV
        - name: DATABASE_URL
          valueFrom:
            fileKeyRef:
              path: app.env
              volumeName: config-volume
              key: DATABASE_URL
        - name: API_TIMEOUT
          valueFrom:
            fileKeyRef:
              path: app.env
              volumeName: config-volume
              key: API_TIMEOUT
        - name: FEATURE_FLAGS
          valueFrom:
            fileKeyRef:
              path: app.env
              volumeName: config-volume
              key: FEATURE_FLAGS
        command:
        - sh
        - -c
        - |
          echo "=== Node.js Application Starting ==="
          echo "Environment Variables loaded via EnvFiles:"
          echo "PORT: $PORT"
          echo "NODE_ENV: $NODE_ENV"
          echo "DATABASE_URL: $DATABASE_URL"
          echo "API_TIMEOUT: $API_TIMEOUT"
          echo "FEATURE_FLAGS: $FEATURE_FLAGS"
          echo ""
          echo "Creating web server..."
          node -e "
          const http = require('http');
          const server = http.createServer((req, res) => {
            const config = {
              port: process.env.PORT,
              nodeEnv: process.env.NODE_ENV,
              databaseUrl: process.env.DATABASE_URL,
              apiTimeout: process.env.API_TIMEOUT,
              featureFlags: process.env.FEATURE_FLAGS?.split(',') || []
            };
            
            res.writeHead(200, {'Content-Type': 'application/json'});
            res.end(JSON.stringify({
              message: 'EnvFiles Demo Application',
              timestamp: new Date().toISOString(),
              configuration: config
            }, null, 2));
          });
          
          server.listen(process.env.PORT || 3000, () => {
            console.log('Server running on port ' + (process.env.PORT || 3000));
          });
          "
        volumeMounts:
        - name: config-volume
          mountPath: /config
        ports:
        - containerPort: 3000
      volumes:
      - name: config-volume
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: node-envfiles-demo
  namespace: default
spec:
  selector:
    app: node-envfiles-demo
  ports:
  - port: 80
    targetPort: 3000
  type: LoadBalancer

Save the above YAML to deployment.yaml and deploy it:

kubectl apply -f node-envfiles-demo.yaml

# Watch the deployment
kubectl get pods -l app=node-envfiles-demo -w

Verify the EnvFiles Feature in Action

Check Init Container Logs

The init container creates our configuration file:

kubectl logs deployment/node-envfiles-demo -c create-config
Init Container Logs

Check Main Container Logs

The Node.js application loads the environment variables:

kubectl logs deployment/node-envfiles-demo -c node-app

You should see output similar to:

Application Logs

Inspect the Pod Configuration

You can also examine how Kubernetes configured the pod:

kubectl get pod -l app=node-envfiles-demo -o yaml | grep -A 20 fileKeyRef
Pod Configuration

Test the Application

# Get the application URL (if using k3d with LoadBalancer)
curl http://localhost/

# Or port-forward to test locally
kubectl port-forward service/node-envfiles-demo 8080:80
curl http://localhost:8080

You should receive a JSON response showing all the configuration loaded via EnvFiles:

Application Response

Understanding the EnvFiles Architecture

The EnvFiles feature works through several key components:

1. Init Container

  • Runs before the main application container
  • Generates configuration files in an emptyDir volume
  • Can fetch data from external services, generate secrets, or create dynamic configuration

2. EmptyDir Volume

  • Shared storage between init and main containers
  • Lives only for the duration of the pod
  • Currently the only supported volume type for EnvFiles (alpha limitation)

3. FileKeyRef Configuration

  • New field in container environment variable specification
  • Points to a specific file and key within that file
  • Loads the value at container startup time

Example fileKeyRef Syntax:

env:
- name: MY_VARIABLE
  valueFrom:
    fileKeyRef:
      path: config.env          # File path within the volume
      volumeName: config-volume # Name of the volume
      key: MY_VARIABLE         # Key to extract from the file

Use Cases and Benefits

The EnvFiles feature opens up several powerful use cases:

1. Dynamic License Management

Generate license tokens or fetch them from external services without storing them as persistent secrets.

2. Service Discovery

Query service registries or DNS to build connection strings dynamically.

3. Vendor Tool Integration

Many vendor tools expect environment variables but provide complex configuration logic that's easier to handle in init containers.

4. Temporary Secrets

Generate short-lived credentials or tokens that don't need to persist beyond the pod lifecycle.

5. Complex Configuration Assembly

Combine multiple configuration sources, apply templates, or perform preprocessing before the main application starts.

Current Limitations (Alpha Stage)

As an alpha feature, EnvFiles has some important limitations:

  • Volume Support: Only emptyDir volumes are supported
  • File Format: Uses standard key=value format (like .env files)
  • Feature Gate: Must be explicitly enabled via feature gates
  • Error Handling: Limited error reporting for file parsing issues

Production Considerations

While EnvFiles is exciting, consider these factors for production use:

  1. Alpha Feature: API may change in future Kubernetes versions
  2. Error Handling: Implement proper validation in init containers
  3. Security: Remember that emptyDir contents may be accessible via node filesystem access
  4. Monitoring: Add appropriate logging and health checks for configuration generation
  5. Backup Strategy: Since configuration is ephemeral, ensure your init containers can reliably regenerate it

Cleanup

When you're done experimenting, clean up the resources:

# Delete the demo application
kubectl delete -f node-envfiles-demo.yaml

# Delete the test pod
kubectl delete pod test-envfiles-basic

# Delete the k3d cluster
k3d cluster delete envfiles-demo

Conclusion

The EnvFiles feature in Kubernetes v1.34 represents a significant step forward in dynamic configuration management. By allowing containers to load environment variables from files generated at runtime, it opens up new possibilities for complex application deployment scenarios while maintaining the security and isolation principles of Kubernetes.

While still in alpha, this feature shows great promise for simplifying vendor tool integrations, dynamic secret management, and complex configuration scenarios. As it matures through beta and stable releases, we can expect to see broader adoption and additional volume type support.

The combination of init containers and fileKeyRef provides a clean, Kubernetes-native way to handle dynamic configuration without the overhead of additional API resources or complex external systems.


References