diff --git a/go.mod b/go.mod
index cf1397c46..ca8c79c68 100644
--- a/go.mod
+++ b/go.mod
@@ -102,7 +102,6 @@ require (
 	golang.org/x/tools v0.1.12
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 	gopkg.in/ini.v1 v1.67.0
-	gopkg.in/yaml.v2 v2.4.0
 	gopkg.in/yaml.v3 v3.0.1
 	mvdan.cc/xurls/v2 v2.4.0
 	strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
@@ -293,6 +292,7 @@ require (
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 	sigs.k8s.io/yaml v1.2.0 // indirect
 )
 
diff --git a/modules/migration/file_format.go b/modules/migration/file_format.go
index 30e1d256c..92cf71407 100644
--- a/modules/migration/file_format.go
+++ b/modules/migration/file_format.go
@@ -8,12 +8,13 @@ import (
 	"fmt"
 	"os"
 	"strings"
+	"time"
 
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 
 	"github.com/santhosh-tekuri/jsonschema/v5"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 // Load project data from file, with optional validation
@@ -84,13 +85,9 @@ func validate(bs []byte, datatype interface{}, isJSON bool) error {
 func toStringKeys(val interface{}) (interface{}, error) {
 	var err error
 	switch val := val.(type) {
-	case map[interface{}]interface{}:
+	case map[string]interface{}:
 		m := make(map[string]interface{})
 		for k, v := range val {
-			k, ok := k.(string)
-			if !ok {
-				return nil, fmt.Errorf("found non-string key %T %s", k, k)
-			}
 			m[k], err = toStringKeys(v)
 			if err != nil {
 				return nil, err
@@ -106,6 +103,8 @@ func toStringKeys(val interface{}) (interface{}, error) {
 			}
 		}
 		return l, nil
+	case time.Time:
+		return val.Format(time.RFC3339), nil
 	default:
 		return val, nil
 	}
diff --git a/modules/packages/helm/metadata.go b/modules/packages/helm/metadata.go
index 9517448ca..fb5e51d0c 100644
--- a/modules/packages/helm/metadata.go
+++ b/modules/packages/helm/metadata.go
@@ -14,7 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/validation"
 
 	"github.com/hashicorp/go-version"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 var (
diff --git a/modules/packages/pub/metadata.go b/modules/packages/pub/metadata.go
index 1fc4908b9..f3e9bf20b 100644
--- a/modules/packages/pub/metadata.go
+++ b/modules/packages/pub/metadata.go
@@ -15,7 +15,7 @@ import (
 	"code.gitea.io/gitea/modules/validation"
 
 	"github.com/hashicorp/go-version"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 var (
diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go
index 05c1a8a71..f1fc39991 100644
--- a/modules/packages/rubygems/metadata.go
+++ b/modules/packages/rubygems/metadata.go
@@ -14,7 +14,7 @@ import (
 
 	"code.gitea.io/gitea/modules/validation"
 
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 var (
@@ -120,7 +120,7 @@ func (r requirement) AsVersionRequirement() []VersionRequirement {
 		if !ok {
 			continue
 		}
-		vm, ok := req[1].(map[interface{}]interface{})
+		vm, ok := req[1].(map[string]interface{})
 		if !ok {
 			continue
 		}
diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go
index 662d9a5dd..17f0a0d31 100644
--- a/routers/api/packages/helm/helm.go
+++ b/routers/api/packages/helm/helm.go
@@ -23,7 +23,7 @@ import (
 	"code.gitea.io/gitea/routers/api/packages/helper"
 	packages_service "code.gitea.io/gitea/services/packages"
 
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 func apiError(ctx *context.Context, status int, obj interface{}) {
diff --git a/services/migrations/dump.go b/services/migrations/dump.go
index 4ab4539c8..8103e2faa 100644
--- a/services/migrations/dump.go
+++ b/services/migrations/dump.go
@@ -26,7 +26,7 @@ import (
 	"code.gitea.io/gitea/modules/structs"
 
 	"github.com/google/uuid"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 var _ base.Uploader = &RepositoryDumper{}
diff --git a/services/migrations/restore.go b/services/migrations/restore.go
index c3fbcbb25..10fe8c4ee 100644
--- a/services/migrations/restore.go
+++ b/services/migrations/restore.go
@@ -13,7 +13,7 @@ import (
 
 	base "code.gitea.io/gitea/modules/migration"
 
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 // RepositoryRestorer implements an Downloader from the local directory
diff --git a/tests/integration/api_packages_helm_test.go b/tests/integration/api_packages_helm_test.go
index 393bf3cbe..3c30a6848 100644
--- a/tests/integration/api_packages_helm_test.go
+++ b/tests/integration/api_packages_helm_test.go
@@ -22,7 +22,7 @@ import (
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 func TestPackageHelm(t *testing.T) {
diff --git a/tests/integration/dump_restore_test.go b/tests/integration/dump_restore_test.go
index 19513d027..80c71810e 100644
--- a/tests/integration/dump_restore_test.go
+++ b/tests/integration/dump_restore_test.go
@@ -25,7 +25,7 @@ import (
 	"code.gitea.io/gitea/services/migrations"
 
 	"github.com/stretchr/testify/assert"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 )
 
 func TestDumpRestore(t *testing.T) {