diff --git a/integrations/mirror_push_test.go b/integrations/mirror_push_test.go
index a73b69e786..3a22b00754 100644
--- a/integrations/mirror_push_test.go
+++ b/integrations/mirror_push_test.go
@@ -13,6 +13,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -47,7 +48,7 @@ func testMirrorPush(t *testing.T, u *url.URL) {
 
 	doCreatePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword)(t)
 
-	mirrors, err := repo_model.GetPushMirrorsByRepoID(srcRepo.ID)
+	mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
 	assert.NoError(t, err)
 	assert.Len(t, mirrors, 1)
 
@@ -72,7 +73,7 @@ func testMirrorPush(t *testing.T, u *url.URL) {
 
 	// Cleanup
 	doRemovePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword, int(mirrors[0].ID))(t)
-	mirrors, err = repo_model.GetPushMirrorsByRepoID(srcRepo.ID)
+	mirrors, _, err = repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
 	assert.NoError(t, err)
 	assert.Len(t, mirrors, 0)
 }
diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go
index 0a7dea79c9..02ffcbee76 100644
--- a/models/repo/pushmirror.go
+++ b/models/repo/pushmirror.go
@@ -5,12 +5,15 @@
 package repo
 
 import (
+	"context"
 	"errors"
 	"time"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/timeutil"
+
+	"xorm.io/builder"
 )
 
 // ErrPushMirrorNotExist mirror does not exist error
@@ -29,6 +32,25 @@ type PushMirror struct {
 	LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
 	LastError      string             `xorm:"text"`
 }
+type PushMirrorOptions struct {
+	ID         int64
+	RepoID     int64
+	RemoteName string
+}
+
+func (opts *PushMirrorOptions) toConds() builder.Cond {
+	cond := builder.NewCond()
+	if opts.RepoID > 0 {
+		cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
+	}
+	if opts.RemoteName != "" {
+		cond = cond.And(builder.Eq{"remote_name": opts.RemoteName})
+	}
+	if opts.ID > 0 {
+		cond = cond.And(builder.Eq{"id": opts.ID})
+	}
+	return cond
+}
 
 func init() {
 	db.RegisterModel(new(PushMirror))
@@ -53,45 +75,48 @@ func (m *PushMirror) GetRemoteName() string {
 }
 
 // InsertPushMirror inserts a push-mirror to database
-func InsertPushMirror(m *PushMirror) error {
-	_, err := db.GetEngine(db.DefaultContext).Insert(m)
+func InsertPushMirror(ctx context.Context, m *PushMirror) error {
+	_, err := db.GetEngine(ctx).Insert(m)
 	return err
 }
 
 // UpdatePushMirror updates the push-mirror
-func UpdatePushMirror(m *PushMirror) error {
-	_, err := db.GetEngine(db.DefaultContext).ID(m.ID).AllCols().Update(m)
+func UpdatePushMirror(ctx context.Context, m *PushMirror) error {
+	_, err := db.GetEngine(ctx).ID(m.ID).AllCols().Update(m)
 	return err
 }
 
-// DeletePushMirrorByID deletes a push-mirrors by ID
-func DeletePushMirrorByID(ID int64) error {
-	_, err := db.GetEngine(db.DefaultContext).ID(ID).Delete(&PushMirror{})
-	return err
+func DeletePushMirrors(ctx context.Context, opts PushMirrorOptions) error {
+	if opts.RepoID > 0 {
+		_, err := db.GetEngine(ctx).Where(opts.toConds()).Delete(&PushMirror{})
+		return err
+	}
+	return errors.New("repoID required and must be set")
 }
 
-// DeletePushMirrorsByRepoID deletes all push-mirrors by repoID
-func DeletePushMirrorsByRepoID(repoID int64) error {
-	_, err := db.GetEngine(db.DefaultContext).Delete(&PushMirror{RepoID: repoID})
-	return err
-}
-
-// GetPushMirrorByID returns push-mirror information.
-func GetPushMirrorByID(ID int64) (*PushMirror, error) {
-	m := &PushMirror{}
-	has, err := db.GetEngine(db.DefaultContext).ID(ID).Get(m)
+func GetPushMirror(ctx context.Context, opts PushMirrorOptions) (*PushMirror, error) {
+	mirror := &PushMirror{}
+	exist, err := db.GetEngine(ctx).Where(opts.toConds()).Get(mirror)
 	if err != nil {
 		return nil, err
-	} else if !has {
+	} else if !exist {
 		return nil, ErrPushMirrorNotExist
 	}
-	return m, nil
+	return mirror, nil
 }
 
 // GetPushMirrorsByRepoID returns push-mirror information of a repository.
-func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) {
+func GetPushMirrorsByRepoID(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*PushMirror, int64, error) {
+	sess := db.GetEngine(ctx).Where("repo_id = ?", repoID)
+	if listOptions.Page != 0 {
+		sess = db.SetSessionPagination(sess, &listOptions)
+		mirrors := make([]*PushMirror, 0, listOptions.PageSize)
+		count, err := sess.FindAndCount(&mirrors)
+		return mirrors, count, err
+	}
 	mirrors := make([]*PushMirror, 0, 10)
-	return mirrors, db.GetEngine(db.DefaultContext).Where("repo_id=?", repoID).Find(&mirrors)
+	count, err := sess.FindAndCount(&mirrors)
+	return mirrors, count, err
 }
 
 // GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits
@@ -103,8 +128,8 @@ func GetPushMirrorsSyncedOnCommit(repoID int64) ([]*PushMirror, error) {
 }
 
 // PushMirrorsIterate iterates all push-mirror repositories.
-func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
-	return db.GetEngine(db.DefaultContext).
+func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean interface{}) error) error {
+	return db.GetEngine(ctx).
 		Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
 		And("`interval` != 0").
 		OrderBy("last_update ASC").
diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go
index d36a48547e..5087e30095 100644
--- a/models/repo/pushmirror_test.go
+++ b/models/repo/pushmirror_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 	"time"
 
+	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/timeutil"
@@ -20,20 +21,20 @@ func TestPushMirrorsIterate(t *testing.T) {
 
 	now := timeutil.TimeStampNow()
 
-	repo_model.InsertPushMirror(&repo_model.PushMirror{
+	repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{
 		RemoteName:     "test-1",
 		LastUpdateUnix: now,
 		Interval:       1,
 	})
 
 	long, _ := time.ParseDuration("24h")
-	repo_model.InsertPushMirror(&repo_model.PushMirror{
+	repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{
 		RemoteName:     "test-2",
 		LastUpdateUnix: now,
 		Interval:       long,
 	})
 
-	repo_model.InsertPushMirror(&repo_model.PushMirror{
+	repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{
 		RemoteName:     "test-3",
 		LastUpdateUnix: now,
 		Interval:       0,
@@ -41,7 +42,7 @@ func TestPushMirrorsIterate(t *testing.T) {
 
 	time.Sleep(1 * time.Millisecond)
 
-	repo_model.PushMirrorsIterate(1, func(idx int, bean interface{}) error {
+	repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean interface{}) error {
 		m, ok := bean.(*repo_model.PushMirror)
 		assert.True(t, ok)
 		assert.Equal(t, "test-1", m.RemoteName)
diff --git a/modules/context/repo.go b/modules/context/repo.go
index 1d9f98158e..ea40542069 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -393,7 +393,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
 		}
 	}
 
-	pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID)
+	pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
 	if err != nil {
 		ctx.ServerError("GetPushMirrorsByRepoID", err)
 		return
diff --git a/modules/convert/mirror.go b/modules/convert/mirror.go
new file mode 100644
index 0000000000..b2414f4677
--- /dev/null
+++ b/modules/convert/mirror.go
@@ -0,0 +1,39 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package convert
+
+import (
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/modules/git"
+	api "code.gitea.io/gitea/modules/structs"
+)
+
+// ToPushMirror convert from repo_model.PushMirror and remoteAddress to api.TopicResponse
+func ToPushMirror(pm *repo_model.PushMirror) (*api.PushMirror, error) {
+	repo := pm.GetRepository()
+	remoteAddress, err := getRemoteAddress(repo, pm.RemoteName)
+	if err != nil {
+		return nil, err
+	}
+	return &api.PushMirror{
+		RepoName:       repo.Name,
+		RemoteName:     pm.RemoteName,
+		RemoteAddress:  remoteAddress,
+		CreatedUnix:    pm.CreatedUnix.FormatLong(),
+		LastUpdateUnix: pm.LastUpdateUnix.FormatLong(),
+		LastError:      pm.LastError,
+		Interval:       pm.Interval.String(),
+	}, nil
+}
+
+func getRemoteAddress(repo *repo_model.Repository, remoteName string) (string, error) {
+	url, err := git.GetRemoteURL(git.DefaultContext, repo.RepoPath(), remoteName)
+	if err != nil {
+		return "", err
+	}
+	// remove confidential information
+	url.User = nil
+	return url.String(), nil
+}
diff --git a/modules/structs/mirror.go b/modules/structs/mirror.go
new file mode 100644
index 0000000000..8e8a8a2705
--- /dev/null
+++ b/modules/structs/mirror.go
@@ -0,0 +1,25 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package structs
+
+// CreatePushMirrorOption represents need information to create a push mirror of a repository.
+type CreatePushMirrorOption struct {
+	RemoteAddress  string `json:"remote_address"`
+	RemoteUsername string `json:"remote_username"`
+	RemotePassword string `json:"remote_password"`
+	Interval       string `json:"interval"`
+}
+
+// PushMirror represents information of a push mirror
+// swagger:model
+type PushMirror struct {
+	RepoName       string `json:"repo_name"`
+	RemoteName     string `json:"remote_name"`
+	RemoteAddress  string `json:"remote_address"`
+	CreatedUnix    string `json:"created"`
+	LastUpdateUnix string `json:"last_update"`
+	LastError      string `json:"last_error"`
+	Interval       string `json:"interval"`
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 44e0c290a0..e1478fa2aa 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -982,6 +982,15 @@ func Routes() *web.Route {
 					})
 				}, reqRepoReader(unit.TypeReleases))
 				m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync)
+				m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync)
+				m.Group("/push_mirrors", func() {
+					m.Combo("").Get(repo.ListPushMirrors).
+						Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror)
+					m.Combo("/{name}").
+						Delete(repo.DeletePushMirrorByRemoteName).
+						Get(repo.GetPushMirrorByName)
+				}, reqAdmin())
+
 				m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig)
 				m.Group("/pulls", func() {
 					m.Combo("").Get(repo.ListPullRequests).
diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go
index 3d29383550..91e5e0c031 100644
--- a/routers/api/v1/repo/mirror.go
+++ b/routers/api/v1/repo/mirror.go
@@ -6,13 +6,25 @@ package repo
 
 import (
 	"errors"
+	"fmt"
 	"net/http"
+	"time"
 
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/convert"
 	mirror_module "code.gitea.io/gitea/modules/mirror"
 	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/util"
+	"code.gitea.io/gitea/modules/web"
+	"code.gitea.io/gitea/routers/api/v1/utils"
+	"code.gitea.io/gitea/services/forms"
+	"code.gitea.io/gitea/services/migrations"
+	mirror_service "code.gitea.io/gitea/services/mirror"
 )
 
 // MirrorSync adds a mirrored repository to the sync queue
@@ -63,3 +75,317 @@ func MirrorSync(ctx *context.APIContext) {
 
 	ctx.Status(http.StatusOK)
 }
+
+// PushMirrorSync adds all push mirrored repositories to the sync queue
+func PushMirrorSync(ctx *context.APIContext) {
+	// swagger:operation POST /repos/{owner}/{repo}/push_mirrors-sync repository repoPushMirrorSync
+	// ---
+	// summary: Sync all push mirrored repository
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo to sync
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo to sync
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/empty"
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+
+	if !setting.Mirror.Enabled {
+		ctx.Error(http.StatusBadRequest, "PushMirrorSync", "Mirror feature is disabled")
+		return
+	}
+	// Get All push mirrors of a specific repo
+	pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
+	if err != nil {
+		ctx.Error(http.StatusNotFound, "PushMirrorSync", err)
+		return
+	}
+	for _, mirror := range pushMirrors {
+		ok := mirror_service.SyncPushMirror(ctx, mirror.ID)
+		if !ok {
+			ctx.Error(http.StatusInternalServerError, "PushMirrorSync", "error occurred when syncing push mirror "+mirror.RemoteName)
+			return
+		}
+	}
+
+	ctx.Status(http.StatusOK)
+}
+
+// ListPushMirrors get list of push mirrors of a repository
+func ListPushMirrors(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/push_mirrors repository repoListPushMirrors
+	// ---
+	// summary: Get all push mirrors of the repository
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: page
+	//   in: query
+	//   description: page number of results to return (1-based)
+	//   type: integer
+	// - name: limit
+	//   in: query
+	//   description: page size of results
+	//   type: integer
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/PushMirrorList"
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+
+	if !setting.Mirror.Enabled {
+		ctx.Error(http.StatusBadRequest, "GetPushMirrorsByRepoID", "Mirror feature is disabled")
+		return
+	}
+
+	repo := ctx.Repo.Repository
+	// Get all push mirrors for the specified repository.
+	pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, utils.GetListOptions(ctx))
+	if err != nil {
+		ctx.Error(http.StatusNotFound, "GetPushMirrorsByRepoID", err)
+		return
+	}
+
+	responsePushMirrors := make([]*api.PushMirror, 0, len(pushMirrors))
+	for _, mirror := range pushMirrors {
+		m, err := convert.ToPushMirror(mirror)
+		if err == nil {
+			responsePushMirrors = append(responsePushMirrors, m)
+		}
+
+	}
+	ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize)
+	ctx.SetTotalCountHeader(count)
+	ctx.JSON(http.StatusOK, responsePushMirrors)
+}
+
+// GetPushMirrorByName get push mirror of a repository by name
+func GetPushMirrorByName(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/push_mirrors/{name} repository repoGetPushMirrorByRemoteName
+	// ---
+	// summary: Get push mirror of the repository by remoteName
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: name
+	//   in: path
+	//   description: remote name of push mirror
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/PushMirror"
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+
+	if !setting.Mirror.Enabled {
+		ctx.Error(http.StatusBadRequest, "GetPushMirrorByRemoteName", "Mirror feature is disabled")
+		return
+	}
+
+	mirrorName := ctx.Params(":name")
+	// Get push mirror of a specific repo by remoteName
+	pushMirror, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: mirrorName})
+	if err != nil {
+		ctx.Error(http.StatusNotFound, "GetPushMirrors", err)
+		return
+	}
+	m, err := convert.ToPushMirror(pushMirror)
+	if err != nil {
+		ctx.ServerError("GetPushMirrorByRemoteName", err)
+		return
+	}
+	ctx.JSON(http.StatusOK, m)
+}
+
+// AddPushMirror adds a push mirror to a repository
+func AddPushMirror(ctx *context.APIContext) {
+	// swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror
+	// ---
+	// summary: add a push mirror to the repository
+	// consumes:
+	// - application/json
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: body
+	//   in: body
+	//   schema:
+	//     "$ref": "#/definitions/CreatePushMirrorOption"
+	// responses:
+	//   "201":
+	//     "$ref": "#/responses/PushMirror"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+	//   "400":
+	//     "$ref": "#/responses/error"
+
+	if !setting.Mirror.Enabled {
+		ctx.Error(http.StatusBadRequest, "AddPushMirror", "Mirror feature is disabled")
+		return
+	}
+
+	pushMirror := web.GetForm(ctx).(*api.CreatePushMirrorOption)
+	CreatePushMirror(ctx, pushMirror)
+}
+
+// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName
+func DeletePushMirrorByRemoteName(ctx *context.APIContext) {
+	// swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror
+	// ---
+	// summary: deletes a push mirror from a repository by remoteName
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: name
+	//   in: path
+	//   description: remote name of the pushMirror
+	//   type: string
+	//   required: true
+	// responses:
+	//   "204":
+	//     "$ref": "#/responses/empty"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+	//   "400":
+	//     "$ref": "#/responses/error"
+
+	if !setting.Mirror.Enabled {
+		ctx.Error(http.StatusBadRequest, "DeletePushMirrorByName", "Mirror feature is disabled")
+		return
+	}
+
+	remoteName := ctx.Params(":name")
+	// Delete push mirror on repo by name.
+	err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: remoteName})
+	if err != nil {
+		ctx.Error(http.StatusNotFound, "DeletePushMirrors", err)
+		return
+	}
+	ctx.Status(http.StatusNoContent)
+}
+
+func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirrorOption) {
+	repo := ctx.Repo.Repository
+
+	interval, err := time.ParseDuration(mirrorOption.Interval)
+	if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
+		ctx.Error(http.StatusBadRequest, "CreatePushMirror", err)
+		return
+	}
+
+	address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword)
+	if err == nil {
+		err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser)
+	}
+	if err != nil {
+		HandleRemoteAddressError(ctx, err)
+		return
+	}
+
+	remoteSuffix, err := util.CryptoRandomString(10)
+	if err != nil {
+		ctx.ServerError("CryptoRandomString", err)
+		return
+	}
+
+	pushMirror := &repo_model.PushMirror{
+		RepoID:     repo.ID,
+		Repo:       repo,
+		RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
+		Interval:   interval,
+	}
+
+	if err = repo_model.InsertPushMirror(ctx, pushMirror); err != nil {
+		ctx.ServerError("InsertPushMirror", err)
+		return
+	}
+
+	// if the registration of the push mirrorOption fails remove it from the database
+	if err = mirror_service.AddPushMirrorRemote(ctx, pushMirror, address); err != nil {
+		if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: pushMirror.ID, RepoID: pushMirror.RepoID}); err != nil {
+			ctx.ServerError("DeletePushMirrors", err)
+		}
+		ctx.ServerError("AddPushMirrorRemote", err)
+		return
+	}
+	m, err := convert.ToPushMirror(pushMirror)
+	if err != nil {
+		ctx.ServerError("ToPushMirror", err)
+		return
+	}
+	ctx.JSON(http.StatusOK, m)
+}
+
+func HandleRemoteAddressError(ctx *context.APIContext, err error) {
+	if models.IsErrInvalidCloneAddr(err) {
+		addrErr := err.(*models.ErrInvalidCloneAddr)
+		switch {
+		case addrErr.IsProtocolInvalid:
+			ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol")
+		case addrErr.IsURLError:
+			ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid Url ")
+		case addrErr.IsPermissionDenied:
+			ctx.Error(http.StatusUnauthorized, "CreatePushMirror", "Permission denied")
+		default:
+			ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Unknown error")
+		}
+		return
+	}
+}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index df3d011246..e8cfc0706f 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -169,4 +169,7 @@ type swaggerParameterBodies struct {
 
 	// in:body
 	CreateWikiPageOptions api.CreateWikiPageOptions
+
+	// in:body
+	CreatePushMirrorOption api.CreatePushMirrorOption
 }
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index ab802db781..3522e24276 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -345,6 +345,20 @@ type swaggerWikiCommitList struct {
 	Body api.WikiCommitList `json:"body"`
 }
 
+// PushMirror
+// swagger:response PushMirror
+type swaggerPushMirror struct {
+	// in:body
+	Body api.PushMirror `json:"body"`
+}
+
+// PushMirrorList
+// swagger:response PushMirrorList
+type swaggerPushMirrorList struct {
+	// in:body
+	Body []api.PushMirror `json:"body"`
+}
+
 // RepoCollaboratorPermission
 // swagger:response RepoCollaboratorPermission
 type swaggerRepoCollaboratorPermission struct {
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 7f6b0feafb..a59824cecd 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -90,7 +90,7 @@ func SettingsCtxData(ctx *context.Context) {
 		}
 		ctx.Data["StatsIndexerStatus"] = status
 	}
-	pushMirrors, err := repo_model.GetPushMirrorsByRepoID(ctx.Repo.Repository.ID)
+	pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
 	if err != nil {
 		ctx.ServerError("GetPushMirrorsByRepoID", err)
 		return
@@ -284,7 +284,7 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
-		m, err := selectPushMirrorByForm(form, repo)
+		m, err := selectPushMirrorByForm(ctx, form, repo)
 		if err != nil {
 			ctx.NotFound("", nil)
 			return
@@ -305,7 +305,7 @@ func SettingsPost(ctx *context.Context) {
 		// as an error on the UI for this action
 		ctx.Data["Err_RepoName"] = nil
 
-		m, err := selectPushMirrorByForm(form, repo)
+		m, err := selectPushMirrorByForm(ctx, form, repo)
 		if err != nil {
 			ctx.NotFound("", nil)
 			return
@@ -316,7 +316,7 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
-		if err = repo_model.DeletePushMirrorByID(m.ID); err != nil {
+		if err = repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
 			ctx.ServerError("DeletePushMirrorByID", err)
 			return
 		}
@@ -364,14 +364,14 @@ func SettingsPost(ctx *context.Context) {
 			SyncOnCommit: form.PushMirrorSyncOnCommit,
 			Interval:     interval,
 		}
-		if err := repo_model.InsertPushMirror(m); err != nil {
+		if err := repo_model.InsertPushMirror(ctx, m); err != nil {
 			ctx.ServerError("InsertPushMirror", err)
 			return
 		}
 
 		if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil {
-			if err := repo_model.DeletePushMirrorByID(m.ID); err != nil {
-				log.Error("DeletePushMirrorByID %v", err)
+			if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
+				log.Error("DeletePushMirrors %v", err)
 			}
 			ctx.ServerError("AddPushMirrorRemote", err)
 			return
@@ -1222,13 +1222,13 @@ func SettingsDeleteAvatar(ctx *context.Context) {
 	ctx.Redirect(ctx.Repo.RepoLink + "/settings")
 }
 
-func selectPushMirrorByForm(form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
+func selectPushMirrorByForm(ctx *context.Context, form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
 	id, err := strconv.ParseInt(form.PushMirrorID, 10, 64)
 	if err != nil {
 		return nil, err
 	}
 
-	pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID)
+	pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
 	if err != nil {
 		return nil, err
 	}
diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index 8321829ad2..3b4a8e5f8a 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -106,7 +106,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
 
 	pushMirrorsRequested := 0
 	if pushLimit != 0 {
-		if err := repo_model.PushMirrorsIterate(pushLimit, func(idx int, bean interface{}) error {
+		if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean interface{}) error {
 			if err := handler(idx, bean); err != nil {
 				return err
 			}
diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go
index 2927bed72b..0c8960d78b 100644
--- a/services/mirror/mirror_push.go
+++ b/services/mirror/mirror_push.go
@@ -94,7 +94,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
 		log.Error("PANIC whilst syncPushMirror[%d] Panic: %v\nStacktrace: %s", mirrorID, err, log.Stack(2))
 	}()
 
-	m, err := repo_model.GetPushMirrorByID(mirrorID)
+	m, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{ID: mirrorID})
 	if err != nil {
 		log.Error("GetPushMirrorByID [%d]: %v", mirrorID, err)
 		return false
@@ -116,7 +116,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
 
 	m.LastUpdateUnix = timeutil.TimeStampNow()
 
-	if err := repo_model.UpdatePushMirror(m); err != nil {
+	if err := repo_model.UpdatePushMirror(ctx, m); err != nil {
 		log.Error("UpdatePushMirror [%d]: %v", m.ID, err)
 
 		return false
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index c0dd50a7b9..daec38044f 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -8774,6 +8774,233 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/push_mirrors": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Get all push mirrors of the repository",
+        "operationId": "repoListPushMirrors",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "page number of results to return (1-based)",
+            "name": "page",
+            "in": "query"
+          },
+          {
+            "type": "integer",
+            "description": "page size of results",
+            "name": "limit",
+            "in": "query"
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/PushMirrorList"
+          },
+          "400": {
+            "$ref": "#/responses/error"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          }
+        }
+      },
+      "post": {
+        "consumes": [
+          "application/json"
+        ],
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "add a push mirror to the repository",
+        "operationId": "repoAddPushMirror",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "name": "body",
+            "in": "body",
+            "schema": {
+              "$ref": "#/definitions/CreatePushMirrorOption"
+            }
+          }
+        ],
+        "responses": {
+          "201": {
+            "$ref": "#/responses/PushMirror"
+          },
+          "400": {
+            "$ref": "#/responses/error"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          }
+        }
+      }
+    },
+    "/repos/{owner}/{repo}/push_mirrors-sync": {
+      "post": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Sync all push mirrored repository",
+        "operationId": "repoPushMirrorSync",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo to sync",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo to sync",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/empty"
+          },
+          "400": {
+            "$ref": "#/responses/error"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          }
+        }
+      }
+    },
+    "/repos/{owner}/{repo}/push_mirrors/{name}": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Get push mirror of the repository by remoteName",
+        "operationId": "repoGetPushMirrorByRemoteName",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "remote name of push mirror",
+            "name": "name",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/PushMirror"
+          },
+          "400": {
+            "$ref": "#/responses/error"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          }
+        }
+      },
+      "delete": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "deletes a push mirror from a repository by remoteName",
+        "operationId": "repoDeletePushMirror",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "remote name of the pushMirror",
+            "name": "name",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "204": {
+            "$ref": "#/responses/empty"
+          },
+          "400": {
+            "$ref": "#/responses/error"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/raw/{filepath}": {
       "get": {
         "produces": [
@@ -14441,6 +14668,29 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "CreatePushMirrorOption": {
+      "type": "object",
+      "title": "CreatePushMirrorOption represents need information to create a push mirror of a repository.",
+      "properties": {
+        "interval": {
+          "type": "string",
+          "x-go-name": "Interval"
+        },
+        "remote_address": {
+          "type": "string",
+          "x-go-name": "RemoteAddress"
+        },
+        "remote_password": {
+          "type": "string",
+          "x-go-name": "RemotePassword"
+        },
+        "remote_username": {
+          "type": "string",
+          "x-go-name": "RemoteUsername"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "CreateReleaseOption": {
       "description": "CreateReleaseOption options when creating a release",
       "type": "object",
@@ -17516,6 +17766,41 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "PushMirror": {
+      "description": "PushMirror represents information of a push mirror",
+      "type": "object",
+      "properties": {
+        "created": {
+          "type": "string",
+          "x-go-name": "CreatedUnix"
+        },
+        "interval": {
+          "type": "string",
+          "x-go-name": "Interval"
+        },
+        "last_error": {
+          "type": "string",
+          "x-go-name": "LastError"
+        },
+        "last_update": {
+          "type": "string",
+          "x-go-name": "LastUpdateUnix"
+        },
+        "remote_address": {
+          "type": "string",
+          "x-go-name": "RemoteAddress"
+        },
+        "remote_name": {
+          "type": "string",
+          "x-go-name": "RemoteName"
+        },
+        "repo_name": {
+          "type": "string",
+          "x-go-name": "RepoName"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "Reaction": {
       "description": "Reaction contain one reaction",
       "type": "object",
@@ -19293,6 +19578,21 @@
         }
       }
     },
+    "PushMirror": {
+      "description": "PushMirror",
+      "schema": {
+        "$ref": "#/definitions/PushMirror"
+      }
+    },
+    "PushMirrorList": {
+      "description": "PushMirrorList",
+      "schema": {
+        "type": "array",
+        "items": {
+          "$ref": "#/definitions/PushMirror"
+        }
+      }
+    },
     "Reaction": {
       "description": "Reaction",
       "schema": {
@@ -19572,7 +19872,7 @@
     "parameterBodies": {
       "description": "parameterBodies",
       "schema": {
-        "$ref": "#/definitions/CreateWikiPageOptions"
+        "$ref": "#/definitions/CreatePushMirrorOption"
       }
     },
     "redirect": {