diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 95aa237..cc42da7 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -45,7 +45,7 @@ jobs:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
         go: ["1.21", "1.22"]
-        tags: ["", "finder", "viper_bind_struct"]
+        tags: ["", "viper_finder", "viper_bind_struct"]
 
     steps:
       - name: Checkout repository
diff --git a/file.go b/file.go
index a54fe5a..101624d 100644
--- a/file.go
+++ b/file.go
@@ -1,4 +1,4 @@
-//go:build !finder
+//go:build !viper_finder
 
 package viper
 
diff --git a/file_finder.go b/file_finder.go
index d96a1bd..c873621 100644
--- a/file_finder.go
+++ b/file_finder.go
@@ -1,4 +1,4 @@
-//go:build finder
+//go:build viper_finder
 
 package viper
 
@@ -11,18 +11,22 @@ import (
 // Search all configPaths for any config file.
 // Returns the first path that exists (and is a config file).
 func (v *Viper) findConfigFile() (string, error) {
-	var names []string
+	finder := v.finder
 
-	if v.configType != "" {
-		names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)
-	} else {
-		names = locafero.NameWithExtensions(v.configName, SupportedExts...)
-	}
+	if finder == nil {
+		var names []string
 
-	finder := locafero.Finder{
-		Paths: v.configPaths,
-		Names: names,
-		Type:  locafero.FileTypeFile,
+		if v.configType != "" {
+			names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)
+		} else {
+			names = locafero.NameWithExtensions(v.configName, SupportedExts...)
+		}
+
+		finder = locafero.Finder{
+			Paths: v.configPaths,
+			Names: names,
+			Type:  locafero.FileTypeFile,
+		}
 	}
 
 	results, err := finder.Find(v.fs)
diff --git a/finder.go b/finder.go
new file mode 100644
index 0000000..58a321b
--- /dev/null
+++ b/finder.go
@@ -0,0 +1,47 @@
+package viper
+
+import (
+	"errors"
+
+	"github.com/spf13/afero"
+)
+
+// WithFinder sets a custom [Finder].
+func WithFinder(f Finder) Option {
+	return optionFunc(func(v *Viper) {
+		v.finder = f
+	})
+}
+
+// Finder looks for files and directories in an [afero.Fs] filesystem.
+type Finder interface {
+	Find(fsys afero.Fs) ([]string, error)
+}
+
+// Finders combines multiple finders into one.
+func Finders(finders ...Finder) Finder {
+	return &CombinedFinder{finders: finders}
+}
+
+// CombinedFinder is a Finder that combines multiple finders.
+type CombinedFinder struct {
+	finders []Finder
+}
+
+// Find implements the [Finder] interface.
+func (c *CombinedFinder) Find(fsys afero.Fs) ([]string, error) {
+	var results []string
+	var errs []error
+
+	for _, finder := range c.finders {
+		r, err := finder.Find(fsys)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		results = append(results, r...)
+	}
+
+	return results, errors.Join(errs...)
+}
diff --git a/finder_test.go b/finder_test.go
new file mode 100644
index 0000000..d57a413
--- /dev/null
+++ b/finder_test.go
@@ -0,0 +1,42 @@
+package viper
+
+import (
+	"testing"
+
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+type finderStub struct {
+	results []string
+}
+
+func (f finderStub) Find(_ afero.Fs) ([]string, error) {
+	return f.results, nil
+}
+
+func TestFinders(t *testing.T) {
+	finder := Finders(
+		finderStub{
+			results: []string{
+				"/home/user/.viper.yaml",
+			},
+		},
+		finderStub{
+			results: []string{
+				"/etc/viper/config.yaml",
+			},
+		},
+	)
+
+	results, err := finder.Find(afero.NewMemMapFs())
+	require.NoError(t, err)
+
+	expected := []string{
+		"/home/user/.viper.yaml",
+		"/etc/viper/config.yaml",
+	}
+
+	assert.Equal(t, expected, results)
+}
diff --git a/viper.go b/viper.go
index cebdde6..1ddeb2c 100644
--- a/viper.go
+++ b/viper.go
@@ -158,6 +158,8 @@ type Viper struct {
 	// The filesystem to read config from.
 	fs afero.Fs
 
+	finder Finder
+
 	// A set of remote providers to search for the configuration
 	remoteProviders []*defaultRemoteProvider
 
@@ -506,6 +508,12 @@ func (v *Viper) ConfigFileUsed() string { return v.configFile }
 func AddConfigPath(in string) { v.AddConfigPath(in) }
 
 func (v *Viper) AddConfigPath(in string) {
+	if v.finder != nil {
+		v.logger.Warn("call to AddConfigPath is ineffective when a custom finder is configured")
+
+		return
+	}
+
 	if in != "" {
 		absin := absPathify(v.logger, in)
 
@@ -1955,6 +1963,12 @@ func (v *Viper) SetFs(fs afero.Fs) {
 func SetConfigName(in string) { v.SetConfigName(in) }
 
 func (v *Viper) SetConfigName(in string) {
+	if v.finder != nil {
+		v.logger.Warn("call to SetConfigName is ineffective when a custom finder is configured")
+
+		return
+	}
+
 	if in != "" {
 		v.configName = in
 		v.configFile = ""