diff --git a/Dockerfile.alpine b/Dockerfile.alpine
index 4cc96326857f59914b8ed4b566a63eacda72638d..cf9a8965716370a92b08994beea5c73ce954034c 100644
--- a/Dockerfile.alpine
+++ b/Dockerfile.alpine
@@ -1,4 +1,12 @@
-ARG BASE_IMAGE=alpine:3.21.3
+ARG BASE_IMAGE=docker.io/library/alpine:3.21.3
+
+FROM docker.io/library/golang:1.24-alpine AS builder
+
+WORKDIR /app
+# Copy the Go source code
+COPY src/auto-define-backend /app/
+# Build the Go binary with static linking
+RUN go build -o gitlab-tofu-auto-define-backend .
 
 FROM $BASE_IMAGE
 
@@ -27,6 +35,7 @@ RUN curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opent
 WORKDIR /
 
 COPY --chmod=755 src/gitlab-tofu.sh /usr/bin/gitlab-tofu
+COPY --from=builder --chmod=755 /app/gitlab-tofu-auto-define-backend /usr/bin/
 
 # Override ENTRYPOINT
 ENTRYPOINT []
diff --git a/Dockerfile.debian b/Dockerfile.debian
index 3b6949de623e45c1f4a9b8b35db8eccf8fe4e48a..7f13246391ec9369451ec68552f35e37aa779d78 100644
--- a/Dockerfile.debian
+++ b/Dockerfile.debian
@@ -1,4 +1,12 @@
-ARG BASE_IMAGE=debian:12.9-slim
+ARG BASE_IMAGE=docker.io/library/debian:12.9-slim
+
+FROM docker.io/library/golang:1.24-bookworm AS builder
+
+WORKDIR /app
+# Copy the Go source code
+COPY src/auto-define-backend /app/
+# Build the Go binary with static linking
+RUN go build -o gitlab-tofu-auto-define-backend .
 
 FROM $BASE_IMAGE
 
@@ -42,6 +50,7 @@ RUN curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opent
 WORKDIR /
 
 COPY --chmod=755 src/gitlab-tofu.sh /usr/bin/gitlab-tofu
+COPY --from=builder --chmod=755 /app/gitlab-tofu-auto-define-backend /usr/bin/
 
 # Override ENTRYPOINT
 ENTRYPOINT []
diff --git a/src/auto-define-backend/go.mod b/src/auto-define-backend/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..4b42fce8dd80d1f1919cfbacdbca8aab2be2ebf0
--- /dev/null
+++ b/src/auto-define-backend/go.mod
@@ -0,0 +1,17 @@
+module gitlab.com/components/opentofu/src/discover-http-backend
+
+go 1.24
+
+require github.com/hashicorp/hcl/v2 v2.23.0
+
+require (
+	github.com/agext/levenshtein v1.2.1 // indirect
+	github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
+	github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
+	github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
+	github.com/zclconf/go-cty v1.13.0 // indirect
+	golang.org/x/mod v0.8.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
+	golang.org/x/text v0.11.0 // indirect
+	golang.org/x/tools v0.6.0 // indirect
+)
diff --git a/src/auto-define-backend/go.sum b/src/auto-define-backend/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..0f49a8988a9b6f8c002159897c652004385587a7
--- /dev/null
+++ b/src/auto-define-backend/go.sum
@@ -0,0 +1,30 @@
+github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
+github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
+github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
+github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
+github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
+github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
+github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
+github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
+github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
+golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
+golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
diff --git a/src/auto-define-backend/main.go b/src/auto-define-backend/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..29722106e814306f20b24afb6d4c1e45439d2d50
--- /dev/null
+++ b/src/auto-define-backend/main.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
+)
+
+var ErrManuallyConfigured = errors.New("HTTP backend is manually configured")
+var ErrDifferentManuallyConfigured = errors.New("Backend of different type is manually configured")
+
+func isHTTPBackendConfigured(root *os.Root) (bool, error) {
+	err := fs.WalkDir(root.FS(), ".", func(path string, d fs.DirEntry, err error) error {
+		if path != "." && d.IsDir() {
+			return fs.SkipDir
+		}
+
+		name := filepath.Base(path)
+		ext := filepath.Ext(name)
+		if ext == "" || strings.HasPrefix(name, ".") || strings.HasSuffix(name, "~") || (strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) {
+			return nil
+		}
+
+		if ext == ".tftest.hcl" || ext == ".tftest.json" || ext == ".tofutest.hcl" || ext == ".tofutest.json" {
+			return nil
+		}
+
+		// FIXME: should actually prefer .tofu if .tf does exist, too.
+		if ext != ".tf" && ext != ".tofu" {
+			return nil
+		}
+
+		file, err := root.Open(path)
+		if err != nil {
+			return err
+		}
+
+		data, err := io.ReadAll(file)
+		if err != nil {
+			return err
+		}
+
+		hclFile, diags := hclsyntax.ParseConfig(data, path, hcl.InitialPos)
+		if diags.HasErrors() {
+			return diags
+		}
+
+		root, _, diags := hclFile.Body.PartialContent(&hcl.BodySchema{Blocks: []hcl.BlockHeaderSchema{{Type: "terraform"}}})
+		if diags.HasErrors() {
+			return diags
+		}
+
+		for _, block := range root.Blocks {
+			content, _, diags := block.Body.PartialContent(&hcl.BodySchema{Blocks: []hcl.BlockHeaderSchema{{Type: "backend", LabelNames: []string{"type"}}}})
+			if diags.HasErrors() {
+				return diags
+			}
+			for _, cb := range content.Blocks {
+				if cb.Type == "backend" && len(cb.Labels) == 1 {
+					if cb.Labels[0] == "http" {
+						return ErrManuallyConfigured
+					} else {
+						return ErrDifferentManuallyConfigured
+					}
+				}
+			}
+		}
+		return nil
+	})
+
+	if err != nil {
+		switch err {
+		case ErrManuallyConfigured:
+			return true, nil
+		case ErrDifferentManuallyConfigured:
+			return true, err
+		default:
+			return false, err
+		}
+	}
+
+	return false, nil
+}
+
+func main() {
+	if len(os.Args) < 2 {
+		fmt.Printf("gitlab-tofu: no root directory for backend discovery provided as first argument\n")
+		os.Exit(1)
+	}
+	root, err := os.OpenRoot(os.Args[1])
+	if err != nil {
+		fmt.Printf("gitlab-tofu: unable to open root directory for backend discovery: %s\n", err)
+		os.Exit(1)
+	}
+
+	configured, err := isHTTPBackendConfigured(root)
+	if err != nil {
+		fmt.Printf("gitlab-tofu: error during backend discovery: %s\n", err)
+		os.Exit(1)
+	}
+
+	if configured {
+		fmt.Println("gitlab-tofu: HTTP backend manually configured, doing nothing")
+		os.Exit(0)
+	}
+
+	file, err := root.Create("__gitlab-opentofu-backend.tf")
+	if err != nil {
+		fmt.Printf("gitlab-tofu: failed to create __gitlab-opentofu-backend.tf to automatically define HTTP backend: %s\n", err)
+		os.Exit(1)
+	}
+
+	_, err = file.WriteString(`terraform {
+  backend "http" {}
+}`)
+	if err != nil {
+		fmt.Printf("gitlab-tofu: failed to write __gitlab-opentofu-backend.tf to automatically define HTTP backend: %s\n", err)
+		os.Exit(1)
+	}
+	err = file.Close()
+	if err != nil {
+		fmt.Printf("gitlab-tofu: failed to close __gitlab-opentofu-backend.tf to automatically define HTTP backend: %s\n", err)
+		os.Exit(1)
+	}
+
+	fmt.Printf("gitlab-tofu: automatically defining the HTTP backend in __gitlab-opentofu-backend.tf")
+}
diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh
index 8a339e09122a14d3a0a37391ee1e6fb80b1ba855..3fb85a90c15cbc58fa9427df207f4b3b8a4c239f 100644
--- a/src/gitlab-tofu.sh
+++ b/src/gitlab-tofu.sh
@@ -238,17 +238,7 @@ define_http_backend() {
     return
   fi
 
-  if ! grep -q '^[[:space:]]*backend[[:space:]]\+"http"[[:space:]]\+{.*$' "${abs_tf_root}" -r 2>/dev/null; then
-    echo "gitlab-tofu: automatically defining the HTTP backend in __gitlab-opentofu-backend.tf. If that is a mistake, please disable it with the auto_define_backend: false input."
-
-    cat <<EOF > __gitlab-opentofu-backend.tf
-terraform {
-  backend "http" {}
-}
-EOF
-  else
-    echo "gitlab-tofu: auto_define_backend is enabled, but found manually configured HTTP backend, doing nothing."
-  fi
+  gitlab-tofu-auto-define-backend "${abs_tf_root}"
 }
 
 # configure_variables_for_tofu sets and exports all relevant variables for subsequent `tofu` command invocations.