diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh
index 8a27947e7023c712df87748c98c9fea91cf0d719..b552713ca1c12670fc5095061fe138533dd86eef 100644
--- a/src/gitlab-tofu.sh
+++ b/src/gitlab-tofu.sh
@@ -204,6 +204,11 @@ if [ $sourced -eq 0 ]; then
   # Authenticate to private registry
   terraform_authenticate_private_registry
 
+  var_file_args=""
+  if [ -n "${OPENTOFU_COMPONENT_VAR_FILE}" ]; then
+    var_file_args="--var-file=${OPENTOFU_COMPONENT_VAR_FILE}"
+  fi
+
   case "${1}" in
     "apply")
       auto_approve_args=""
@@ -215,7 +220,8 @@ if [ $sourced -eq 0 ]; then
       if [ "$TF_APPLY_NO_PLAN" = false ]; then
         tofu "${TF_CHDIR_OPT}" "${@}" -input=false "${auto_approve_args}" "${TF_PLAN_CACHE}"
       else
-        tofu "${TF_CHDIR_OPT}" "${@}" -input=false "${auto_approve_args}"
+        # shellcheck disable=SC2086
+        tofu "${TF_CHDIR_OPT}" "${@}" -input=false "${auto_approve_args}" ${var_file_args}
       fi
     ;;
     "destroy")
@@ -232,7 +238,8 @@ if [ $sourced -eq 0 ]; then
     ;;
     "plan")
       $TF_IMPLICIT_INIT && terraform_init
-      tofu "${TF_CHDIR_OPT}" "${@}" -input=false -out="${TF_PLAN_CACHE}"
+      # shellcheck disable=SC2086
+      tofu "${TF_CHDIR_OPT}" "${@}" -input=false -out="${TF_PLAN_CACHE}" ${var_file_args}
     ;;
     "plan-json")
       tofu "${TF_CHDIR_OPT}" show -json "${TF_PLAN_CACHE}" | \
@@ -241,15 +248,18 @@ if [ $sourced -eq 0 ]; then
     ;;
     "validate")
       $TF_IMPLICIT_INIT && terraform_init -backend=false
-      tofu "${TF_CHDIR_OPT}" "${@}"
+      # shellcheck disable=SC2086
+      tofu "${TF_CHDIR_OPT}" "${@}" ${var_file_args}
     ;;
     "test")
       $TF_IMPLICIT_INIT && terraform_init -backend=false
-      tofu "${TF_CHDIR_OPT}" "${@}"
+      # shellcheck disable=SC2086
+      tofu "${TF_CHDIR_OPT}" "${@}" ${var_file_args}
     ;;
     "graph")
       $TF_IMPLICIT_INIT && terraform_init
-      tofu "${TF_CHDIR_OPT}" "${@}"
+      # shellcheck disable=SC2086
+      tofu "${TF_CHDIR_OPT}" "${@}" ${var_file_args}
     ;;
     --)
       shift
diff --git a/templates/apply.yml b/templates/apply.yml
index 90adf3961d02cb5c52f4f129422dbe3d34455aeb..3836302c773311e28cf9696710a0c6cb2bdc2421 100644
--- a/templates/apply.yml
+++ b/templates/apply.yml
@@ -84,6 +84,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether the apply job is manual or automatically run.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir. Only used if no_plan is true otherwise the variables are coming from the plan.'
 
 ---
 
@@ -108,6 +112,7 @@ spec:
     TF_STATE_NAME: $[[ inputs.state_name ]]
     TF_APPLY_NO_PLAN: $[[ inputs.no_plan ]]
     TF_PLAN_NAME: $[[ inputs.plan_name ]]
+    OPENTOFU_COMPONENT_VAR_FILE: '$[[ inputs.var_file ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/templates/destroy.yml b/templates/destroy.yml
index 9975044db5f8bd2d18efc5561f86fa7b41948308..07914954e875154e21d20741bb051efae8d2d838 100644
--- a/templates/destroy.yml
+++ b/templates/destroy.yml
@@ -84,6 +84,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether the destroy job is manual or automatically run.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir. Only used if no_plan is true otherwise the variables are coming from the plan.'
 
 ---
 
@@ -107,6 +111,7 @@ spec:
     TF_STATE_NAME: $[[ inputs.state_name ]]
     TF_APPLY_NO_PLAN: $[[ inputs.no_plan ]]
     TF_PLAN_NAME: $[[ inputs.plan_name ]]
+    OPENTOFU_COMPONENT_VAR_FILE: '$[[ inputs.var_file ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml
index 12e01c46890460f9cea6de56f9641f33372e17ec..922debb56a28f7a7a49198578504225137ad4f64 100644
--- a/templates/full-pipeline.yml
+++ b/templates/full-pipeline.yml
@@ -96,6 +96,10 @@ spec:
     plan_artifacts_access:
       default: 'none'
       description: 'Access level for the plan artifact. See https://docs.gitlab.com/ee/ci/yaml/#artifactsaccess for possible values.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -123,6 +127,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/test.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]test'
@@ -135,6 +140,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
     rules:
       - exists:
           - $[[ inputs.root_dir ]]/**/*.tftest.hcl
@@ -151,6 +157,7 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       artifacts_access: $[[ inputs.plan_artifacts_access ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/apply.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]apply'
@@ -164,6 +171,7 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_apply: $[[ inputs.auto_apply ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/destroy.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]destroy'
@@ -177,6 +185,7 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_destroy: $[[ inputs.auto_destroy ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/delete-state.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]delete-state'
diff --git a/templates/graph.yml b/templates/graph.yml
index 7a0a0d0796b80469e3b693c9ab3edcfe08f2f8ec..18cc1bf450e18f22b89b9e4cc74fb231fb4d4294 100644
--- a/templates/graph.yml
+++ b/templates/graph.yml
@@ -78,6 +78,10 @@ spec:
       description: |
         Name of the graph file that should be generated.
         It will be uploaded as an artifact.
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -93,6 +97,7 @@ spec:
     __CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
     TF_ROOT: $[[ inputs.root_dir ]]
     TF_STATE_NAME: $[[ inputs.state_name ]]
+    OPENTOFU_COMPONENT_VAR_FILE: '$[[ inputs.var_file ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/templates/job-templates.yml b/templates/job-templates.yml
index 448d0ad68ac9f3fe699f2e2988892125f4dd4b2c..6fd140b7e4d94d46ede041c6c15c9d7bfa177e1a 100644
--- a/templates/job-templates.yml
+++ b/templates/job-templates.yml
@@ -93,6 +93,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether the destroy job is manual or automatically run.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -120,6 +124,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/graph.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]graph'
@@ -130,6 +135,7 @@ include:
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
       root_dir: $[[ inputs.root_dir ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/test.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]test'
@@ -142,6 +148,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/plan.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]plan'
@@ -154,6 +161,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/apply.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]apply'
@@ -167,6 +175,7 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_apply: $[[ inputs.auto_apply ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/destroy.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]destroy'
@@ -180,6 +189,7 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_destroy: $[[ inputs.auto_destroy ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/delete-state.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]delete-state'
diff --git a/templates/plan.yml b/templates/plan.yml
index cf6e7892bea2aaf054975582c78bfcae871c30aa..d6af1e8d863660e9c71a648e416572e7570b6f1e 100644
--- a/templates/plan.yml
+++ b/templates/plan.yml
@@ -83,6 +83,10 @@ spec:
     artifacts_access:
       default: 'none'
       description: 'Access level for the plan artifact. See https://docs.gitlab.com/ee/ci/yaml/#artifactsaccess for possible values.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -117,6 +121,7 @@ spec:
     TF_ROOT: $[[ inputs.root_dir ]]
     TF_STATE_NAME: $[[ inputs.state_name ]]
     TF_PLAN_NAME: $[[ inputs.plan_name ]]
+    OPENTOFU_COMPONENT_VAR_FILE: '$[[ inputs.var_file ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/templates/test.yml b/templates/test.yml
index 2f4894b54965c782149b60d32adacbcd22de5dc0..376f2c4e73c395ff2b3d6669fe6f0da40b12d07c 100644
--- a/templates/test.yml
+++ b/templates/test.yml
@@ -73,6 +73,10 @@ spec:
     state_name:
       default: default
       description: 'Remote OpenTofu state name.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -93,6 +97,7 @@ spec:
     __CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
     TF_ROOT: $[[ inputs.root_dir ]]
     TF_STATE_NAME: $[[ inputs.state_name ]]
+    OPENTOFU_COMPONENT_VAR_FILE: '$[[ inputs.var_file ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/templates/validate-plan-apply.yml b/templates/validate-plan-apply.yml
index abf6c4f7bebc99168814fbd0daf8d4785ab08489..9b3e65f4fa8658515ab6f9fca7561a2cf3e952c2 100644
--- a/templates/validate-plan-apply.yml
+++ b/templates/validate-plan-apply.yml
@@ -86,6 +86,10 @@ spec:
     plan_artifacts_access:
       default: 'none'
       description: 'Access level for the plan artifact. See https://docs.gitlab.com/ee/ci/yaml/#artifactsaccess for possible values.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -113,6 +117,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/plan.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]plan'
@@ -126,6 +131,7 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       artifacts_access: $[[ inputs.plan_artifacts_access ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/apply.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]apply'
@@ -139,3 +145,4 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_apply: $[[ inputs.auto_apply ]]
+      var_file: $[[ inputs.var_file ]]
diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml
index c68b05c29cc49a8e8eb2c57060a45662b6d71757..fe136c66ce7e4288c2c342dafc223d21998625d9 100644
--- a/templates/validate-plan-destroy.yml
+++ b/templates/validate-plan-destroy.yml
@@ -89,6 +89,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether the destroy job is manual or automatically run.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -116,6 +120,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/plan.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]plan'
@@ -131,6 +136,7 @@ include:
       plan_name: $[[ inputs.plan_name ]]
       artifacts_access: $[[ inputs.plan_artifacts_access ]]
       destroy: true
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/destroy.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]destroy'
@@ -146,6 +152,7 @@ include:
       no_plan: false
       plan_name: $[[ inputs.plan_name ]]
       auto_destroy: $[[ inputs.auto_destroy ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/delete-state.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]delete-state'
diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml
index e3c30856239a14822323e862b675bfe911b55893..39b61dd9bc63923ef6450c9da374277cd4f00b33 100644
--- a/templates/validate-plan.yml
+++ b/templates/validate-plan.yml
@@ -79,6 +79,10 @@ spec:
     artifacts_access:
       default: 'none'
       description: 'Access level for the plan artifact. See https://docs.gitlab.com/ee/ci/yaml/#artifactsaccess for possible values.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -106,6 +110,7 @@ include:
       image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
+      var_file: $[[ inputs.var_file ]]
   - local: '/templates/plan.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]plan'
@@ -119,3 +124,4 @@ include:
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       artifacts_access: $[[ inputs.artifacts_access ]]
+      var_file: $[[ inputs.var_file ]]
diff --git a/templates/validate.yml b/templates/validate.yml
index f88189c05e927392a067146736c4b128e1e9a055..9b474e24897ac175bfea616f05f2fb5e599a6ad5 100644
--- a/templates/validate.yml
+++ b/templates/validate.yml
@@ -73,6 +73,10 @@ spec:
     state_name:
       default: default
       description: 'Remote OpenTofu state name.'
+    var_file:
+      default: ''
+      type: string
+      description: 'Path to a variables files relative to root_dir.'
 
 ---
 
@@ -93,6 +97,7 @@ spec:
     TF_ROOT: $[[ inputs.root_dir ]]
     TF_STATE_NAME: $[[ inputs.state_name ]]
     TF_IGNORE_INIT_ERRORS: 'true' # Tofu can report errors which might be the reason init failed.
+    OPENTOFU_COMPONENT_VAR_FILE: '$[[ inputs.var_file ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/tests/iac/main.tf b/tests/iac/main.tf
index 9efc7bc0082e7e3ae1d28a61cc0018a793fa114d..f65a2b5a7a0681119c15026fd645f1ab8a7f8501 100644
--- a/tests/iac/main.tf
+++ b/tests/iac/main.tf
@@ -7,11 +7,20 @@ resource "local_file" "foo" {
   filename = "${path.module}/foo.bar"
 }
 
-variable "CI_PROJECT_NAME" {
+variable "ci_project_name" {
   type    = string
   default = "default"
 }
 
+variable "test_variable" {
+  type    = string
+  default = "default value"
+}
+
 output "project_name" {
-  value = var.CI_PROJECT_NAME
+  value = var.ci_project_name
+}
+
+output "test_variable" {
+  value = var.test_variable
 }
diff --git a/tests/iac/varfile.integration-test.tfvars b/tests/iac/varfile.integration-test.tfvars
new file mode 100644
index 0000000000000000000000000000000000000000..a9e6ed522698bc6187641fb135c39346cd4223e2
--- /dev/null
+++ b/tests/iac/varfile.integration-test.tfvars
@@ -0,0 +1 @@
+test_variable = "varfile integration test"
diff --git a/tests/integration-tests/VarFile.gitlab-ci.yml b/tests/integration-tests/VarFile.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0c035dae6f4d7ab226aa7453dbba4a6340da3927
--- /dev/null
+++ b/tests/integration-tests/VarFile.gitlab-ci.yml
@@ -0,0 +1,36 @@
+include:
+  - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/full-pipeline@$CI_COMMIT_SHA
+    inputs:
+      image_registry_base: $GITLAB_OPENTOFU_IMAGE_BASE
+      version: $CI_COMMIT_SHA
+      base_os: $GITLAB_OPENTOFU_BASE_IMAGE_OS
+      opentofu_version: $OPENTOFU_VERSION
+      root_dir: $TEST_TF_ROOT
+      state_name: $TEST_TF_STATE_NAME
+      var_file: varfile.integration-test.tfvars
+
+stages: [validate, test, build, deploy, cleanup]
+
+# Required to run everything immediately, instead of manually.
+
+fmt:
+  rules: [{when: always}]
+
+validate:
+  rules: [{when: always}]
+
+test:
+  rules: [{when: always}]
+
+plan:
+  rules: [{when: always}]
+
+apply:
+  rules: [{when: always}]
+
+destroy:
+  rules: [{when: always}]
+
+delete-state:
+  rules: [{when: always}]
+
diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml
index 7b1f5fbae32a5eb2edaa3bb68a773a14884624d2..af76bcbdec8f316bea3f05fc6bac6a34f7fd49d7 100644
--- a/tests/integration.gitlab-ci.yml
+++ b/tests/integration.gitlab-ci.yml
@@ -15,6 +15,7 @@ component:
           - TestJob
           - ModuleRelease
           - Destroy
+          - VarFile
         GITLAB_OPENTOFU_BASE_IMAGE_OS:
           - alpine
           - debian