diff --git a/.gitignore b/.gitignore
index 9825b96ee3d9d..0bca5df1dfded 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@ _testmain.go
 coverage.out
 
 /modules/public/bindata.go
+/modules/templates/bindata.go
 
 *.db
 *.log
diff --git a/cmd/web.go b/cmd/web.go
index 5442850561379..dfb3ac2385461 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -7,7 +7,6 @@ package cmd
 import (
 	"crypto/tls"
 	"fmt"
-	"io/ioutil"
 	"net"
 	"net/http"
 	"net/http/fcgi"
@@ -15,7 +14,6 @@ import (
 	"path"
 	"strings"
 
-	"code.gitea.io/git"
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/auth"
 	"code.gitea.io/gitea/modules/bindata"
@@ -23,7 +21,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/public"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/template"
+	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/routers"
 	"code.gitea.io/gitea/routers/admin"
 	apiv1 "code.gitea.io/gitea/routers/api/v1"
@@ -39,10 +37,7 @@ import (
 	"github.com/go-macaron/i18n"
 	"github.com/go-macaron/session"
 	"github.com/go-macaron/toolbox"
-	"github.com/go-xorm/xorm"
-	version "github.com/mcuadros/go-version"
 	"github.com/urfave/cli"
-	ini "gopkg.in/ini.v1"
 	macaron "gopkg.in/macaron.v1"
 )
 
@@ -74,45 +69,6 @@ type VerChecker struct {
 	Expected   string
 }
 
-// checkVersion checks if binary matches the version of templates files.
-func checkVersion() {
-	// Templates.
-	data, err := ioutil.ReadFile(setting.StaticRootPath + "/templates/.VERSION")
-	if err != nil {
-		log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
-	}
-	tplVer := string(data)
-	if tplVer != setting.AppVer {
-		if version.Compare(tplVer, setting.AppVer, ">") {
-			log.Fatal(4, "Binary version is lower than template file version, did you forget to recompile Gogs?")
-		} else {
-			log.Fatal(4, "Binary version is higher than template file version, did you forget to update template files?")
-		}
-	}
-
-	// Check dependency version.
-	checkers := []VerChecker{
-		{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.5"},
-		{"github.com/go-macaron/binding", binding.Version, "0.3.2"},
-		{"github.com/go-macaron/cache", cache.Version, "0.1.2"},
-		{"github.com/go-macaron/csrf", csrf.Version, "0.1.0"},
-		{"github.com/go-macaron/i18n", i18n.Version, "0.3.0"},
-		{"github.com/go-macaron/session", session.Version, "0.1.6"},
-		{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
-		{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
-		{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
-		{"code.gitea.io/git", git.Version, "0.4.1"},
-	}
-	for _, c := range checkers {
-		if !version.Compare(c.Version(), c.Expected, ">=") {
-			log.Fatal(4, `Dependency outdated!
-Package '%s' current version (%s) is below requirement (%s),
-please use following command to update this package and recompile Gogs:
-go get -u %[1]s`, c.ImportPath, c.Version(), c.Expected)
-		}
-	}
-}
-
 // newMacaron initializes Macaron instance.
 func newMacaron() *macaron.Macaron {
 	m := macaron.New()
@@ -140,15 +96,8 @@ func newMacaron() *macaron.Macaron {
 		},
 	))
 
-	funcMap := template.NewFuncMap()
-	m.Use(macaron.Renderer(macaron.RenderOptions{
-		Directory:         path.Join(setting.StaticRootPath, "templates"),
-		AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
-		Funcs:             funcMap,
-		IndentJSON:        macaron.Env != macaron.PROD,
-	}))
-	models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
-		path.Join(setting.CustomPath, "templates/mail"), funcMap)
+	m.Use(templates.Renderer())
+	models.InitMailRender(templates.Mailer())
 
 	localeNames, err := bindata.AssetDir("conf/locale")
 	if err != nil {
@@ -200,7 +149,6 @@ func runWeb(ctx *cli.Context) error {
 		setting.CustomConf = ctx.String("config")
 	}
 	routers.GlobalInit()
-	checkVersion()
 
 	m := newMacaron()
 
diff --git a/models/git_diff.go b/models/git_diff.go
index b8f9cacec9f66..fe99f61bfea18 100644
--- a/models/git_diff.go
+++ b/models/git_diff.go
@@ -18,10 +18,10 @@ import (
 
 	"code.gitea.io/git"
 	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/highlight"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/template/highlight"
 	"github.com/Unknwon/com"
 	"github.com/sergi/go-diff/diffmatchpatch"
 	"golang.org/x/net/html/charset"
diff --git a/models/mail.go b/models/mail.go
index f89e38e625294..a5e9bd551feb6 100644
--- a/models/mail.go
+++ b/models/mail.go
@@ -5,18 +5,18 @@
 package models
 
 import (
+	"bytes"
 	"fmt"
 	"html/template"
 	"path"
 
-	"gopkg.in/gomail.v2"
-	"gopkg.in/macaron.v1"
-
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/mailer"
 	"code.gitea.io/gitea/modules/markdown"
 	"code.gitea.io/gitea/modules/setting"
+	"gopkg.in/gomail.v2"
+	"gopkg.in/macaron.v1"
 )
 
 const (
@@ -31,27 +31,11 @@ const (
 	mailNotifyCollaborator base.TplName = "notify/collaborator"
 )
 
-type mailRenderInterface interface {
-	HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error)
-}
-
-var mailRender mailRenderInterface
+var templates *template.Template
 
 // InitMailRender initializes the macaron mail renderer
-func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) {
-	opt := &macaron.RenderOptions{
-		Directory:         dir,
-		AppendDirectories: []string{appendDir},
-		Funcs:             funcMap,
-		Extensions:        []string{".tmpl", ".html"},
-	}
-	ts := macaron.NewTemplateSet()
-	ts.Set(macaron.DEFAULT_TPL_SET_NAME, opt)
-
-	mailRender = &macaron.TplRender{
-		TemplateSet: ts,
-		Opt:         opt,
-	}
+func InitMailRender(tmpls *template.Template) {
+	templates = tmpls
 }
 
 // SendTestMail sends a test mail
@@ -67,13 +51,15 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject,
 		"ResetPwdCodeLives": setting.Service.ResetPwdCodeLives / 60,
 		"Code":              code,
 	}
-	body, err := mailRender.HTMLString(string(tpl), data)
-	if err != nil {
-		log.Error(3, "HTMLString: %v", err)
+
+	var content bytes.Buffer
+
+	if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil {
+		log.Error(3, "Template: %v", err)
 		return
 	}
 
-	msg := mailer.NewMessage([]string{u.Email}, subject, body)
+	msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
 	msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
 
 	mailer.SendAsync(msg)
@@ -97,13 +83,15 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
 		"Code":            u.GenerateEmailActivateCode(email.Email),
 		"Email":           email.Email,
 	}
-	body, err := mailRender.HTMLString(string(mailAuthActivateEmail), data)
-	if err != nil {
-		log.Error(3, "HTMLString: %v", err)
+
+	var content bytes.Buffer
+
+	if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
+		log.Error(3, "Template: %v", err)
 		return
 	}
 
-	msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body)
+	msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), content.String())
 	msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
 
 	mailer.SendAsync(msg)
@@ -114,13 +102,15 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) {
 	data := map[string]interface{}{
 		"Username": u.DisplayName(),
 	}
-	body, err := mailRender.HTMLString(string(mailAuthRegisterNotify), data)
-	if err != nil {
-		log.Error(3, "HTMLString: %v", err)
+
+	var content bytes.Buffer
+
+	if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
+		log.Error(3, "Template: %v", err)
 		return
 	}
 
-	msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body)
+	msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), content.String())
 	msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
 
 	mailer.SendAsync(msg)
@@ -136,13 +126,15 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) {
 		"RepoName": repoName,
 		"Link":     repo.HTMLURL(),
 	}
-	body, err := mailRender.HTMLString(string(mailNotifyCollaborator), data)
-	if err != nil {
-		log.Error(3, "HTMLString: %v", err)
+
+	var content bytes.Buffer
+
+	if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
+		log.Error(3, "Template: %v", err)
 		return
 	}
 
-	msg := mailer.NewMessage([]string{u.Email}, subject, body)
+	msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
 	msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
 
 	mailer.SendAsync(msg)
@@ -161,11 +153,14 @@ func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []s
 	body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
 	data := composeTplData(subject, body, issue.HTMLURL())
 	data["Doer"] = doer
-	content, err := mailRender.HTMLString(string(tplName), data)
-	if err != nil {
-		log.Error(3, "HTMLString (%s): %v", tplName, err)
+
+	var content bytes.Buffer
+
+	if err := templates.ExecuteTemplate(&content, string(tplName), data); err != nil {
+		log.Error(3, "Template: %v", err)
 	}
-	msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content)
+
+	msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content.String())
 	msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
 	return msg
 }
diff --git a/modules/template/highlight/highlight.go b/modules/highlight/highlight.go
similarity index 100%
rename from modules/template/highlight/highlight.go
rename to modules/highlight/highlight.go
diff --git a/modules/public/public.go b/modules/public/public.go
index 261ed88874c08..9c9e9d533dadc 100644
--- a/modules/public/public.go
+++ b/modules/public/public.go
@@ -6,6 +6,8 @@ package public
 
 //go:generate go-bindata -tags "bindata" -ignore "\\.go|\\.less" -pkg "public" -o "bindata.go" ../../public/...
 //go:generate go fmt bindata.go
+//go:generate sed -i.bak s/..\/..\/public\/// bindata.go
+//go:generate rm -f bindata.go.bak
 
 // Options represents the available options to configure the macaron handler.
 type Options struct {
diff --git a/modules/public/static.go b/modules/public/static.go
index 8184bc214978b..f68400d329c1e 100644
--- a/modules/public/static.go
+++ b/modules/public/static.go
@@ -22,7 +22,7 @@ func Static(opts *Options) macaron.Handler {
 				AssetDir:   AssetDir,
 				AssetInfo:  AssetInfo,
 				AssetNames: AssetNames,
-				Prefix:     "../../public",
+				Prefix:     "",
 			}),
 		},
 	)
diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go
new file mode 100644
index 0000000000000..c127b69470805
--- /dev/null
+++ b/modules/templates/dynamic.go
@@ -0,0 +1,103 @@
+// +build !bindata
+
+// Copyright 2016 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 templates
+
+import (
+	"html/template"
+	"io/ioutil"
+	"path"
+	"strings"
+
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"github.com/Unknwon/com"
+	"gopkg.in/macaron.v1"
+)
+
+var (
+	templates = template.New("")
+)
+
+// Renderer implements the macaron handler for serving the templates.
+func Renderer() macaron.Handler {
+	return macaron.Renderer(macaron.RenderOptions{
+		Funcs:     NewFuncMap(),
+		Directory: path.Join(setting.StaticRootPath, "templates"),
+		AppendDirectories: []string{
+			path.Join(setting.CustomPath, "templates"),
+		},
+	})
+}
+
+// Mailer provides the templates required for sending notification mails.
+func Mailer() *template.Template {
+	for _, funcs := range NewFuncMap() {
+		templates.Funcs(funcs)
+	}
+
+	staticDir := path.Join(setting.StaticRootPath, "templates", "mail")
+
+	if com.IsDir(staticDir) {
+		files, err := com.StatDir(staticDir)
+
+		if err != nil {
+			log.Warn("Failed to read %s templates dir. %v", staticDir, err)
+		} else {
+			for _, filePath := range files {
+				if !strings.HasSuffix(filePath, ".tmpl") {
+					continue
+				}
+
+				content, err := ioutil.ReadFile(path.Join(staticDir, filePath))
+
+				if err != nil {
+					log.Warn("Failed to read static %s template. %v", filePath, err)
+					continue
+				}
+
+				templates.New(
+					strings.TrimSuffix(
+						filePath,
+						".tmpl",
+					),
+				).Parse(string(content))
+			}
+		}
+	}
+
+	customDir := path.Join(setting.CustomPath, "templates", "mail")
+
+	if com.IsDir(customDir) {
+		files, err := com.StatDir(customDir)
+
+		if err != nil {
+			log.Warn("Failed to read %s templates dir. %v", customDir, err)
+		} else {
+			for _, filePath := range files {
+				if !strings.HasSuffix(filePath, ".tmpl") {
+					continue
+				}
+
+				content, err := ioutil.ReadFile(path.Join(customDir, filePath))
+
+				if err != nil {
+					log.Warn("Failed to read custom %s template. %v", filePath, err)
+					continue
+				}
+
+				templates.New(
+					strings.TrimSuffix(
+						filePath,
+						".tmpl",
+					),
+				).Parse(string(content))
+			}
+		}
+	}
+
+	return templates
+}
diff --git a/modules/template/template.go b/modules/templates/helper.go
similarity index 99%
rename from modules/template/template.go
rename to modules/templates/helper.go
index 65d78c2f6e0f1..c2564553992b0 100644
--- a/modules/template/template.go
+++ b/modules/templates/helper.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package template
+package templates
 
 import (
 	"container/list"
diff --git a/modules/templates/static.go b/modules/templates/static.go
new file mode 100644
index 0000000000000..5c9903cde6579
--- /dev/null
+++ b/modules/templates/static.go
@@ -0,0 +1,109 @@
+// +build bindata
+
+// Copyright 2016 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 templates
+
+import (
+	"html/template"
+	"io/ioutil"
+	"path"
+	"strings"
+
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"github.com/Unknwon/com"
+	"github.com/go-macaron/bindata"
+	"gopkg.in/macaron.v1"
+)
+
+var (
+	templates = template.New("")
+)
+
+// Renderer implements the macaron handler for serving the templates.
+func Renderer() macaron.Handler {
+	return macaron.Renderer(macaron.RenderOptions{
+		Funcs: NewFuncMap(),
+		AppendDirectories: []string{
+			path.Join(setting.CustomPath, "templates"),
+		},
+		TemplateFileSystem: bindata.Templates(
+			bindata.Options{
+				Asset:      Asset,
+				AssetDir:   AssetDir,
+				AssetInfo:  AssetInfo,
+				AssetNames: AssetNames,
+				Prefix:     "",
+			},
+		),
+	})
+}
+
+// Mailer provides the templates required for sending notification mails.
+func Mailer() *template.Template {
+	for _, funcs := range NewFuncMap() {
+		templates.Funcs(funcs)
+	}
+
+	for _, assetPath := range AssetNames() {
+		if !strings.HasPrefix(assetPath, "mail/") {
+			continue
+		}
+
+		if !strings.HasSuffix(assetPath, ".tmpl") {
+			continue
+		}
+
+		content, err := Asset(assetPath)
+
+		if err != nil {
+			log.Warn("Failed to read embedded %s template. %v", assetPath, err)
+			continue
+		}
+
+		templates.New(
+			strings.TrimPrefix(
+				strings.TrimSuffix(
+					assetPath,
+					".tmpl",
+				),
+				"mail/",
+			),
+		).Parse(string(content))
+	}
+
+	customDir := path.Join(setting.CustomPath, "templates", "mail")
+
+	if com.IsDir(customDir) {
+		files, err := com.StatDir(customDir)
+
+		if err != nil {
+			log.Warn("Failed to read %s templates dir. %v", customDir, err)
+		} else {
+			for _, filePath := range files {
+				if !strings.HasSuffix(filePath, ".tmpl") {
+					continue
+				}
+
+				content, err := ioutil.ReadFile(path.Join(customDir, filePath))
+
+				if err != nil {
+					log.Warn("Failed to read custom %s template. %v", filePath, err)
+					continue
+				}
+
+				templates.New(
+					strings.TrimSuffix(
+						filePath,
+						".tmpl",
+					),
+				).Parse(string(content))
+			}
+		}
+	}
+
+	return templates
+}
diff --git a/modules/templates/templates.go b/modules/templates/templates.go
new file mode 100644
index 0000000000000..91c8db522894e
--- /dev/null
+++ b/modules/templates/templates.go
@@ -0,0 +1,10 @@
+// Copyright 2016 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 templates
+
+//go:generate go-bindata -tags "bindata" -ignore "\\.go" -pkg "templates" -o "bindata.go" ../../templates/...
+//go:generate go fmt bindata.go
+//go:generate sed -i.bak s/..\/..\/templates\/// bindata.go
+//go:generate rm -f bindata.go.bak
diff --git a/routers/init.go b/routers/init.go
index b6b9911c80996..3d5235d79d200 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -11,12 +11,12 @@ import (
 	"code.gitea.io/git"
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/cron"
+	"code.gitea.io/gitea/modules/highlight"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/mailer"
 	"code.gitea.io/gitea/modules/markdown"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/ssh"
-	"code.gitea.io/gitea/modules/template/highlight"
 	macaron "gopkg.in/macaron.v1"
 )
 
diff --git a/routers/repo/editor.go b/routers/repo/editor.go
index f037b39b21d36..8d45521a2ee9e 100644
--- a/routers/repo/editor.go
+++ b/routers/repo/editor.go
@@ -18,7 +18,7 @@ import (
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/template"
+	"code.gitea.io/gitea/modules/templates"
 )
 
 const (
@@ -74,7 +74,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
 
 		d, _ := ioutil.ReadAll(dataRc)
 		buf = append(buf, d...)
-		if content, err := template.ToUTF8WithErr(buf); err != nil {
+		if content, err := templates.ToUTF8WithErr(buf); err != nil {
 			if err != nil {
 				log.Error(4, "ToUTF8WithErr: %v", err)
 			}
diff --git a/routers/repo/view.go b/routers/repo/view.go
index 3ef3716f27df9..4417383bdc7ea 100644
--- a/routers/repo/view.go
+++ b/routers/repo/view.go
@@ -16,11 +16,11 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/highlight"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markdown"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/template"
-	"code.gitea.io/gitea/modules/template/highlight"
+	"code.gitea.io/gitea/modules/templates"
 	"github.com/Unknwon/paginater"
 )
 
@@ -164,7 +164,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 		} else {
 			// Building code view blocks with line number on server side.
 			var fileContent string
-			if content, err := template.ToUTF8WithErr(buf); err != nil {
+			if content, err := templates.ToUTF8WithErr(buf); err != nil {
 				if err != nil {
 					log.Error(4, "ToUTF8WithErr: %s", err)
 				}
diff --git a/templates/.VERSION b/templates/.VERSION
deleted file mode 100644
index 927f673c535dc..0000000000000
--- a/templates/.VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.9.99.0915
\ No newline at end of file