diff --git a/models/models.go b/models/models.go
index 6aaa26d627..f12a4e8b30 100644
--- a/models/models.go
+++ b/models/models.go
@@ -145,7 +145,16 @@ func getEngine() (*xorm.Engine, error) {
 		return nil, err
 	}
 
-	engine, err := xorm.NewEngine(setting.Database.Type, connStr)
+	var engine *xorm.Engine
+
+	if setting.Database.UsePostgreSQL && len(setting.Database.Schema) > 0 {
+		// OK whilst we sort out our schema issues - create a schema aware postgres
+		registerPostgresSchemaDriver()
+		engine, err = xorm.NewEngine("postgresschema", connStr)
+	} else {
+		engine, err = xorm.NewEngine(setting.Database.Type, connStr)
+	}
+
 	if err != nil {
 		return nil, err
 	}
@@ -155,16 +164,6 @@ func getEngine() (*xorm.Engine, error) {
 		engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
 	}
 	engine.SetSchema(setting.Database.Schema)
-	if setting.Database.UsePostgreSQL && len(setting.Database.Schema) > 0 {
-		// Add the schema to the search path
-		if _, err := engine.Exec(`SELECT set_config(
-			'search_path',
-			? || ',' || current_setting('search_path'),
-			false)`,
-			setting.Database.Schema); err != nil {
-			return nil, err
-		}
-	}
 	return engine, nil
 }
 
diff --git a/models/sql_postgres_with_schema.go b/models/sql_postgres_with_schema.go
new file mode 100644
index 0000000000..0c8893ea27
--- /dev/null
+++ b/models/sql_postgres_with_schema.go
@@ -0,0 +1,75 @@
+// Copyright 2020 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 models
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"sync"
+
+	"code.gitea.io/gitea/modules/setting"
+
+	"github.com/lib/pq"
+	"xorm.io/xorm/dialects"
+)
+
+var registerOnce sync.Once
+
+func registerPostgresSchemaDriver() {
+	registerOnce.Do(func() {
+		sql.Register("postgresschema", &postgresSchemaDriver{})
+		dialects.RegisterDriver("postgresschema", dialects.QueryDriver("postgres"))
+	})
+}
+
+type postgresSchemaDriver struct {
+	pq.Driver
+}
+
+// Open opens a new connection to the database. name is a connection string.
+// This function opens the postgres connection in the default manner but immediately
+// runs set_config to set the search_path appropriately
+func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
+	conn, err := d.Driver.Open(name)
+	if err != nil {
+		return conn, err
+	}
+	schemaValue, _ := driver.String.ConvertValue(setting.Database.Schema)
+
+	// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
+	// and in any case pq does not implement it
+	if execer, ok := conn.(driver.Execer); ok { //nolint
+		_, err := execer.Exec(`SELECT set_config(
+			'search_path',
+			$1 || ',' || current_setting('search_path'),
+			false)`, []driver.Value{schemaValue}) //nolint
+		if err != nil {
+			_ = conn.Close()
+			return nil, err
+		}
+		return conn, nil
+	}
+
+	stmt, err := conn.Prepare(`SELECT set_config(
+		'search_path',
+		$1 || ',' || current_setting('search_path'),
+		false)`)
+	if err != nil {
+		_ = conn.Close()
+		return nil, err
+	}
+	defer stmt.Close()
+
+	// driver.String.ConvertValue will never return err for string
+
+	// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
+	_, err = stmt.Exec([]driver.Value{schemaValue}) //nolint
+	if err != nil {
+		_ = conn.Close()
+		return nil, err
+	}
+
+	return conn, nil
+}