Repository: kubernetes/kompose

Issue:
[BUG] Environment variables referencing .env values are resolved as blank in generated manifests
### Expected Behavior

Variables like ${MINIO_USER}, ${MINIO_PASSWORD}, etc., should be resolved to their values defined in the .env file.

Generated manifests (e.g., minio-pod.yaml) should contain environment variables with non-empty values.

### Actual Behavior

Kompose outputs warnings indicating variables are unset (e.g., WARN The "MINIO_PORT" variable is not set. Defaulting to a blank string).
terminal output:
```
WARN The "MINIO_USER" variable is not set. Defaulting to a blank string. 
WARN The "MINIO_PASSWORD" variable is not set. Defaulting to a blank string. 
WARN The "TIMEZONE" variable is not set. Defaulting to a blank string. 
WARN The "MINIO_PORT" variable is not set. Defaulting to a blank string. 
WARN The "MINIO_CONSOLE_PORT" variable is not set. Defaulting to a blank string. 
WARN /Users/xiaoyuefan/workenv-new/kompose/pkg/loader/compose/docker-compose-simple.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion 
WARN File don't exist or failed to check if the directory is empty: stat :/data: no such file or directory 
INFO Kubernetes file "pkg/loader/compose/docker-compose-simple/templates/minio-service.yaml" created 
INFO Kubernetes file "pkg/loader/compose/docker-compose-simple/templates/minio-pod.yaml" created 
INFO Kubernetes file "pkg/loader/compose/docker-compose-simple/templates/env-configmap.yaml" created 
INFO Kubernetes file "pkg/loader/compose/docker-compose-simple/templates/minio-data-persistentvolumeclaim.yaml" created 
INFO chart created in "./pkg/loader/compose/docker-compose-simple/"
```

The generated minio-pod.yaml includes environment variables with empty values:

```
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kompose.cmd: kompose -f ./pkg/loader/compose/docker-compose-simple.yml convert -c
    kompose.version: 1.35.0 (HEAD)
  labels:
    io.kompose.service: minio
  name: minio
spec:
  containers:
    - args:
        - server
        - --console-address
        - :9001
        - /data
      env:
        - name: MINIO_CONSOLE_PORT
          valueFrom:
            configMapKeyRef:
              key: MINIO_CONSOLE_PORT
              name: env
        - name: MINIO_HOST
          valueFrom:
            configMapKeyRef:
              key: MINIO_HOST
              name: env
        - name: MINIO_PASSWORD
          valueFrom:
            configMapKeyRef:
              key: MINIO_PASSWORD
              name: env
        - name: MINIO_PORT
          valueFrom:
            configMapKeyRef:
              key: MINIO_PORT
              name: env
        - name: MINIO_ROOT_PASSWORD
        - name: MINIO_ROOT_USER
        - name: MINIO_USER
          valueFrom:
            configMapKeyRef:
              key: MINIO_USER
              name: env
        - name: TIMEZONE
          valueFrom:
            configMapKeyRef:
              key: TIMEZONE
              name: env
        - name: TZ
      image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
      name: ragflow-minio
      ports:
        - containerPort: 9000
          protocol: TCP
        - containerPort: 9001
          protocol: TCP
      volumeMounts:
        - mountPath: /data
          name: minio-data
  restartPolicy: OnFailure
  volumes:
    - name: minio-data
      persistentVolumeClaim:
        claimName: minio-data
```


### Steps To Reproduce

1. Create a `docker-compose-simple.yml` with:

```
version: '3'
services:
  minio:
    image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
    env_file: .env
    environment:
      - MINIO_ROOT_USER=${MINIO_USER}
      - MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}
      - TZ=${TIMEZONE}
    ports:
      - ${MINIO_PORT}:9000
      - ${MINIO_CONSOLE_PORT}:9001
```
2. Create a `.env` file in the same directory with:
```
MINIO_USER=rag_flow
MINIO_PASSWORD=infini_rag_flow
TIMEZONE=Asia/Shanghai
MINIO_PORT=9000
MINIO_CONSOLE_PORT=9001
```
3. Run `kompose -f docker-compose-simple.yml convert -c`.



### Kompose Version

```Text
1.35.0
```

### Docker-Compose file

```YAML
version: '3'
services:
  minio:
    image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
    env_file: .env
    environment:
      - MINIO_ROOT_USER=${MINIO_USER}
      - MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}
      - TZ=${TIMEZONE}
    ports:
      - ${MINIO_PORT}:9000
      - ${MINIO_CONSOLE_PORT}:9001
```

### Anything else?

If this is confirmed as a bug, I would be glad to submit a fix.

Discussion:
@yuefanxiao: I will try to fix it, please assign me

Solution patch:
diff --git a/pkg/loader/compose/compose.go b/pkg/loader/compose/compose.go
index 796999c6..ca1f77ab 100644
--- a/pkg/loader/compose/compose.go
+++ b/pkg/loader/compose/compose.go
@@ -163,6 +163,8 @@ func (c *Compose) LoadFile(files []string, profiles []string, noInterpolate bool
 		cli.WithWorkingDirectory(workingDir),
 		cli.WithInterpolation(!noInterpolate),
 		cli.WithProfiles(profiles),
+		cli.WithEnvFiles([]string{}...),
+		cli.WithDotEnv,
 	)
 	if err != nil {
 		return kobject.KomposeObject{}, errors.Wrap(err, "Unable to create compose options")
diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go
index 5e7da57e..82421630 100644
--- a/pkg/transformer/kubernetes/k8sutils.go
+++ b/pkg/transformer/kubernetes/k8sutils.go
@@ -31,6 +31,7 @@ import (
 	"text/template"
 	"time"
 
+	"github.com/compose-spec/compose-go/v2/dotenv"
 	"github.com/compose-spec/compose-go/v2/types"
 	"github.com/joho/godotenv"
 	"github.com/kubernetes/kompose/pkg/kobject"
@@ -962,6 +963,10 @@ func GetEnvsFromFile(file string) (map[string]string, error) {
 	return envLoad, nil
 }
 
+func LoadEnvFiles(file string, lookup func(key string) (string, bool)) (map[string]string, error) {
+	return dotenv.ReadWithLookup(lookup, file)
+}
+
 // GetContentFromFile gets the content from the file..
 func GetContentFromFile(file string) (string, error) {
 	fileBytes, err := os.ReadFile(file)
diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go
index 360cfcc7..66b44b4b 100644
--- a/pkg/transformer/kubernetes/kubernetes.go
+++ b/pkg/transformer/kubernetes/kubernetes.go
@@ -219,6 +219,38 @@ func (k *Kubernetes) InitSvc(name string, service kobject.ServiceConfig) *api.Se
 	return svc
 }
 
+// InitConfigMapForEnvWithLookup initializes a ConfigMap object from an env_file with variable interpolation support
+// using the provided lookup function to resolve variable references like ${VAR} or ${VAR:-default}
+func (k *Kubernetes) InitConfigMapForEnvWithLookup(name string, opt kobject.ConvertOptions, envFile string, lookup func(key string) (string, bool)) *api.ConfigMap {
+	workDir, err := transformer.GetComposeFileDir(opt.InputFiles)
+	if err != nil {
+		log.Fatalf("Unable to get compose file directory: %s", err)
+	}
+	envs, err := LoadEnvFiles(filepath.Join(workDir, envFile), lookup)
+	if err != nil {
+		log.Fatalf("Unable to retrieve env file: %s", err)
+	}
+
+	// Remove root pathing
+	// replace all other slashes / periods
+	envName := FormatEnvName(envFile, name)
+
+	// In order to differentiate files, we append to the name and remove '.env' if applicable from the file name
+	configMap := &api.ConfigMap{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "ConfigMap",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:   envName,
+			Labels: transformer.ConfigLabels(name + "-" + envName),
+		},
+		Data: envs,
+	}
+
+	return configMap
+}
+
 // InitConfigMapForEnv initializes a ConfigMap object
 func (k *Kubernetes) InitConfigMapForEnv(name string, opt kobject.ConvertOptions, envFile string) *api.ConfigMap {
 	workDir, err := transformer.GetComposeFileDir(opt.InputFiles)
@@ -1326,13 +1358,8 @@ func (k *Kubernetes) CreateWorkloadAndConfigMapObjects(name string, service kobj
 		objects = append(objects, k.InitSS(name, service, replica))
 	}
 
-	if len(service.EnvFile) > 0 {
-		for _, envFile := range service.EnvFile {
-			configMap := k.InitConfigMapForEnv(name, opt, envFile)
-			objects = append(objects, configMap)
-		}
-	}
-
+	envConfigMaps := k.PargeEnvFiletoConfigMaps(name, service, opt)
+	objects = append(objects, envConfigMaps...)
 	return objects
 }
 
@@ -1671,13 +1698,8 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
 				pod := k.InitPod(name, service)
 				objects = append(objects, pod)
 			}
-
-			if len(service.EnvFile) > 0 {
-				for _, envFile := range service.EnvFile {
-					configMap := k.InitConfigMapForEnv(name, opt, envFile)
-					objects = append(objects, configMap)
-				}
-			}
+			envConfigMaps := k.PargeEnvFiletoConfigMaps(name, service, opt)
+			objects = append(objects, envConfigMaps...)
 		} else {
 			objects = k.CreateWorkloadAndConfigMapObjects(name, service, opt)
 		}
@@ -1776,3 +1798,19 @@ func (k *Kubernetes) configHorizontalPodScaler(name string, service kobject.Serv
 	*objects = append(*objects, &hpa)
 	return nil
 }
+
+func (k *Kubernetes) PargeEnvFiletoConfigMaps(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) []runtime.Object {
+	envs := make(map[string]string)
+	for _, env := range service.Environment {
+		envs[env.Name] = env.Value
+	}
+	configMaps := make([]runtime.Object, 0)
+	for _, envFile := range service.EnvFile {
+		configMap := k.InitConfigMapForEnvWithLookup(name, opt, envFile, func(key string) (string, bool) {
+			v, ok := envs[key]
+			return v, ok
+		})
+		configMaps = append(configMaps, configMap)
+	}
+	return configMaps
+}
diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go
index ae382b7d..2947de4e 100644
--- a/pkg/transformer/openshift/openshift.go
+++ b/pkg/transformer/openshift/openshift.go
@@ -340,12 +340,8 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
 				objects = append(objects, pod)
 			}
 
-			if len(service.EnvFile) > 0 {
-				for _, envFile := range service.EnvFile {
-					configMap := o.InitConfigMapForEnv(name, opt, envFile)
-					objects = append(objects, configMap)
-				}
-			}
+			envConfigMaps := o.PargeEnvFiletoConfigMaps(name, service, opt)
+			objects = append(objects, envConfigMaps...)
 		} else {
 			objects = o.CreateWorkloadAndConfigMapObjects(name, service, opt)
 



Test patch:
diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go
index e252073c..f4a6422e 100644
--- a/pkg/transformer/kubernetes/kubernetes_test.go
+++ b/pkg/transformer/kubernetes/kubernetes_test.go
@@ -19,6 +19,8 @@ package kubernetes
 import (
 	"encoding/json"
 	"fmt"
+	"os"
+	"path/filepath"
 	"reflect"
 	"strings"
 	"testing"
@@ -36,6 +38,7 @@ import (
 	networkingv1beta1 "k8s.io/api/networking/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 )
 
@@ -1254,3 +1257,79 @@ func newSecrets(stringsSecretConfig SecretsConfig) types.Secrets {
 		},
 	}
 }
+
+// TestPargeEnvFiletoConfigMaps tests the conversion of environment variable files to ConfigMap objects
+func TestPargeEnvFiletoConfigMaps(t *testing.T) {
+	// Prepare a temp .env file for the expression test
+	tempFile, err := os.CreateTemp("", ".env")
+	if err != nil {
+		t.Fatalf("Failed to create temp env file: %v", err)
+	}
+	defer os.Remove(tempFile.Name())
+	content := []byte(`FOO=bar
+BAR=${FOO}_baz
+DOC_ENGINE=${DOC_ENGINE:-elasticsearch}
+COMPOSE_PROFILES=${DOC_ENGINE}
+UNDEFINED_VAR=${MISSING_VAR:-default_value}
+`)
+	if _, err := tempFile.Write(content); err != nil {
+		t.Fatalf("Failed to write to temp env file: %v", err)
+	}
+	tempFile.Close()
+
+	tempFileName := filepath.Base(tempFile.Name())
+	testCases := map[string]struct {
+		service kobject.ServiceConfig
+		opt     kobject.ConvertOptions
+		want    int
+		check   func(t *testing.T, cms []runtime.Object)
+	}{
+		"Env file with variable expressions": {
+			service: kobject.ServiceConfig{
+				Name: "test-app",
+				Environment: []kobject.EnvVar{
+					{
+						Name:  "DOC_ENGINE",
+						Value: "test-env",
+					},
+				},
+				EnvFile: []string{tempFileName},
+			},
+			opt:  kobject.ConvertOptions{InputFiles: []string{tempFile.Name()}},
+			want: 1,
+			check: func(t *testing.T, cms []runtime.Object) {
+				cm, ok := cms[0].(*api.ConfigMap)
+				if !ok {
+					t.Errorf("Returned object is not a ConfigMap")
+					return
+				}
+				if cm.Data["FOO"] != "bar" {
+					t.Errorf("Expected FOO=bar, got %s", cm.Data["FOO"])
+				}
+				if cm.Data["BAR"] != "bar_baz" {
+					t.Errorf("Expected BAR=bar_baz, got %s", cm.Data["BAR"])
+				}
+				if cm.Data["DOC_ENGINE"] != "test-env" {
+					t.Errorf("Expected DOC_ENGINE=test-env, got %s", cm.Data["DOC_ENGINE"])
+				}
+				if cm.Data["COMPOSE_PROFILES"] != "test-env" {
+					t.Errorf("Expected COMPOSE_PROFILES=test-env, got %s", cm.Data["COMPOSE_PROFILES"])
+				}
+				if cm.Data["UNDEFINED_VAR"] != "default_value" {
+					t.Errorf("Expected UNDEFINED_VAR=default_value, got %s", cm.Data["UNDEFINED_VAR"])
+				}
+			},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			k := Kubernetes{}
+			cms := k.PargeEnvFiletoConfigMaps(tc.service.Name, tc.service, tc.opt)
+			if len(cms) != tc.want {
+				t.Errorf("Expected %d ConfigMaps, got %d", tc.want, len(cms))
+			}
+			tc.check(t, cms)
+		})
+	}
+}
diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh
index ccb185ff..f982a410 100755
--- a/script/test/cmd/tests.sh
+++ b/script/test/cmd/tests.sh
@@ -228,6 +228,14 @@ os_output="$KOMPOSE_ROOT/script/test/fixtures/envvars-interpolation/output-os.ya
 convert::expect_success_and_warning "$k8s_cmd" "$k8s_output" || exit 1
 convert::expect_success_and_warning "$os_cmd" "$os_output" || exit 1
 
+# Test load .env file by default
+k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/env-dotenv/compose.yaml convert --stdout --with-kompose-annotation=false"
+os_cmd="kompose  --provider=openshift -f  $KOMPOSE_ROOT/script/test/fixtures/env-dotenv/compose.yaml  convert --stdout --with-kompose-annotation=false"
+k8s_output="$KOMPOSE_ROOT/script/test/fixtures/env-dotenv/output-k8s.yaml"
+os_output="$KOMPOSE_ROOT/script/test/fixtures/env-dotenv/output-os.yaml"
+convert::expect_success_and_warning "$k8s_cmd" "$k8s_output" || exit 1
+convert::expect_success_and_warning "$os_cmd" "$os_output" || exit 1
+
 # Test single file output feature
 k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/single-file-output/compose.yaml convert --stdout --with-kompose-annotation=false"
 k8s_output="$KOMPOSE_ROOT/script/test/fixtures/single-file-output/output-k8s.yaml"
@@ -282,6 +290,14 @@ k8s_output="$KOMPOSE_ROOT/script/test/fixtures/compose-env-no-interpolation/outp
 convert::expect_success "$k8s_cmd" "$k8s_output" || exit 1
 convert::expect_success "$os_cmd" "$os_output" || exit 1
 
+# Test configmap generated by env_file with variable interpolation 
+k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/envfile-interpolation/compose.yaml convert --stdout --with-kompose-annotation=false"
+os_cmd="kompose  --provider=openshift -f  $KOMPOSE_ROOT/script/test/fixtures/envfile-interpolation/compose.yaml  convert --stdout --with-kompose-annotation=false"
+k8s_output="$KOMPOSE_ROOT/script/test/fixtures/envfile-interpolation/output-k8s.yaml"
+os_output="$KOMPOSE_ROOT/script/test/fixtures/envfile-interpolation/output-os.yaml"
+convert::expect_success_and_warning "$k8s_cmd" "$k8s_output" || exit 1
+convert::expect_success_and_warning "$os_cmd" "$os_output" || exit 1
+
 # Test support for subpath volume
 k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/vols-subpath/compose.yaml convert --stdout --with-kompose-annotation=false"
 k8s_output="$KOMPOSE_ROOT/script/test/fixtures/vols-subpath/output-k8s.yaml"
diff --git a/script/test/fixtures/env-dotenv/.env b/script/test/fixtures/env-dotenv/.env
new file mode 100644
index 00000000..b5e0badb
--- /dev/null
+++ b/script/test/fixtures/env-dotenv/.env
@@ -0,0 +1,5 @@
+MINIO_USER=rag_flow
+MINIO_PASSWORD=infini_rag_flow
+TIMEZONE=Asia/Shanghai
+MINIO_PORT=9000
+MINIO_CONSOLE_PORT=9001
\ No newline at end of file
diff --git a/script/test/fixtures/env-dotenv/compose.yaml b/script/test/fixtures/env-dotenv/compose.yaml
new file mode 100644
index 00000000..665044de
--- /dev/null
+++ b/script/test/fixtures/env-dotenv/compose.yaml
@@ -0,0 +1,12 @@
+version: '3'
+services:
+  minio:
+    image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
+    env_file: .env
+    environment:
+      - MINIO_ROOT_USER=${MINIO_USER}
+      - MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}
+      - TZ=${TIMEZONE}
+    ports:
+      - ${MINIO_PORT}:9000
+      - ${MINIO_CONSOLE_PORT}:9001
\ No newline at end of file
diff --git a/script/test/fixtures/env-dotenv/output-k8s.yaml b/script/test/fixtures/env-dotenv/output-k8s.yaml
new file mode 100644
index 00000000..9dcb24eb
--- /dev/null
+++ b/script/test/fixtures/env-dotenv/output-k8s.yaml
@@ -0,0 +1,68 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  ports:
+    - name: "9000"
+      port: 9000
+      targetPort: 9000
+    - name: "9001"
+      port: 9001
+      targetPort: 9001
+  selector:
+    io.kompose.service: minio
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      io.kompose.service: minio
+  template:
+    metadata:
+      labels:
+        io.kompose.service: minio
+    spec:
+      containers:
+        - env:
+            - name: MINIO_ROOT_PASSWORD
+              value: infini_rag_flow
+            - name: MINIO_ROOT_USER
+              value: rag_flow
+            - name: TZ
+              value: Asia/Shanghai
+          envFrom:
+            - configMapRef:
+                name: env
+          image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
+          name: minio
+          ports:
+            - containerPort: 9000
+              protocol: TCP
+            - containerPort: 9001
+              protocol: TCP
+      restartPolicy: Always
+
+---
+apiVersion: v1
+data:
+  MINIO_CONSOLE_PORT: "9001"
+  MINIO_PASSWORD: infini_rag_flow
+  MINIO_PORT: "9000"
+  MINIO_USER: rag_flow
+  TIMEZONE: Asia/Shanghai
+kind: ConfigMap
+metadata:
+  labels:
+    io.kompose.service: minio-env
+  name: env
\ No newline at end of file
diff --git a/script/test/fixtures/env-dotenv/output-os.yaml b/script/test/fixtures/env-dotenv/output-os.yaml
new file mode 100644
index 00000000..89ad3ca6
--- /dev/null
+++ b/script/test/fixtures/env-dotenv/output-os.yaml
@@ -0,0 +1,96 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  ports:
+    - name: "9000"
+      port: 9000
+      targetPort: 9000
+    - name: "9001"
+      port: 9001
+      targetPort: 9001
+  selector:
+    io.kompose.service: minio
+
+---
+apiVersion: v1
+data:
+  MINIO_CONSOLE_PORT: "9001"
+  MINIO_PASSWORD: infini_rag_flow
+  MINIO_PORT: "9000"
+  MINIO_USER: rag_flow
+  TIMEZONE: Asia/Shanghai
+kind: ConfigMap
+metadata:
+  labels:
+    io.kompose.service: minio-env
+  name: env
+
+---
+apiVersion: apps.openshift.io/v1
+kind: DeploymentConfig
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  replicas: 1
+  selector:
+    io.kompose.service: minio
+  template:
+    metadata:
+      labels:
+        io.kompose.service: minio
+    spec:
+      containers:
+        - env:
+            - name: MINIO_ROOT_PASSWORD
+              value: infini_rag_flow
+            - name: MINIO_ROOT_USER
+              value: rag_flow
+            - name: TZ
+              value: Asia/Shanghai
+          envFrom:
+            - configMapRef:
+                name: env
+          image: ' '
+          name: minio
+          ports:
+            - containerPort: 9000
+              protocol: TCP
+            - containerPort: 9001
+              protocol: TCP
+      restartPolicy: Always
+  test: false
+  triggers:
+    - type: ConfigChange
+    - imageChangeParams:
+        automatic: true
+        containerNames:
+          - minio
+        from:
+          kind: ImageStreamTag
+          name: minio:RELEASE.2023-12-20T01-00-02Z
+      type: ImageChange
+
+---
+apiVersion: image.openshift.io/v1
+kind: ImageStream
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  lookupPolicy:
+    local: false
+  tags:
+    - from:
+        kind: DockerImage
+        name: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
+      name: RELEASE.2023-12-20T01-00-02Z
+      referencePolicy:
+        type: ""
\ No newline at end of file
diff --git a/script/test/fixtures/envfile-interpolation/.env b/script/test/fixtures/envfile-interpolation/.env
new file mode 100644
index 00000000..6e6df478
--- /dev/null
+++ b/script/test/fixtures/envfile-interpolation/.env
@@ -0,0 +1,4 @@
+DOC_ENGINE=${DOC_ENGINE:-elasticsearch}
+COMPOSE_PROFILES=${DOC_ENGINE}
+MINIO_CONSOLE_PORT=9001
+MINIO_PORT=9000
\ No newline at end of file
diff --git a/script/test/fixtures/envfile-interpolation/compose.yaml b/script/test/fixtures/envfile-interpolation/compose.yaml
new file mode 100644
index 00000000..28233ec1
--- /dev/null
+++ b/script/test/fixtures/envfile-interpolation/compose.yaml
@@ -0,0 +1,14 @@
+version: '3' 
+
+services:
+  minio:
+    image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
+    container_name: ragflow-minio
+    command: server --console-address ":9001" /data
+    ports:
+      - ${MINIO_PORT}:9000
+      - ${MINIO_CONSOLE_PORT}:9001
+    env_file: .env
+    environment:
+      - DOC_ENGINE=test-env
+    restart: on-failure
diff --git a/script/test/fixtures/envfile-interpolation/output-k8s.yaml b/script/test/fixtures/envfile-interpolation/output-k8s.yaml
new file mode 100644
index 00000000..36190dd5
--- /dev/null
+++ b/script/test/fixtures/envfile-interpolation/output-k8s.yaml
@@ -0,0 +1,56 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  ports:
+    - name: "9000"
+      port: 9000
+      targetPort: 9000
+    - name: "9001"
+      port: 9001
+      targetPort: 9001
+  selector:
+    io.kompose.service: minio
+
+---
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  containers:
+    - args:
+        - server
+        - --console-address
+        - :9001
+        - /data
+      envFrom:
+        - configMapRef:
+            name: env
+      image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
+      name: ragflow-minio
+      ports:
+        - containerPort: 9000
+          protocol: TCP
+        - containerPort: 9001
+          protocol: TCP
+  restartPolicy: OnFailure
+
+---
+apiVersion: v1
+data:
+  COMPOSE_PROFILES: test-env
+  DOC_ENGINE: test-env
+  MINIO_CONSOLE_PORT: "9001"
+  MINIO_PORT: "9000"
+kind: ConfigMap
+metadata:
+  labels:
+    io.kompose.service: minio-env
+  name: env
\ No newline at end of file
diff --git a/script/test/fixtures/envfile-interpolation/output-os.yaml b/script/test/fixtures/envfile-interpolation/output-os.yaml
new file mode 100644
index 00000000..36190dd5
--- /dev/null
+++ b/script/test/fixtures/envfile-interpolation/output-os.yaml
@@ -0,0 +1,56 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  ports:
+    - name: "9000"
+      port: 9000
+      targetPort: 9000
+    - name: "9001"
+      port: 9001
+      targetPort: 9001
+  selector:
+    io.kompose.service: minio
+
+---
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    io.kompose.service: minio
+  name: minio
+spec:
+  containers:
+    - args:
+        - server
+        - --console-address
+        - :9001
+        - /data
+      envFrom:
+        - configMapRef:
+            name: env
+      image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
+      name: ragflow-minio
+      ports:
+        - containerPort: 9000
+          protocol: TCP
+        - containerPort: 9001
+          protocol: TCP
+  restartPolicy: OnFailure
+
+---
+apiVersion: v1
+data:
+  COMPOSE_PROFILES: test-env
+  DOC_ENGINE: test-env
+  MINIO_CONSOLE_PORT: "9001"
+  MINIO_PORT: "9000"
+kind: ConfigMap
+metadata:
+  labels:
+    io.kompose.service: minio-env
+  name: env
\ No newline at end of file