diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index dd31562c3a7..62471abe6f1 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2279,6 +2279,8 @@ release.downloads = Downloads
 release.download_count = Downloads: %s
 release.add_tag_msg = Use the title and content of release as tag message.
 release.add_tag = Create Tag Only
+release.releases_for = Releases for %s
+release.tags_for = Tags for %s
 
 branch.name = Branch Name
 branch.search = Search branches
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index 7d472a7f87c..7c375a085f0 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -12,6 +12,7 @@ import (
 	"strings"
 
 	activities_model "code.gitea.io/gitea/models/activities"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/markup/markdown"
@@ -268,3 +269,46 @@ func GetFeedType(name string, req *http.Request) (bool, string, string) {
 
 	return false, name, ""
 }
+
+// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
+func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, isReleasesOnly bool) (items []*feeds.Item, err error) {
+	for _, rel := range releases {
+		err := rel.LoadAttributes(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		var title, content string
+
+		if rel.IsTag {
+			title = rel.TagName
+		} else {
+			title = rel.Title
+		}
+
+		link := &feeds.Link{Href: rel.HTMLURL()}
+		content, err = markdown.RenderString(&markup.RenderContext{
+			Ctx:       ctx,
+			URLPrefix: rel.Repo.Link(),
+			Metas:     rel.Repo.ComposeMetas(),
+		}, rel.Note)
+
+		if err != nil {
+			return nil, err
+		}
+
+		items = append(items, &feeds.Item{
+			Title:   title,
+			Link:    link,
+			Created: rel.CreatedUnix.AsTime(),
+			Author: &feeds.Author{
+				Name:  rel.Publisher.DisplayName(),
+				Email: rel.Publisher.GetEmail(),
+			},
+			Id:      fmt.Sprintf("%v: %v", strconv.FormatInt(rel.ID, 10), link.Href),
+			Content: content,
+		})
+	}
+
+	return items, err
+}
diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go
new file mode 100644
index 00000000000..fbfa11c63ec
--- /dev/null
+++ b/routers/web/feed/release.go
@@ -0,0 +1,50 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package feed
+
+import (
+	"time"
+
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/modules/context"
+
+	"github.com/gorilla/feeds"
+)
+
+// shows tags and/or releases on the repo as RSS / Atom feed
+func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleasesOnly bool, formatType string) {
+	releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
+		IncludeTags: !isReleasesOnly,
+	})
+	if err != nil {
+		ctx.ServerError("GetReleasesByRepoID", err)
+		return
+	}
+
+	var title string
+	var link *feeds.Link
+
+	if isReleasesOnly {
+		title = ctx.Tr("repo.release.releases_for", repo.FullName())
+		link = &feeds.Link{Href: repo.HTMLURL() + "/release"}
+	} else {
+		title = ctx.Tr("repo.release.tags_for", repo.FullName())
+		link = &feeds.Link{Href: repo.HTMLURL() + "/tags"}
+	}
+
+	feed := &feeds.Feed{
+		Title:       title,
+		Link:        link,
+		Description: repo.Description,
+		Created:     time.Now(),
+	}
+
+	feed.Items, err = releasesToFeedItems(ctx, releases, isReleasesOnly)
+	if err != nil {
+		ctx.ServerError("releasesToFeedItems", err)
+		return
+	}
+
+	writeFeed(ctx, feed, formatType)
+}
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 2047a1cfb9c..54f503642bc 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -23,6 +23,7 @@ import (
 	"code.gitea.io/gitea/modules/upload"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
+	"code.gitea.io/gitea/routers/web/feed"
 	"code.gitea.io/gitea/services/forms"
 	releaseservice "code.gitea.io/gitea/services/release"
 )
@@ -199,6 +200,30 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
 	ctx.HTML(http.StatusOK, tplReleases)
 }
 
+// ReleasesFeedRSS get feeds for releases in RSS format
+func ReleasesFeedRSS(ctx *context.Context) {
+	releasesOrTagsFeed(ctx, true, "rss")
+}
+
+// TagsListFeedRSS get feeds for tags in RSS format
+func TagsListFeedRSS(ctx *context.Context) {
+	releasesOrTagsFeed(ctx, false, "rss")
+}
+
+// ReleasesFeedAtom get feeds for releases in Atom format
+func ReleasesFeedAtom(ctx *context.Context) {
+	releasesOrTagsFeed(ctx, true, "atom")
+}
+
+// TagsListFeedAtom get feeds for tags in RSS format
+func TagsListFeedAtom(ctx *context.Context) {
+	releasesOrTagsFeed(ctx, false, "atom")
+}
+
+func releasesOrTagsFeed(ctx *context.Context, isReleasesOnly bool, formatType string) {
+	feed.ShowReleaseFeed(ctx, ctx.Repo.Repository, isReleasesOnly, formatType)
+}
+
 // SingleRelease renders a single release's page
 func SingleRelease(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.release.releases")
diff --git a/routers/web/web.go b/routers/web/web.go
index 20d067a163c..31b3eb9baad 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1087,12 +1087,21 @@ func RegisterRoutes(m *web.Route) {
 
 	// Releases
 	m.Group("/{username}/{reponame}", func() {
-		m.Get("/tags", repo.TagsList, repo.MustBeNotEmpty,
-			reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag))
+		m.Group("/tags", func() {
+			m.Get("", repo.TagsList)
+			m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
+			m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
+		}, func(ctx *context.Context) {
+			ctx.Data["EnableFeed"] = setting.EnableFeed
+		}, repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
 		m.Group("/releases", func() {
 			m.Get("/", repo.Releases)
 			m.Get("/tag/*", repo.SingleRelease)
 			m.Get("/latest", repo.LatestRelease)
+			m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
+			m.Get(".atom", feedEnabled, repo.ReleasesFeedAtom)
+		}, func(ctx *context.Context) {
+			ctx.Data["EnableFeed"] = setting.EnableFeed
 		}, repo.MustBeNotEmpty, reqRepoReleaseReader, context.RepoRefByType(context.RepoRefTag, true))
 		m.Get("/releases/attachments/{uuid}", repo.GetAttachment, repo.MustBeNotEmpty, reqRepoReleaseReader)
 		m.Group("/releases", func() {
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index f8c54bf7f6e..9f4104bec5c 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -11,6 +11,9 @@
 				{{.locale.Tr "repo.release.tags"}}
 			{{end}}
 		
+		{{if .EnableFeed}}
+			{{svg "octicon-rss" 18}}
+		{{end}}
 		{{if (and .CanCreateRelease (not .PageIsTagList))}}
 			
 				{{.locale.Tr "repo.release.new_release"}}