diff --git a/config/config_test.go b/config/config_test.go
index 24a4d311c..0e23d1936 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -5,6 +5,7 @@ import (
 	"io/ioutil"
 	"os"
 	"reflect"
+	"strconv"
 	"syscall"
 	"testing"
 	"time"
@@ -1121,6 +1122,34 @@ func TestParse(t *testing.T) {
 			},
 			false,
 		},
+		{
+			"template_uid_backward_compat",
+			`template {
+				uid = 1000
+			}`,
+			&Config{
+				Templates: &TemplateConfigs{
+					&TemplateConfig{
+						Uid: Int(1000),
+					},
+				},
+			},
+			false,
+		},
+		{
+			"template_gid_backward_compat",
+			`template {
+				gid = 1000
+			}`,
+			&Config{
+				Templates: &TemplateConfigs{
+					&TemplateConfig{
+						Gid: Int(1000),
+					},
+				},
+			},
+			false,
+		},
 		{
 			"template_uid_gid_default",
 			`template {
@@ -1130,6 +1159,8 @@ func TestParse(t *testing.T) {
 					&TemplateConfig{
 						User:  nil,
 						Group: nil,
+						Uid:   nil,
+						Gid:   nil,
 					},
 				},
 			},
@@ -1764,6 +1795,68 @@ func TestFinalize(t *testing.T) {
 				},
 			},
 		},
+		{
+			"uid_backward_compat",
+			func(act, exp *Config) (bool, error) {
+				for i, tA := range *act.Templates {
+					for j, tE := range *exp.Templates {
+						if i != j {
+							continue
+						}
+						var userInt, _ = strconv.Atoi(*tE.User)
+						if userInt != *tA.Uid {
+							return false, fmt.Errorf("\nexp: %#v\nact: %#v", *tE.User, *tA.User)
+						}
+					}
+				}
+				return true, nil
+			},
+			&Config{
+				Templates: &TemplateConfigs{
+					&TemplateConfig{
+						Uid: Int(1000),
+					},
+				},
+			},
+			&Config{
+				Templates: &TemplateConfigs{
+					&TemplateConfig{
+						User: String("1000"),
+					},
+				},
+			},
+		},
+		{
+			"gid_backward_compat",
+			func(act, exp *Config) (bool, error) {
+				for i, tA := range *act.Templates {
+					for j, tE := range *exp.Templates {
+						if i != j {
+							continue
+						}
+						var groupInt, _ = strconv.Atoi(*tE.Group)
+						if groupInt != *tA.Gid {
+							return false, fmt.Errorf("\nexp: %#v\nact: %#v", *tE.Group, *tA.Group)
+						}
+					}
+				}
+				return true, nil
+			},
+			&Config{
+				Templates: &TemplateConfigs{
+					&TemplateConfig{
+						Gid: Int(1000),
+					},
+				},
+			},
+			&Config{
+				Templates: &TemplateConfigs{
+					&TemplateConfig{
+						Group: String("1000"),
+					},
+				},
+			},
+		},
 	}
 
 	for i, tc := range cases {
diff --git a/config/template.go b/config/template.go
index a004cf1e8..8adc874c7 100644
--- a/config/template.go
+++ b/config/template.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -75,6 +76,9 @@ type TemplateConfig struct {
 	// with a warning
 	User *string `mapstructure:"user"`
 
+	// Uid is equivalent to User when it's a valid int and it's defined for backward compatibility with v0.28.0
+	Uid *int `mapstructure:"uid"`
+
 	// Group is the group name or gid that will be set when creating the file on disk.
 	// Useful when simply setting Perms is not enough.
 	//
@@ -82,6 +86,9 @@ type TemplateConfig struct {
 	// with a warning
 	Group *string `mapstructure:"group"`
 
+	// Gid is equivalent to Group when it's a valid int and it's defined for backward compatibility with v0.28.0
+	Gid *int `mapstructure:"gid"`
+
 	// Source is the path on disk to the template contents to evaluate. Either
 	// this or Contents should be specified, but not both.
 	Source *string `mapstructure:"source"`
@@ -295,6 +302,18 @@ func (c *TemplateConfig) Finalize() {
 		c.ErrFatal = Bool(true)
 	}
 
+	// Backwards compatibility for uid
+	if c.User == nil && c.Uid != nil {
+		var uStr = strconv.Itoa(*c.Uid)
+		c.User = &uStr
+	}
+
+	// Backwards compatibility for gid
+	if c.Group == nil && c.Gid != nil {
+		var gStr = strconv.Itoa(*c.Gid)
+		c.Group = &gStr
+	}
+
 	if c.Exec == nil {
 		c.Exec = DefaultExecConfig()
 	}