diff --git a/apps/apps.json b/apps/apps.json
new file mode 100644
index 000000000..6d72ca95c
--- /dev/null
+++ b/apps/apps.json
@@ -0,0 +1,4 @@
+{
+  "version": "0.0.1",
+  "package": ""
+}
diff --git a/backend/app.yaml b/backend/app.yaml
index d0850568b..9e51cf59e 100644
--- a/backend/app.yaml
+++ b/backend/app.yaml
@@ -3,8 +3,7 @@ system:
   db_type: sqlite
   level: debug
   data_dir:  /opt/1Panel/data
-  resource_dir:  /opt/1Panel/data/resource
-  app_dir: /opt/1Panel/data/apps
+  app_oss: "https://1panel.oss-cn-hangzhou.aliyuncs.com/apps.json"
 
 mysql:
   path: localhost
diff --git a/backend/app/dto/app.go b/backend/app/dto/app.go
index d4e24fb32..2c1c1d314 100644
--- a/backend/app/dto/app.go
+++ b/backend/app/dto/app.go
@@ -24,49 +24,6 @@ type AppDetailDTO struct {
 	Params interface{} `json:"params"`
 }
 
-type AppList struct {
-	Version string      `json:"version"`
-	Tags    []Tag       `json:"tags"`
-	Items   []AppDefine `json:"items"`
-}
-
-type AppDefine struct {
-	Key                string   `json:"key"`
-	Name               string   `json:"name"`
-	Tags               []string `json:"tags"`
-	Versions           []string `json:"versions"`
-	Icon               string   `json:"icon"`
-	Author             string   `json:"author"`
-	Source             string   `json:"source"`
-	ShortDesc          string   `json:"short_desc"`
-	Type               string   `json:"type"`
-	Required           []string `json:"Required"`
-	CrossVersionUpdate bool     `json:"crossVersionUpdate"`
-}
-
-func (define AppDefine) GetRequired() string {
-	by, _ := json.Marshal(define.Required)
-	return string(by)
-}
-
-type Tag struct {
-	Key  string `json:"key"`
-	Name string `json:"name"`
-}
-
-type AppForm struct {
-	FormFields []AppFormFields `json:"form_fields"`
-}
-
-type AppFormFields struct {
-	Type     string `json:"type"`
-	LabelZh  string `json:"label_zh"`
-	LabelEn  string `json:"label_en"`
-	Required string `json:"required"`
-	Default  string `json:"default"`
-	EnvKey   string `json:"env_variable"`
-}
-
 type AppRequest struct {
 	PageInfo
 	Name string   `json:"name"`
@@ -143,7 +100,55 @@ type ContainerExec struct {
 	Auth          AuthParam   `json:"auth"`
 }
 
+type AppOssConfig struct {
+	Version string `json:"version"`
+	Package string `json:"package"`
+}
+
 type AppVersion struct {
 	Version  string `json:"version"`
 	DetailId uint   `json:"detailId"`
 }
+
+type AppList struct {
+	Version string      `json:"version"`
+	Tags    []Tag       `json:"tags"`
+	Items   []AppDefine `json:"items"`
+}
+
+type AppDefine struct {
+	Key                string   `json:"key"`
+	Name               string   `json:"name"`
+	Tags               []string `json:"tags"`
+	Versions           []string `json:"versions"`
+	Icon               string   `json:"icon"`
+	Author             string   `json:"author"`
+	Source             string   `json:"source"`
+	ShortDesc          string   `json:"short_desc"`
+	Type               string   `json:"type"`
+	Required           []string `json:"Required"`
+	CrossVersionUpdate bool     `json:"crossVersionUpdate"`
+}
+
+func (define AppDefine) GetRequired() string {
+	by, _ := json.Marshal(define.Required)
+	return string(by)
+}
+
+type Tag struct {
+	Key  string `json:"key"`
+	Name string `json:"name"`
+}
+
+type AppForm struct {
+	FormFields []AppFormFields `json:"form_fields"`
+}
+
+type AppFormFields struct {
+	Type     string `json:"type"`
+	LabelZh  string `json:"label_zh"`
+	LabelEn  string `json:"label_en"`
+	Required string `json:"required"`
+	Default  string `json:"default"`
+	EnvKey   string `json:"env_variable"`
+}
diff --git a/backend/app/repo/app_install.go b/backend/app/repo/app_install.go
index 3eb97d02c..ad8d13568 100644
--- a/backend/app/repo/app_install.go
+++ b/backend/app/repo/app_install.go
@@ -84,5 +84,5 @@ func (a AppInstallRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOpt
 	if len(opts) == 0 {
 		db = db.Where("1=1")
 	}
-	return db.Updates(&maps).Error
+	return db.Debug().Updates(&maps).Error
 }
diff --git a/backend/app/service/app.go b/backend/app/service/app.go
index 96e64be82..b074decc6 100644
--- a/backend/app/service/app.go
+++ b/backend/app/service/app.go
@@ -444,7 +444,10 @@ func (a AppService) SyncInstalled(installId uint) error {
 }
 
 func (a AppService) SyncAppList() error {
-	//TODO 从 oss 拉取最新列表
+	if err := getAppFromOss(); err != nil {
+		global.LOG.Errorf("get app from oss  error: %s", err.Error())
+		return err
+	}
 
 	appDir := constant.AppResourceDir
 	iconDir := path.Join(appDir, "icons")
diff --git a/backend/app/service/app_utils.go b/backend/app/service/app_utils.go
index 5de087afa..a9812bade 100644
--- a/backend/app/service/app_utils.go
+++ b/backend/app/service/app_utils.go
@@ -7,6 +7,7 @@ import (
 	"github.com/1Panel-dev/1Panel/app/dto"
 	"github.com/1Panel-dev/1Panel/app/model"
 	"github.com/1Panel-dev/1Panel/constant"
+	"github.com/1Panel-dev/1Panel/global"
 	"github.com/1Panel-dev/1Panel/utils/cmd"
 	"github.com/1Panel-dev/1Panel/utils/common"
 	"github.com/1Panel-dev/1Panel/utils/compose"
@@ -14,7 +15,9 @@ import (
 	"github.com/joho/godotenv"
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v3"
+	"io/ioutil"
 	"math"
+	"net/http"
 	"os"
 	"path"
 	"reflect"
@@ -229,13 +232,9 @@ func restoreInstall(install model.AppInstall, backup model.AppInstallBackup) err
 	if !fileOp.Stat(backupFile) {
 		return errors.New(fmt.Sprintf("%s file is not exist", backup.Name))
 	}
-	backDir := installDir + "_back"
-	if fileOp.Stat(backDir) {
-		if err := fileOp.DeleteDir(backDir); err != nil {
-			return err
-		}
-	}
-	if err := fileOp.Rename(installDir, backDir); err != nil {
+
+	backupDir, err := fileOp.Backup(installDir)
+	if err != nil {
 		return err
 	}
 	if err := fileOp.Decompress(backupFile, installKeyDir, files.TarGz); err != nil {
@@ -280,7 +279,7 @@ func restoreInstall(install model.AppInstall, backup model.AppInstallBackup) err
 	}
 
 	install.Param = backup.Param
-	_ = fileOp.DeleteDir(backDir)
+	_ = fileOp.DeleteDir(backupDir)
 	if out, err := compose.Up(install.GetComposePath()); err != nil {
 		return handleErr(install, err, out)
 	}
@@ -456,3 +455,50 @@ func handleErr(install model.AppInstall, err error, out string) error {
 	_ = appInstallRepo.Save(&install)
 	return reErr
 }
+
+func getAppFromOss() error {
+	res, err := http.Get(global.CONF.System.AppOss)
+	if err != nil {
+		return err
+	}
+	appByte, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		return err
+	}
+	var ossConfig dto.AppOssConfig
+	if err := json.Unmarshal(appByte, &ossConfig); err != nil {
+		return err
+	}
+	appDir := constant.AppResourceDir
+	oldListFile := path.Join(appDir, "list.json")
+	content, err := os.ReadFile(oldListFile)
+	if err != nil {
+		return err
+	}
+	list := &dto.AppList{}
+	if err := json.Unmarshal(content, list); err != nil {
+		return err
+	}
+	if list.Version == ossConfig.Version {
+		return nil
+	}
+
+	fileOp := files.NewFileOp()
+	if _, err := fileOp.Backup(appDir); err != nil {
+		return err
+	}
+
+	packageName := path.Base(ossConfig.Package)
+	packagePath := path.Join(constant.ResourceDir, packageName)
+	if err := fileOp.DownloadFile(ossConfig.Package, packagePath); err != nil {
+		return err
+	}
+	if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
+		return err
+	}
+
+	defer func() {
+		_ = fileOp.DeleteFile(packagePath)
+	}()
+	return nil
+}
diff --git a/backend/app/service/file.go b/backend/app/service/file.go
index a40205b2f..3da89be9d 100644
--- a/backend/app/service/file.go
+++ b/backend/app/service/file.go
@@ -126,7 +126,7 @@ func (f FileService) ChangeName(re dto.FileRename) error {
 func (f FileService) Wget(w dto.FileWget) (string, error) {
 	fo := files.NewFileOp()
 	key := "file-wget-" + uuid.NewV4().String()
-	return key, fo.DownloadFile(w.Url, filepath.Join(w.Path, w.Name), key)
+	return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key)
 }
 
 func (f FileService) MvFile(m dto.FileMove) error {
diff --git a/backend/utils/files/file_op.go b/backend/utils/files/file_op.go
index bfaddc382..6a36be426 100644
--- a/backend/utils/files/file_op.go
+++ b/backend/utils/files/file_op.go
@@ -137,7 +137,7 @@ func (w *WriteCounter) SaveProcess() {
 
 }
 
-func (f FileOp) DownloadFile(url, dst, key string) error {
+func (f FileOp) DownloadFileWithProcess(url, dst, key string) error {
 	resp, err := http.Get(url)
 	if err != nil {
 		global.LOG.Errorf("get download file [%s] error, err %s", dst, err.Error())
@@ -167,6 +167,26 @@ func (f FileOp) DownloadFile(url, dst, key string) error {
 	return nil
 }
 
+func (f FileOp) DownloadFile(url, dst string) error {
+	resp, err := http.Get(url)
+	if err != nil {
+		global.LOG.Errorf("get download file [%s] error, err %s", dst, err.Error())
+	}
+
+	out, err := os.Create(dst)
+	if err != nil {
+		global.LOG.Errorf("create download file [%s] error, err %s", dst, err.Error())
+	}
+
+	if _, err = io.Copy(out, resp.Body); err != nil {
+		global.LOG.Errorf("save download file [%s] error, err %s", dst, err.Error())
+	}
+	out.Close()
+	resp.Body.Close()
+
+	return nil
+}
+
 func (f FileOp) Cut(oldPaths []string, dst string) error {
 	for _, p := range oldPaths {
 		base := filepath.Base(p)
@@ -396,3 +416,19 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
 	}
 	return format.Extract(context.Background(), input, nil, handler)
 }
+
+func (f FileOp) Backup(srcFile string) (string, error) {
+	backupPath := srcFile + "_bak"
+	info, _ := f.Fs.Stat(backupPath)
+	if info != nil {
+		if info.IsDir() {
+			f.DeleteDir(backupPath)
+		} else {
+			f.DeleteFile(backupPath)
+		}
+	}
+	if err := f.Rename(srcFile, backupPath); err != nil {
+		return backupPath, err
+	}
+	return backupPath, nil
+}