diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index b3b9fd96cc..ba5b7c8d7b 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -62,6 +62,8 @@ DEFAULT_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,re
 PREFIX_ARCHIVE_FILES = true
 ; Disable the creation of new mirrors. Pre-existing mirrors remain valid.
 DISABLE_MIRRORS = false
+; Disable migrating feature.
+DISABLE_MIGRATIONS = true
 ; The default branch name of new repositories
 DEFAULT_BRANCH=master
 ; Allow adoption of unadopted repositories
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 673eeac79d..a862f55ba0 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -71,6 +71,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `ENABLE_PUSH_CREATE_ORG`:  **false**: Allow users to push local repositories to Gitea and have them automatically created for an org.
 - `PREFIX_ARCHIVE_FILES`: **true**: Prefix archive files by placing them in a directory named after the repository.
 - `DISABLE_MIRRORS`: **false**: Disable the creation of **new** mirrors. Pre-existing mirrors remain valid.
+- `DISABLE_MIGRATIONS`: **false**: Disable migrating feature.
 - `DEFAULT_BRANCH`: **master**: Default branch name of all repositories.
 
 ### Repository - Pull Request (`repository.pull-request`)
diff --git a/modules/context/context.go b/modules/context/context.go
index 1405860e07..c6597cf024 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -343,6 +343,7 @@ func Contexter() macaron.Handler {
 
 		ctx.Data["EnableSwagger"] = setting.API.EnableSwagger
 		ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
+		ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
 
 		c.Map(ctx)
 	}
diff --git a/modules/cron/tasks_basic.go b/modules/cron/tasks_basic.go
index 4da21fc7d9..81b1a9cb9c 100644
--- a/modules/cron/tasks_basic.go
+++ b/modules/cron/tasks_basic.go
@@ -115,5 +115,7 @@ func initBasicTasks() {
 	registerArchiveCleanup()
 	registerSyncExternalUsers()
 	registerDeletedBranchesCleanup()
-	registerUpdateMigrationPosterID()
+	if !setting.Repository.DisableMigrations {
+		registerUpdateMigrationPosterID()
+	}
 }
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index b5764f7fc4..1eaca3c8fb 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -43,6 +43,7 @@ var (
 		DefaultRepoUnits                        []string
 		PrefixArchiveFiles                      bool
 		DisableMirrors                          bool
+		DisableMigrations                       bool
 		DefaultBranch                           string
 		AllowAdoptionOfUnadoptedRepositories    bool
 		AllowDeleteOfUnadoptedRepositories      bool
@@ -148,6 +149,7 @@ var (
 		DefaultRepoUnits:                        []string{},
 		PrefixArchiveFiles:                      true,
 		DisableMirrors:                          false,
+		DisableMigrations:                       false,
 		DefaultBranch:                           "master",
 
 		// Repository editor settings
diff --git a/routers/admin/users.go b/routers/admin/users.go
index 531f81b8b5..118c98ffbe 100644
--- a/routers/admin/users.go
+++ b/routers/admin/users.go
@@ -188,6 +188,7 @@ func EditUser(ctx *context.Context) {
 	ctx.Data["PageIsAdmin"] = true
 	ctx.Data["PageIsAdminUsers"] = true
 	ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
+	ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
 
 	prepareUserInfo(ctx)
 	if ctx.Written() {
@@ -202,6 +203,7 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
 	ctx.Data["Title"] = ctx.Tr("admin.users.edit_account")
 	ctx.Data["PageIsAdmin"] = true
 	ctx.Data["PageIsAdminUsers"] = true
+	ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
 
 	u := prepareUserInfo(ctx)
 	if ctx.Written() {
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index f9cddbb7cd..613823e668 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -119,6 +119,11 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) {
 		return
 	}
 
+    if setting.Repository.DisableMigrations {
+        ctx.Error(http.StatusForbidden, "MigrationsGlobalDisabled", fmt.Errorf("the site administrator has disabled migrations"))
+        return
+    }
+
 	var opts = migrations.MigrateOptions{
 		CloneAddr:      remoteAddr,
 		RepoName:       form.RepoName,
diff --git a/routers/repo/migrate.go b/routers/repo/migrate.go
index 9b10970bf0..2870f98075 100644
--- a/routers/repo/migrate.go
+++ b/routers/repo/migrate.go
@@ -6,6 +6,7 @@
 package repo
 
 import (
+	"fmt"
 	"strings"
 
 	"code.gitea.io/gitea/models"
@@ -25,6 +26,11 @@ const (
 
 // Migrate render migration of repository page
 func Migrate(ctx *context.Context) {
+	if setting.Repository.DisableMigrations {
+		ctx.ServerError("MigratePost", fmt.Errorf("cannot migrate; migrations disabled"))
+		return
+	}
+
 	ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...)
 	serviceType := ctx.QueryInt("service_type")
 	if serviceType == 0 {
@@ -57,6 +63,11 @@ func Migrate(ctx *context.Context) {
 }
 
 func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *auth.MigrateRepoForm) {
+	if setting.Repository.DisableMigrations {
+		ctx.ServerError("MigrateError", fmt.Errorf("migrations disabled"))
+		return
+	}
+
 	switch {
 	case migrations.IsRateLimitError(err):
 		ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form)
@@ -104,6 +115,11 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam
 
 // MigratePost response for migrating from external git repository
 func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
+	if setting.Repository.DisableMigrations {
+		ctx.ServerError("MigratePost", fmt.Errorf("cannot migrate; migrations disabled"))
+		return
+	}
+
 	ctx.Data["Title"] = ctx.Tr("new_migrate")
 	// Plain git should be first
 	ctx.Data["service"] = structs.GitServiceType(form.Service)
diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl
index 042c09954a..4136c67d1d 100644
--- a/templates/admin/user/edit.tmpl
+++ b/templates/admin/user/edit.tmpl
@@ -95,7 +95,7 @@
 						<input name="allow_git_hook" type="checkbox" {{if .User.CanEditGitHook}}checked{{end}} {{if DisableGitHooks}}disabled{{end}}>
 					</div>
 				</div>
-				<div class="inline field">
+				<div class="inline field" {{if or (DisableImportLocal) (.DisableMigrations)}}hidden{{end}}>
 					<div class="ui checkbox">
 						<label><strong>{{.i18n.Tr "admin.users.allow_import_local"}}</strong></label>
 						<input name="allow_import_local" type="checkbox" {{if .User.CanImportLocal}}checked{{end}} {{if DisableImportLocal}}disabled{{end}}>
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index e960f7fe17..8414623a7c 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -89,9 +89,11 @@
 					<a class="item" href="{{AppSubUrl}}/repo/create">
 						<span class="fitted">{{svg "octicon-plus"}}</span> {{.i18n.Tr "new_repo"}}
 					</a>
-					<a class="item" href="{{AppSubUrl}}/repo/migrate">
-						<span class="fitted">{{svg "octicon-repo-push"}}</span> {{.i18n.Tr "new_migrate"}}
-					</a>
+					{{if not .DisableMigrations}}
+						<a class="item" href="{{AppSubUrl}}/repo/migrate">
+							<span class="fitted">{{svg "octicon-repo-push"}}</span> {{.i18n.Tr "new_migrate"}}
+						</a>
+					{{end}}
 					{{if .SignedUser.CanCreateOrganization}}
 					<a class="item" href="{{AppSubUrl}}/org/create">
 						<span class="fitted">{{svg "octicon-organization"}}</span> {{.i18n.Tr "new_org"}}