diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 9e290fb6a51..1051b0f2a2e 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,6 +1,6 @@
 {
   "name": "Gitea DevContainer",
-  "image": "mcr.microsoft.com/devcontainers/go:1.21-bullseye",
+  "image": "mcr.microsoft.com/devcontainers/go:1.22-bullseye",
   "features": {
     // installs nodejs into container
     "ghcr.io/devcontainers/features/node:1": {
diff --git a/Dockerfile b/Dockerfile
index 325b0255dfc..b647c0cd590 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # Build stage
-FROM docker.io/library/golang:1.21-alpine3.19 AS build-env
+FROM docker.io/library/golang:1.22-alpine3.19 AS build-env
 
 ARG GOPROXY
 ENV GOPROXY ${GOPROXY:-direct}
diff --git a/Dockerfile.rootless b/Dockerfile.rootless
index 6f27c698ace..dd7da972783 100644
--- a/Dockerfile.rootless
+++ b/Dockerfile.rootless
@@ -1,5 +1,5 @@
 # Build stage
-FROM docker.io/library/golang:1.21-alpine3.19 AS build-env
+FROM docker.io/library/golang:1.22-alpine3.19 AS build-env
 
 ARG GOPROXY
 ENV GOPROXY ${GOPROXY:-direct}
diff --git a/Makefile b/Makefile
index 366ca6c6240..da4806d9c41 100644
--- a/Makefile
+++ b/Makefile
@@ -23,12 +23,12 @@ SHASUM ?= shasum -a 256
 HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
 COMMA := ,
 
-XGO_VERSION := go-1.21.x
+XGO_VERSION := go-1.22.x
 
 AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0
 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
-GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2
+GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1
 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1
 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5
diff --git a/models/issues/issue_xref.go b/models/issues/issue_xref.go
index 77ef53a0132..cfc3c1683c6 100644
--- a/models/issues/issue_xref.go
+++ b/models/issues/issue_xref.go
@@ -46,10 +46,10 @@ func neuterCrossReferences(ctx context.Context, issueID, commentID int64) error
 	for i, c := range active {
 		ids[i] = c.ID
 	}
-	return neuterCrossReferencesIds(ctx, ids)
+	return neuterCrossReferencesIDs(ctx, ids)
 }
 
-func neuterCrossReferencesIds(ctx context.Context, ids []int64) error {
+func neuterCrossReferencesIDs(ctx context.Context, ids []int64) error {
 	_, err := db.GetEngine(ctx).In("id", ids).Cols("`ref_action`").Update(&Comment{RefAction: references.XRefActionNeutered})
 	return err
 }
@@ -100,7 +100,7 @@ func (issue *Issue) createCrossReferences(stdCtx context.Context, ctx *crossRefe
 			}
 		}
 		if len(ids) > 0 {
-			if err = neuterCrossReferencesIds(stdCtx, ids); err != nil {
+			if err = neuterCrossReferencesIDs(stdCtx, ids); err != nil {
 				return err
 			}
 		}
diff --git a/models/issues/review_list.go b/models/issues/review_list.go
index ed3d0bd028a..282f18b4f7f 100644
--- a/models/issues/review_list.go
+++ b/models/issues/review_list.go
@@ -18,11 +18,11 @@ type ReviewList []*Review
 
 // LoadReviewers loads reviewers
 func (reviews ReviewList) LoadReviewers(ctx context.Context) error {
-	reviewerIds := make([]int64, len(reviews))
+	reviewerIDs := make([]int64, len(reviews))
 	for i := 0; i < len(reviews); i++ {
-		reviewerIds[i] = reviews[i].ReviewerID
+		reviewerIDs[i] = reviews[i].ReviewerID
 	}
-	reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIds)
+	reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIDs)
 	if err != nil {
 		return err
 	}
@@ -38,12 +38,12 @@ func (reviews ReviewList) LoadReviewers(ctx context.Context) error {
 }
 
 func (reviews ReviewList) LoadIssues(ctx context.Context) error {
-	issueIds := container.Set[int64]{}
+	issueIDs := container.Set[int64]{}
 	for i := 0; i < len(reviews); i++ {
-		issueIds.Add(reviews[i].IssueID)
+		issueIDs.Add(reviews[i].IssueID)
 	}
 
-	issues, err := GetIssuesByIDs(ctx, issueIds.Values())
+	issues, err := GetIssuesByIDs(ctx, issueIDs.Values())
 	if err != nil {
 		return err
 	}
diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go
index 26a0d28098a..9e345f3ee0e 100644
--- a/modules/git/log_name_status.go
+++ b/modules/git/log_name_status.go
@@ -143,19 +143,19 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int
 	}
 
 	// Our "line" must look like: <commitid> SP (<parent> SP) * NUL
-	commitIds := string(g.next)
+	commitIDs := string(g.next)
 	if g.buffull {
 		more, err := g.rd.ReadString('\x00')
 		if err != nil {
 			return nil, err
 		}
-		commitIds += more
+		commitIDs += more
 	}
-	commitIds = commitIds[:len(commitIds)-1]
-	splitIds := strings.Split(commitIds, " ")
-	ret.CommitID = splitIds[0]
-	if len(splitIds) > 1 {
-		ret.ParentIDs = splitIds[1:]
+	commitIDs = commitIDs[:len(commitIDs)-1]
+	splitIDs := strings.Split(commitIDs, " ")
+	ret.CommitID = splitIDs[0]
+	if len(splitIDs) > 1 {
+		ret.ParentIDs = splitIDs[1:]
 	}
 
 	// now read the next "line"
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index 013dd8f76f4..f622383bb52 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -94,7 +94,7 @@ type GiteaTemplate struct {
 }
 
 // Globs parses the .gitea/template globs or returns them if they were already parsed
-func (gt GiteaTemplate) Globs() []glob.Glob {
+func (gt *GiteaTemplate) Globs() []glob.Glob {
 	if gt.globs != nil {
 		return gt.globs
 	}
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 03798a712c2..f65cc6e6799 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -377,16 +377,16 @@ func ViewProject(ctx *context.Context) {
 	linkedPrsMap := make(map[int64][]*issues_model.Issue)
 	for _, issuesList := range issuesMap {
 		for _, issue := range issuesList {
-			var referencedIds []int64
+			var referencedIDs []int64
 			for _, comment := range issue.Comments {
 				if comment.RefIssueID != 0 && comment.RefIsPull {
-					referencedIds = append(referencedIds, comment.RefIssueID)
+					referencedIDs = append(referencedIDs, comment.RefIssueID)
 				}
 			}
 
-			if len(referencedIds) > 0 {
+			if len(referencedIDs) > 0 {
 				if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{
-					IssueIDs: referencedIds,
+					IssueIDs: referencedIDs,
 					IsPull:   util.OptionalBoolTrue,
 				}); err == nil {
 					linkedPrsMap[issue.ID] = linkedPrs
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 4908bb796d9..001f0752c36 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -339,16 +339,16 @@ func ViewProject(ctx *context.Context) {
 	linkedPrsMap := make(map[int64][]*issues_model.Issue)
 	for _, issuesList := range issuesMap {
 		for _, issue := range issuesList {
-			var referencedIds []int64
+			var referencedIDs []int64
 			for _, comment := range issue.Comments {
 				if comment.RefIssueID != 0 && comment.RefIsPull {
-					referencedIds = append(referencedIds, comment.RefIssueID)
+					referencedIDs = append(referencedIDs, comment.RefIssueID)
 				}
 			}
 
-			if len(referencedIds) > 0 {
+			if len(referencedIDs) > 0 {
 				if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{
-					IssueIDs: referencedIds,
+					IssueIDs: referencedIDs,
 					IsPull:   util.OptionalBoolTrue,
 				}); err == nil {
 					linkedPrsMap[issue.ID] = linkedPrs
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 44920817c98..83fc4d7162f 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -296,17 +296,17 @@ func Milestones(ctx *context.Context) {
 		}
 	}
 
-	showRepoIds := make(container.Set[int64], len(showRepos))
+	showRepoIDs := make(container.Set[int64], len(showRepos))
 	for _, repo := range showRepos {
 		if repo.ID > 0 {
-			showRepoIds.Add(repo.ID)
+			showRepoIDs.Add(repo.ID)
 		}
 	}
 	if len(repoIDs) == 0 {
-		repoIDs = showRepoIds.Values()
+		repoIDs = showRepoIDs.Values()
 	}
 	repoIDs = slices.DeleteFunc(repoIDs, func(v int64) bool {
-		return !showRepoIds.Contains(v)
+		return !showRepoIDs.Contains(v)
 	})
 
 	var pagerCount int
diff --git a/services/repository/create_test.go b/services/repository/create_test.go
index b3e1f0550c6..41e6b615db7 100644
--- a/services/repository/create_test.go
+++ b/services/repository/create_test.go
@@ -21,12 +21,12 @@ import (
 func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	testTeamRepositories := func(teamID int64, repoIds []int64) {
+	testTeamRepositories := func(teamID int64, repoIDs []int64) {
 		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
 		assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name)
 		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
-		assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name)
-		for i, rid := range repoIds {
+		assert.Len(t, team.Repos, len(repoIDs), "%s: repo count", team.Name)
+		for i, rid := range repoIDs {
 			if rid > 0 {
 				assert.True(t, HasRepository(db.DefaultContext, team, rid), "%s: HasRepository(%d) %d", rid, i)
 			}
@@ -52,12 +52,12 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
 
 	// Create repos.
-	repoIds := make([]int64, 0)
+	repoIDs := make([]int64, 0)
 	for i := 0; i < 3; i++ {
 		r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
 		assert.NoError(t, err, "CreateRepository %d", i)
 		if r != nil {
-			repoIds = append(repoIds, r.ID)
+			repoIDs = append(repoIDs, r.ID)
 		}
 	}
 	// Get fresh copy of Owner team after creating repos.
@@ -93,10 +93,10 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 		},
 	}
 	teamRepos := [][]int64{
-		repoIds,
-		repoIds,
+		repoIDs,
+		repoIDs,
 		{},
-		repoIds,
+		repoIDs,
 		{},
 	}
 	for i, team := range teams {
@@ -109,7 +109,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	// Update teams and check repositories.
 	teams[3].IncludesAllRepositories = false
 	teams[4].IncludesAllRepositories = true
-	teamRepos[4] = repoIds
+	teamRepos[4] = repoIDs
 	for i, team := range teams {
 		assert.NoError(t, models.UpdateTeam(db.DefaultContext, team, false, true), "%s: UpdateTeam", team.Name)
 		testTeamRepositories(team.ID, teamRepos[i])
@@ -119,27 +119,27 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: "repo-last"})
 	assert.NoError(t, err, "CreateRepository last")
 	if r != nil {
-		repoIds = append(repoIds, r.ID)
+		repoIDs = append(repoIDs, r.ID)
 	}
-	teamRepos[0] = repoIds
-	teamRepos[1] = repoIds
-	teamRepos[4] = repoIds
+	teamRepos[0] = repoIDs
+	teamRepos[1] = repoIDs
+	teamRepos[4] = repoIDs
 	for i, team := range teams {
 		testTeamRepositories(team.ID, teamRepos[i])
 	}
 
 	// Remove repo and check teams repositories.
-	assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIds[0]), "DeleteRepository")
-	teamRepos[0] = repoIds[1:]
-	teamRepos[1] = repoIds[1:]
-	teamRepos[3] = repoIds[1:3]
-	teamRepos[4] = repoIds[1:]
+	assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository")
+	teamRepos[0] = repoIDs[1:]
+	teamRepos[1] = repoIDs[1:]
+	teamRepos[3] = repoIDs[1:3]
+	teamRepos[4] = repoIDs[1:]
 	for i, team := range teams {
 		testTeamRepositories(team.ID, teamRepos[i])
 	}
 
 	// Wipe created items.
-	for i, rid := range repoIds {
+	for i, rid := range repoIDs {
 		if i > 0 { // first repo already deleted.
 			assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i)
 		}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 7c10074bc5e..4c09a9d588d 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -44,7 +44,7 @@ parts:
     source: .
     stage-packages: [ git, sqlite3, openssh-client ]
     build-packages: [ git, libpam0g-dev, libsqlite3-dev, build-essential]
-    build-snaps: [ go/1.21/stable, node/18/stable ]
+    build-snaps: [ go/1.22/stable, node/20/stable ]
     build-environment:
       - LDFLAGS: ""
     override-pull: |