diff --git a/services/packages/arch/repository.go b/services/packages/arch/repository.go
index 7fb4222cf61..a12af82ba5f 100644
--- a/services/packages/arch/repository.go
+++ b/services/packages/arch/repository.go
@@ -235,6 +235,28 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
 		return packages_service.DeletePackageFile(ctx, pf)
 	}
 
+	vpfs := make(map[int64]*entryOptions)
+	for _, pf := range pfs {
+		current := &entryOptions{
+			File: pf,
+		}
+		current.Version, err = packages_model.GetVersionByID(ctx, pf.VersionID)
+		if err != nil {
+			return err
+		}
+
+		// here we compare the versions but not using SearchLatestVersions because we shouldn't allow "downgrading" to a older version by "latest" one.
+		// https://wiki.archlinux.org/title/Downgrading_packages : randomly downgrading can mess up dependencies:
+		// If a downgrade involves a soname change, all dependencies may need downgrading or rebuilding too.
+		if old, ok := vpfs[current.Version.PackageID]; ok {
+			if compareVersions(old.Version.Version, current.Version.Version) == -1 {
+				vpfs[current.Version.PackageID] = current
+			}
+		} else {
+			vpfs[current.Version.PackageID] = current
+		}
+	}
+
 	indexContent, _ := packages_module.NewHashedBuffer()
 	defer indexContent.Close()
 
@@ -243,15 +265,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
 
 	cache := make(map[int64]*packages_model.Package)
 
-	for _, pf := range pfs {
-		opts := &entryOptions{
-			File: pf,
-		}
-
-		opts.Version, err = packages_model.GetVersionByID(ctx, pf.VersionID)
-		if err != nil {
-			return err
-		}
+	for _, opts := range vpfs {
 		if err := json.Unmarshal([]byte(opts.Version.MetadataJSON), &opts.VersionMetadata); err != nil {
 			return err
 		}
@@ -263,12 +277,12 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
 			}
 			cache[opts.Package.ID] = opts.Package
 		}
-		opts.Blob, err = packages_model.GetBlobByID(ctx, pf.BlobID)
+		opts.Blob, err = packages_model.GetBlobByID(ctx, opts.File.BlobID)
 		if err != nil {
 			return err
 		}
 
-		sig, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, pf.ID, arch_module.PropertySignature)
+		sig, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, opts.File.ID, arch_module.PropertySignature)
 		if err != nil {
 			return err
 		}
@@ -277,7 +291,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
 		}
 		opts.Signature = sig[0].Value
 
-		meta, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, pf.ID, arch_module.PropertyMetadata)
+		meta, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, opts.File.ID, arch_module.PropertyMetadata)
 		if err != nil {
 			return err
 		}
diff --git a/services/packages/arch/vercmp.go b/services/packages/arch/vercmp.go
new file mode 100644
index 00000000000..0d33dda0f12
--- /dev/null
+++ b/services/packages/arch/vercmp.go
@@ -0,0 +1,113 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package arch
+
+import (
+	"strings"
+	"unicode"
+)
+
+// https://gitlab.archlinux.org/pacman/pacman/-/blob/d55b47e5512808b67bc944feb20c2bcc6c1a4c45/lib/libalpm/version.c
+
+import (
+	"strconv"
+)
+
+func parseEVR(evr string) (epoch, version, release string) {
+	if before, after, f := strings.Cut(evr, ":"); f {
+		epoch = before
+		evr = after
+	} else {
+		epoch = "0"
+	}
+
+	if before, after, f := strings.Cut(evr, "-"); f {
+		version = before
+		release = after
+	} else {
+		version = evr
+		release = "1"
+	}
+	return epoch, version, release
+}
+
+func compareSegments(a, b []string) int {
+	lenA, lenB := len(a), len(b)
+	var l int
+	if lenA > lenB {
+		l = lenB
+	} else {
+		l = lenA
+	}
+	for i := 0; i < l; i++ {
+		if r := compare(a[i], b[i]); r != 0 {
+			return r
+		}
+	}
+	if lenA == lenB {
+		return 0
+	} else if l == lenA {
+		return -1
+	}
+	return 1
+}
+
+func compare(a, b string) int {
+	if a == b {
+		return 0
+	}
+
+	aNumeric := isNumeric(a)
+	bNumeric := isNumeric(b)
+
+	if aNumeric && bNumeric {
+		aInt, _ := strconv.Atoi(a)
+		bInt, _ := strconv.Atoi(b)
+		switch {
+		case aInt < bInt:
+			return -1
+		case aInt > bInt:
+			return 1
+		default:
+			return 0
+		}
+	}
+
+	if aNumeric {
+		return 1
+	}
+	if bNumeric {
+		return -1
+	}
+
+	return strings.Compare(a, b)
+}
+
+func isNumeric(s string) bool {
+	for _, c := range s {
+		if !unicode.IsDigit(c) {
+			return false
+		}
+	}
+	return true
+}
+
+func compareVersions(a, b string) int {
+	if a == b {
+		return 0
+	}
+
+	epochA, versionA, releaseA := parseEVR(a)
+	epochB, versionB, releaseB := parseEVR(b)
+
+	if res := compareSegments([]string{epochA}, []string{epochB}); res != 0 {
+		return res
+	}
+
+	if res := compareSegments(strings.Split(versionA, "."), strings.Split(versionB, ".")); res != 0 {
+		return res
+	}
+
+	return compareSegments([]string{releaseA}, []string{releaseB})
+}
diff --git a/services/packages/arch/vercmp_test.go b/services/packages/arch/vercmp_test.go
new file mode 100644
index 00000000000..2014a6d429d
--- /dev/null
+++ b/services/packages/arch/vercmp_test.go
@@ -0,0 +1,27 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package arch
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestCompareVersions(t *testing.T) {
+	// https://man.archlinux.org/man/vercmp.8.en
+	checks := [][]string{
+		{"1.0a", "1.0b", "1.0beta", "1.0p", "1.0pre", "1.0rc", "1.0", "1.0.a", "1.0.1"},
+		{"1", "1.0", "1.1", "1.1.1", "1.2", "2.0", "3.0.0"},
+	}
+	for _, check := range checks {
+		for i := 0; i < len(check)-1; i++ {
+			require.Equal(t, -1, compareVersions(check[i], check[i+1]))
+			require.Equal(t, 1, compareVersions(check[i+1], check[i]))
+		}
+	}
+	require.Equal(t, 1, compareVersions("1.0-2", "1.0"))
+	require.Equal(t, 0, compareVersions("0:1.0-1", "1.0"))
+	require.Equal(t, 1, compareVersions("1:1.0-1", "2.0"))
+}
diff --git a/tests/integration/api_packages_arch_test.go b/tests/integration/api_packages_arch_test.go
index 9c7a9dd19de..b9064ca3d1c 100644
--- a/tests/integration/api_packages_arch_test.go
+++ b/tests/integration/api_packages_arch_test.go
@@ -79,6 +79,34 @@ license = MIT`)
 
 		return buf.Bytes()
 	}
+	readIndexContent := func(r io.Reader) (map[string]string, error) {
+		gzr, err := gzip.NewReader(r)
+		if err != nil {
+			return nil, err
+		}
+
+		content := make(map[string]string)
+
+		tr := tar.NewReader(gzr)
+		for {
+			hd, err := tr.Next()
+			if err == io.EOF {
+				break
+			}
+			if err != nil {
+				return nil, err
+			}
+
+			buf, err := io.ReadAll(tr)
+			if err != nil {
+				return nil, err
+			}
+
+			content[hd.Name] = string(buf)
+		}
+
+		return content, nil
+	}
 
 	compressions := []string{"gz", "xz", "zst"}
 	repositories := []string{"main", "testing", "with/slash", ""}
@@ -171,35 +199,6 @@ license = MIT`)
 					MakeRequest(t, req, http.StatusConflict)
 				})
 
-				readIndexContent := func(r io.Reader) (map[string]string, error) {
-					gzr, err := gzip.NewReader(r)
-					if err != nil {
-						return nil, err
-					}
-
-					content := make(map[string]string)
-
-					tr := tar.NewReader(gzr)
-					for {
-						hd, err := tr.Next()
-						if err == io.EOF {
-							break
-						}
-						if err != nil {
-							return nil, err
-						}
-
-						buf, err := io.ReadAll(tr)
-						if err != nil {
-							return nil, err
-						}
-
-						content[hd.Name] = string(buf)
-					}
-
-					return content, nil
-				}
-
 				t.Run("Index", func(t *testing.T) {
 					defer tests.PrintCurrentTest(t)()
 
@@ -299,4 +298,39 @@ license = MIT`)
 			})
 		}
 	}
+	t.Run("KeepLastVersion", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+		pkgVer1 := createPackage("gz", "gitea-test", "1.0.0", "aarch64")
+		pkgVer2 := createPackage("gz", "gitea-test", "1.0.1", "aarch64")
+		req := NewRequestWithBody(t, "PUT", rootURL, bytes.NewReader(pkgVer1)).
+			AddBasicAuth(user.Name)
+		MakeRequest(t, req, http.StatusCreated)
+		req = NewRequestWithBody(t, "PUT", rootURL, bytes.NewReader(pkgVer2)).
+			AddBasicAuth(user.Name)
+		MakeRequest(t, req, http.StatusCreated)
+
+		req = NewRequest(t, "GET", fmt.Sprintf("%s/aarch64/%s", rootURL, arch_service.IndexArchiveFilename))
+		resp := MakeRequest(t, req, http.StatusOK)
+
+		content, err := readIndexContent(resp.Body)
+		assert.NoError(t, err)
+		assert.Len(t, content, 2)
+
+		_, has := content["gitea-test-1.0.0/desc"]
+		assert.False(t, has)
+		_, has = content["gitea-test-1.0.1/desc"]
+		assert.True(t, has)
+
+		req = NewRequest(t, "DELETE", fmt.Sprintf("%s/gitea-test/1.0.1/aarch64", rootURL)).
+			AddBasicAuth(user.Name)
+		MakeRequest(t, req, http.StatusNoContent)
+
+		req = NewRequest(t, "GET", fmt.Sprintf("%s/aarch64/%s", rootURL, arch_service.IndexArchiveFilename))
+		resp = MakeRequest(t, req, http.StatusOK)
+		content, err = readIndexContent(resp.Body)
+		assert.NoError(t, err)
+		assert.Len(t, content, 2)
+		_, has = content["gitea-test-1.0.0/desc"]
+		assert.True(t, has)
+	})
 }