diff --git a/internal/app/scheduler.go b/internal/app/scheduler.go
index 1b16ac32..c5e93c9f 100644
--- a/internal/app/scheduler.go
+++ b/internal/app/scheduler.go
@@ -3,6 +3,7 @@ package app
 import (
 	"sync"
 	"time"
+	_ "time/tzdata"
 
 	"github.com/pocketbase/pocketbase/tools/cron"
 )
diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index 4e03ad2e..7d62ac41 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -56,6 +56,7 @@ import (
 	pTencentCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-waf"
 	pUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn"
 	pUCloudUS3 "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-us3"
+	pUpyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/upyun-cdn"
 	pVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn"
 	pVolcEngineCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-clb"
 	pVolcEngineDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-dcdn"
@@ -225,6 +226,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
 					AccessKeyId:     access.AccessKeyId,
 					AccessKeySecret: access.AccessKeySecret,
 					Region:          maputil.GetString(options.ProviderDeployConfig, "region"),
+					ServiceVersion:  maputil.GetOrDefaultString(options.ProviderDeployConfig, "serviceVersion", "3.0"),
 					InstanceId:      maputil.GetString(options.ProviderDeployConfig, "instanceId"),
 					Domain:          maputil.GetString(options.ProviderDeployConfig, "domain"),
 				})
@@ -775,6 +777,27 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
 			}
 		}
 
+	case domain.DeployProviderTypeUpyunCDN:
+		{
+			access := domain.AccessConfigForUpyun{}
+			if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
+				return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+			}
+
+			switch options.Provider {
+			case domain.DeployProviderTypeUpyunCDN:
+				deployer, err := pUpyunCDN.NewDeployer(&pUpyunCDN.DeployerConfig{
+					Username: access.Username,
+					Password: access.Password,
+					Domain:   maputil.GetString(options.ProviderDeployConfig, "domain"),
+				})
+				return deployer, err
+
+			default:
+				break
+			}
+		}
+
 	case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineCLB, domain.DeployProviderTypeVolcEngineDCDN, domain.DeployProviderTypeVolcEngineImageX, domain.DeployProviderTypeVolcEngineLive, domain.DeployProviderTypeVolcEngineTOS:
 		{
 			access := domain.AccessConfigForVolcEngine{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index fc6a7eb1..963d4083 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -201,6 +201,11 @@ type AccessConfigForUCloud struct {
 	ProjectId  string `json:"projectId,omitempty"`
 }
 
+type AccessConfigForUpyun struct {
+	Username string `json:"username"`
+	Password string `json:"password"`
+}
+
 type AccessConfigForVolcEngine struct {
 	AccessKeyId     string `json:"accessKeyId"`
 	SecretAccessKey string `json:"secretAccessKey"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index 78c79d4a..cbc034fa 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -28,6 +28,7 @@ const (
 	AccessProviderTypeCUCCCloud    = AccessProviderType("cucccloud") // 天翼云(预留)
 	AccessProviderTypeDNSLA        = AccessProviderType("dnsla")
 	AccessProviderTypeDogeCloud    = AccessProviderType("dogecloud")
+	AccessProviderTypeDynv6        = AccessProviderType("dynv6") // dynv6(预留)
 	AccessProviderTypeEdgio        = AccessProviderType("edgio")
 	AccessProviderTypeFastly       = AccessProviderType("fastly") // Fastly(预留)
 	AccessProviderTypeGname        = AccessProviderType("gname")
@@ -50,6 +51,7 @@ const (
 	AccessProviderTypeSSH          = AccessProviderType("ssh")
 	AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
 	AccessProviderTypeUCloud       = AccessProviderType("ucloud")
+	AccessProviderTypeUpyun        = AccessProviderType("upyun")
 	AccessProviderTypeVolcEngine   = AccessProviderType("volcengine")
 	AccessProviderTypeWebhook      = AccessProviderType("webhook")
 	AccessProviderTypeWestcn       = AccessProviderType("westcn")
@@ -158,6 +160,7 @@ const (
 	DeployProviderTypeTencentCloudWAF       = DeployProviderType("tencentcloud-waf")
 	DeployProviderTypeUCloudUCDN            = DeployProviderType("ucloud-ucdn")
 	DeployProviderTypeUCloudUS3             = DeployProviderType("ucloud-us3")
+	DeployProviderTypeUpyunCDN              = DeployProviderType("upyun-cdn")
 	DeployProviderTypeVolcEngineCDN         = DeployProviderType("volcengine-cdn")
 	DeployProviderTypeVolcEngineCLB         = DeployProviderType("volcengine-clb")
 	DeployProviderTypeVolcEngineDCDN        = DeployProviderType("volcengine-dcdn")
diff --git a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go
index 02dac427..e8166afd 100644
--- a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go
+++ b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go
@@ -87,18 +87,18 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
 
 	// 判断域名是否已启用 HTTPS。如果已启用,修改域名证书;否则,启用 HTTPS
 	// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
-	if getDomainInfoResp.Https != nil && getDomainInfoResp.Https.CertID != "" {
-		modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(context.TODO(), domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
-		d.logger.Debug("sdk request 'cdn.ModifyDomainHttpsConf'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", modifyDomainHttpsConfResp))
-		if err != nil {
-			return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'")
-		}
-	} else {
+	if getDomainInfoResp.Https == nil || getDomainInfoResp.Https.CertID == "" {
 		enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(context.TODO(), domain, upres.CertId, true, true)
 		d.logger.Debug("sdk request 'cdn.EnableDomainHttps'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", enableDomainHttpsResp))
 		if err != nil {
 			return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.EnableDomainHttps'")
 		}
+	} else if getDomainInfoResp.Https.CertID != upres.CertId {
+		modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(context.TODO(), domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
+		d.logger.Debug("sdk request 'cdn.ModifyDomainHttpsConf'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", modifyDomainHttpsConfResp))
+		if err != nil {
+			return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'")
+		}
 	}
 
 	return &deployer.DeployResult{}, nil
diff --git a/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go
new file mode 100644
index 00000000..84d6cafb
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go
@@ -0,0 +1,129 @@
+package upyuncdn
+
+import (
+	"context"
+	"errors"
+	"log/slog"
+
+	xerrors "github.com/pkg/errors"
+	"golang.org/x/exp/slices"
+
+	"github.com/usual2970/certimate/internal/pkg/core/deployer"
+	"github.com/usual2970/certimate/internal/pkg/core/uploader"
+	uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/upyun-ssl"
+	upyunsdk "github.com/usual2970/certimate/internal/pkg/vendors/upyun-sdk/console"
+)
+
+type DeployerConfig struct {
+	// 又拍云账号用户名。
+	Username string `json:"username"`
+	// 又拍云账号密码。
+	Password string `json:"password"`
+	// 加速域名(支持泛域名)。
+	Domain string `json:"domain"`
+}
+
+type DeployerProvider struct {
+	config      *DeployerConfig
+	logger      *slog.Logger
+	sdkClient   *upyunsdk.Client
+	sslUploader uploader.Uploader
+}
+
+var _ deployer.Deployer = (*DeployerProvider)(nil)
+
+func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
+	if config == nil {
+		panic("config is nil")
+	}
+
+	client, err := createSdkClient(config.Username, config.Password)
+	if err != nil {
+		return nil, xerrors.Wrap(err, "failed to create sdk client")
+	}
+
+	uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
+		Username: config.Username,
+		Password: config.Password,
+	})
+	if err != nil {
+		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
+	}
+
+	return &DeployerProvider{
+		config:      config,
+		logger:      slog.Default(),
+		sdkClient:   client,
+		sslUploader: uploader,
+	}, nil
+}
+
+func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
+	if logger == nil {
+		d.logger = slog.Default()
+	} else {
+		d.logger = logger
+	}
+	d.sslUploader.WithLogger(logger)
+	return d
+}
+
+func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
+	// 上传证书到 SSL
+	upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
+	if err != nil {
+		return nil, xerrors.Wrap(err, "failed to upload certificate file")
+	} else {
+		d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
+	}
+
+	// 获取域名证书配置
+	getHttpsServiceManagerResp, err := d.sdkClient.GetHttpsServiceManager(d.config.Domain)
+	d.logger.Debug("sdk request 'console.GetHttpsServiceManager'", slog.String("request.domain", d.config.Domain), slog.Any("response", getHttpsServiceManagerResp))
+	if err != nil {
+		return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.GetHttpsServiceManager'")
+	}
+
+	// 判断域名是否已启用 HTTPS。如果已启用,迁移域名证书;否则,设置新证书
+	lastCertIndex := slices.IndexFunc(getHttpsServiceManagerResp.Data.Domains, func(item upyunsdk.HttpsServiceManagerDomain) bool {
+		return item.Https
+	})
+	if lastCertIndex == -1 {
+		updateHttpsCertificateManagerReq := &upyunsdk.UpdateHttpsCertificateManagerRequest{
+			CertificateId: upres.CertId,
+			Domain:        d.config.Domain,
+			Https:         true,
+			ForceHttps:    true,
+		}
+		updateHttpsCertificateManagerResp, err := d.sdkClient.UpdateHttpsCertificateManager(updateHttpsCertificateManagerReq)
+		d.logger.Debug("sdk request 'console.EnableDomainHttps'", slog.Any("request", updateHttpsCertificateManagerReq), slog.Any("response", updateHttpsCertificateManagerResp))
+		if err != nil {
+			return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.UpdateHttpsCertificateManager'")
+		}
+	} else if getHttpsServiceManagerResp.Data.Domains[lastCertIndex].CertificateId != upres.CertId {
+		migrateHttpsDomainReq := &upyunsdk.MigrateHttpsDomainRequest{
+			CertificateId: upres.CertId,
+			Domain:        d.config.Domain,
+		}
+		migrateHttpsDomainResp, err := d.sdkClient.MigrateHttpsDomain(migrateHttpsDomainReq)
+		d.logger.Debug("sdk request 'console.MigrateHttpsDomain'", slog.Any("request", migrateHttpsDomainReq), slog.Any("response", migrateHttpsDomainResp))
+		if err != nil {
+			return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.MigrateHttpsDomain'")
+		}
+	}
+
+	return &deployer.DeployResult{}, nil
+}
+
+func createSdkClient(username, password string) (*upyunsdk.Client, error) {
+	if username == "" {
+		return nil, errors.New("invalid upyun username")
+	}
+
+	if password == "" {
+		return nil, errors.New("invalid upyun password")
+	}
+
+	client := upyunsdk.NewClient(username, password)
+	return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go
new file mode 100644
index 00000000..8a7b4485
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go
@@ -0,0 +1,75 @@
+package upyuncdn_test
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"os"
+	"strings"
+	"testing"
+
+	provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/upyun-cdn"
+)
+
+var (
+	fInputCertPath string
+	fInputKeyPath  string
+	fUsername      string
+	fPassword      string
+	fDomain        string
+)
+
+func init() {
+	argsPrefix := "CERTIMATE_DEPLOYER_UPYUNCDN_"
+
+	flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+	flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+	flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
+	flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
+	flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
+}
+
+/*
+Shell command to run this test:
+
+	go test -v ./upyun_cdn_test.go -args \
+	--CERTIMATE_DEPLOYER_UPYUNCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+	--CERTIMATE_DEPLOYER_UPYUNCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
+	--CERTIMATE_DEPLOYER_UPYUNCDN_USERNAME="your-username" \
+	--CERTIMATE_DEPLOYER_UPYUNCDN_PASSWORD="your-password" \
+	--CERTIMATE_DEPLOYER_UPYUNCDN_DOMAIN="example.com" \
+*/
+func TestDeploy(t *testing.T) {
+	flag.Parse()
+
+	t.Run("Deploy", func(t *testing.T) {
+		t.Log(strings.Join([]string{
+			"args:",
+			fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
+			fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
+			fmt.Sprintf("USERNAME: %v", fUsername),
+			fmt.Sprintf("PASSWORD: %v", fPassword),
+			fmt.Sprintf("DOMAIN: %v", fDomain),
+		}, "\n"))
+
+		deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+			Username: fUsername,
+			Password: fPassword,
+			Domain:   fDomain,
+		})
+		if err != nil {
+			t.Errorf("err: %+v", err)
+			return
+		}
+
+		fInputCertData, _ := os.ReadFile(fInputCertPath)
+		fInputKeyData, _ := os.ReadFile(fInputKeyPath)
+		res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
+		if err != nil {
+			t.Errorf("err: %+v", err)
+			return
+		}
+
+		t.Logf("ok: %v", res)
+	})
+}
diff --git a/migrations/1742392800_upgrade.go b/migrations/1742392800_upgrade.go
new file mode 100644
index 00000000..a06bdc75
--- /dev/null
+++ b/migrations/1742392800_upgrade.go
@@ -0,0 +1,81 @@
+package migrations
+
+import (
+	"github.com/pocketbase/pocketbase/core"
+	m "github.com/pocketbase/pocketbase/migrations"
+)
+
+func init() {
+	m.Register(func(app core.App) error {
+		collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
+		if err != nil {
+			return err
+		}
+
+		// update field
+		if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{
+			"hidden": false,
+			"id": "hwy7m03o",
+			"maxSelect": 1,
+			"name": "provider",
+			"presentable": false,
+			"required": false,
+			"system": false,
+			"type": "select",
+			"values": [
+				"1panel",
+				"acmehttpreq",
+				"akamai",
+				"aliyun",
+				"aws",
+				"azure",
+				"baiducloud",
+				"baishan",
+				"baotapanel",
+				"byteplus",
+				"cachefly",
+				"cdnfly",
+				"cloudflare",
+				"cloudns",
+				"cmcccloud",
+				"ctcccloud",
+				"cucccloud",
+				"dnsla",
+				"dogecloud",
+				"dynv6",
+				"edgio",
+				"fastly",
+				"gname",
+				"gcore",
+				"godaddy",
+				"goedge",
+				"huaweicloud",
+				"jdcloud",
+				"k8s",
+				"local",
+				"namecheap",
+				"namedotcom",
+				"namesilo",
+				"ns1",
+				"powerdns",
+				"qiniu",
+				"qingcloud",
+				"rainyun",
+				"safeline",
+				"ssh",
+				"tencentcloud",
+				"ucloud",
+				"upyun",
+				"volcengine",
+				"webhook",
+				"westcn"
+			]
+		}`)); err != nil {
+			return err
+		}
+
+		return app.Save(collection)
+	}, func(app core.App) error {
+		return nil
+	})
+}
diff --git a/ui/public/imgs/providers/upyun.svg b/ui/public/imgs/providers/upyun.svg
new file mode 100644
index 00000000..cc13793d
--- /dev/null
+++ b/ui/public/imgs/providers/upyun.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M819.733081 102.739233a479.288216 479.288216 0 0 0-59.60814-38.284903 33.923332 33.923332 0 0 0-43.131093 8.723143L589.054425 242.309513l-7.269285 13.084713a70.269759 70.269759 0 0 1-59.123521 27.623285h-25.20019a228.255561 228.255561 0 0 0-213.71699 239.886417 25.200189 25.200189 0 0 0 14.538571 21.807856 60.092759 60.092759 0 1 1-82.385234 71.723616v-3.876952a60.577378 60.577378 0 0 1 11.630856-48.461903 36.831046 36.831046 0 0 0 7.753905-26.654046 279.140558 279.140558 0 0 1 276.717463-303.37151 51.854236 51.854236 0 0 0 41.192617-20.838618l117.762423-156.531945a21.323237 21.323237 0 0 0-11.630856-33.923332A511.75769 511.75769 0 0 0 263.39044 959.54567a33.923332 33.923332 0 0 0 43.615712-9.207762L436.399432 780.721249l8.723143-12.115475a70.754378 70.754378 0 0 1 59.123521-27.623285 218.56318 218.56318 0 0 0 25.200189 0 230.194037 230.194037 0 0 0 168.162802-90.623757 225.832466 225.832466 0 0 0 45.554188-149.26266 25.200189 25.200189 0 0 0-14.538571-21.807856 60.092759 60.092759 0 0 1-35.861808-43.131093 60.092759 60.092759 0 0 1 117.277804-27.138666 14.053952 14.053952 0 0 0 0 4.361571 61.061997 61.061997 0 0 1-11.630856 48.461903 36.831046 36.831046 0 0 0-7.753905 26.654046 279.140558 279.140558 0 0 1-276.717463 303.37151 51.854236 51.854236 0 0 0-41.192617 20.838618L355.952674 969.23805a21.807856 21.807856 0 0 0 11.146238 33.923332 512.24231 512.24231 0 0 0 452.634169-900.422149z" fill="#009FFF"></path></svg>
\ No newline at end of file
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index 7f2143ac..caf5c605 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -44,6 +44,7 @@ import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig";
 import AccessFormSSHConfig from "./AccessFormSSHConfig";
 import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig";
 import AccessFormUCloudConfig from "./AccessFormUCloudConfig";
+import AccessFormUpyunConfig from "./AccessFormUpyunConfig";
 import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig";
 import AccessFormWebhookConfig from "./AccessFormWebhookConfig";
 import AccessFormWestcnConfig from "./AccessFormWestcnConfig";
@@ -170,6 +171,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
         return <AccessFormTencentCloudConfig {...nestedFormProps} />;
       case ACCESS_PROVIDERS.UCLOUD:
         return <AccessFormUCloudConfig {...nestedFormProps} />;
+      case ACCESS_PROVIDERS.UPYUN:
+        return <AccessFormUpyunConfig {...nestedFormProps} />;
       case ACCESS_PROVIDERS.VOLCENGINE:
         return <AccessFormVolcEngineConfig {...nestedFormProps} />;
       case ACCESS_PROVIDERS.WEBHOOK:
diff --git a/ui/src/components/access/AccessFormUpyunConfig.tsx b/ui/src/components/access/AccessFormUpyunConfig.tsx
new file mode 100644
index 00000000..8cc06d97
--- /dev/null
+++ b/ui/src/components/access/AccessFormUpyunConfig.tsx
@@ -0,0 +1,76 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForUpyun } from "@/domain/access";
+
+type AccessFormUpyunConfigFieldValues = Nullish<AccessConfigForUpyun>;
+
+export type AccessFormUpyunConfigProps = {
+  form: FormInstance;
+  formName: string;
+  disabled?: boolean;
+  initialValues?: AccessFormUpyunConfigFieldValues;
+  onValuesChange?: (values: AccessFormUpyunConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormUpyunConfigFieldValues => {
+  return {
+    username: "",
+    password: "",
+  };
+};
+
+const AccessFormUpyunConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormUpyunConfigProps) => {
+  const { t } = useTranslation();
+
+  const formSchema = z.object({
+    username: z
+      .string()
+      .trim()
+      .min(1, t("access.form.upyun_username.placeholder"))
+      .max(64, t("common.errmsg.string_max", { max: 64 })),
+    password: z
+      .string()
+      .min(1, t("access.form.upyun_password.placeholder"))
+      .max(64, t("common.errmsg.string_max", { max: 64 }))
+      .trim(),
+  });
+  const formRule = createSchemaFieldRule(formSchema);
+
+  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
+    onValuesChange?.(values);
+  };
+
+  return (
+    <Form
+      form={formInst}
+      disabled={disabled}
+      initialValues={initialValues ?? initFormModel()}
+      layout="vertical"
+      name={formName}
+      onValuesChange={handleFormChange}
+    >
+      <Form.Item
+        name="username"
+        label={t("access.form.upyun_username.label")}
+        rules={[formRule]}
+        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.upyun_username.tooltip") }}></span>}
+      >
+        <Input autoComplete="new-password" placeholder={t("access.form.upyun_username.placeholder")} />
+      </Form.Item>
+
+      <Form.Item
+        name="password"
+        label={t("access.form.upyun_password.label")}
+        rules={[formRule]}
+        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.upyun_password.tooltip") }}></span>}
+      >
+        <Input.Password autoComplete="new-password" placeholder={t("access.form.upyun_password.placeholder")} />
+      </Form.Item>
+    </Form>
+  );
+};
+
+export default AccessFormUpyunConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx
new file mode 100644
index 00000000..e09f5266
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx
@@ -0,0 +1,59 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { validDomainName } from "@/utils/validators";
+
+type DeployNodeConfigFormUpyunCDNConfigFieldValues = Nullish<{
+  domain: string;
+}>;
+
+export type DeployNodeConfigFormUpyunCDNConfigProps = {
+  form: FormInstance;
+  formName: string;
+  disabled?: boolean;
+  initialValues?: DeployNodeConfigFormUpyunCDNConfigFieldValues;
+  onValuesChange?: (values: DeployNodeConfigFormUpyunCDNConfigFieldValues) => void;
+};
+
+const initFormModel = (): DeployNodeConfigFormUpyunCDNConfigFieldValues => {
+  return {};
+};
+
+const DeployNodeConfigFormUpyunCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormUpyunCDNConfigProps) => {
+  const { t } = useTranslation();
+
+  const formSchema = z.object({
+    domain: z
+      .string({ message: t("workflow_node.deploy.form.upyun_cdn_domain.placeholder") })
+      .refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
+  });
+  const formRule = createSchemaFieldRule(formSchema);
+
+  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
+    onValuesChange?.(values);
+  };
+
+  return (
+    <Form
+      form={formInst}
+      disabled={disabled}
+      initialValues={initialValues ?? initFormModel()}
+      layout="vertical"
+      name={formName}
+      onValuesChange={handleFormChange}
+    >
+      <Form.Item
+        name="domain"
+        label={t("workflow_node.deploy.form.upyun_cdn_domain.label")}
+        rules={[formRule]}
+        tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.upyun_cdn_domain.tooltip") }}></span>}
+      >
+        <Input placeholder={t("workflow_node.deploy.form.upyun_cdn_domain.placeholder")} />
+      </Form.Item>
+    </Form>
+  );
+};
+
+export default DeployNodeConfigFormUpyunCDNConfig;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 1b5adf45..59a41cc6 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -40,6 +40,7 @@ export interface AccessModel extends BaseModel {
       | AccessConfigForSSH
       | AccessConfigForTencentCloud
       | AccessConfigForUCloud
+      | AccessConfigForUpyun
       | AccessConfigForVolcEngine
       | AccessConfigForWebhook
       | AccessConfigForWestcn
@@ -224,6 +225,11 @@ export type AccessConfigForUCloud = {
   projectId?: string;
 };
 
+export type AccessConfigForUpyun = {
+  username: string;
+  password: string;
+};
+
 export type AccessConfigForVolcEngine = {
   accessKeyId: string;
   secretAccessKey: string;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index f3d6deb3..998ec718 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -39,6 +39,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
   SSH: "ssh",
   TENCENTCLOUD: "tencentcloud",
   UCLOUD: "ucloud",
+  UPYUN: "upyun",
   VOLCENGINE: "volcengine",
   WEBHOOK: "webhook",
   WESTCN: "westcn",
@@ -80,6 +81,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
     [ACCESS_PROVIDERS.GCORE, "provider.gcore", "/imgs/providers/gcore.png", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]],
 
     [ACCESS_PROVIDERS.QINIU, "provider.qiniu", "/imgs/providers/qiniu.svg", [ACCESS_USAGES.DEPLOY]],
+    [ACCESS_PROVIDERS.UPYUN, "provider.upyun", "/imgs/providers/upyun.svg", [ACCESS_USAGES.DEPLOY]],
     [ACCESS_PROVIDERS.BAISHAN, "provider.baishan", "/imgs/providers/baishan.png", [ACCESS_USAGES.DEPLOY]],
     [ACCESS_PROVIDERS.DOGECLOUD, "provider.dogecloud", "/imgs/providers/dogecloud.png", [ACCESS_USAGES.DEPLOY]],
     [ACCESS_PROVIDERS.BYTEPLUS, "provider.byteplus", "/imgs/providers/byteplus.svg", [ACCESS_USAGES.DEPLOY]],
@@ -263,6 +265,7 @@ export const DEPLOY_PROVIDERS = Object.freeze({
   TENCENTCLOUD_WAF: `${ACCESS_PROVIDERS.TENCENTCLOUD}-waf`,
   UCLOUD_UCDN: `${ACCESS_PROVIDERS.UCLOUD}-ucdn`,
   UCLOUD_US3: `${ACCESS_PROVIDERS.UCLOUD}-us3`,
+  UPYUN_CDN: `${ACCESS_PROVIDERS.UPYUN}-cdn`,
   VOLCENGINE_CDN: `${ACCESS_PROVIDERS.VOLCENGINE}-cdn`,
   VOLCENGINE_CLB: `${ACCESS_PROVIDERS.VOLCENGINE}-clb`,
   VOLCENGINE_DCDN: `${ACCESS_PROVIDERS.VOLCENGINE}-dcdn`,
@@ -344,6 +347,7 @@ export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProv
     [DEPLOY_PROVIDERS.JDCLOUD_VOD, "provider.jdcloud.vod", DEPLOY_CATEGORIES.AV],
     [DEPLOY_PROVIDERS.QINIU_CDN, "provider.qiniu.cdn", DEPLOY_CATEGORIES.CDN],
     [DEPLOY_PROVIDERS.QINIU_PILI, "provider.qiniu.pili", DEPLOY_CATEGORIES.AV],
+    [DEPLOY_PROVIDERS.UPYUN_CDN, "provider.upyun.cdn", DEPLOY_CATEGORIES.CDN],
     [DEPLOY_PROVIDERS.BAISHAN_CDN, "provider.baishan.cdn", DEPLOY_CATEGORIES.CDN],
     [DEPLOY_PROVIDERS.DOGECLOUD_CDN, "provider.dogecloud.cdn", DEPLOY_CATEGORIES.CDN],
     [DEPLOY_PROVIDERS.BYTEPLUS_CDN, "provider.byteplus.cdn", DEPLOY_CATEGORIES.CDN],
diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json
index b3884dd4..caaf1dce 100644
--- a/ui/src/i18n/locales/en/nls.access.json
+++ b/ui/src/i18n/locales/en/nls.access.json
@@ -75,6 +75,12 @@
   "access.form.baiducloud_secret_access_key.label": "Baidu Cloud SecretAccessKey",
   "access.form.baiducloud_secret_access_key.placeholder": "Please enter Baidu Cloud SecretAccessKey",
   "access.form.baiducloud_secret_access_key.tooltip": "For more information, see <a href=\"https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en\" target=\"_blank\">https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en</a>",
+  "access.form.upyun_username.label": "UPYUN subaccount username",
+  "access.form.upyun_username.placeholder": "Please enter UPYUN subaccount username",
+  "access.form.upyun_username.tooltip": "For more information, see <a href=\"https://console.upyun.com/account/subaccount/\" target=\"_blank\">https://console.upyun.com/account/subaccount/</a>",
+  "access.form.upyun_password.label": "UPYUN subaccount password",
+  "access.form.upyun_password.placeholder": "Please enter UPYUN subaccount password",
+  "access.form.upyun_password.tooltip": "For more information, see <a href=\"https://console.upyun.com/account/subaccount/\" target=\"_blank\">https://console.upyun.com/account/subaccount/</a>",
   "access.form.baishan_api_token.label": "Baishan Cloud API token",
   "access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token",
   "access.form.baotapanel_api_url.label": "aaPanel URL",
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index 4c6091e1..9034aeea 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -93,6 +93,8 @@
   "provider.ucloud": "UCloud",
   "provider.ucloud.ucdn": "UCloud - UCDN (UCloud Content Delivery Network)",
   "provider.ucloud.us3": "UCloud - US3 (UCloud Object-based Storage)",
+  "provider.upyun": "UPYUN",
+  "provider.upyun.cdn": "UPYUN - CDN (Content Delivery Network)",
   "provider.volcengine": "Volcengine",
   "provider.volcengine.cdn": "Volcengine - CDN (Content Delivery Network)",
   "provider.volcengine.clb": "Volcengine - CLB (Cloud Load Balancer)",
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index 6d4b0f97..666ef424 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -508,6 +508,9 @@
   "workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 domain",
   "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 domain name",
   "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
+  "workflow_node.deploy.form.upyun_cdn_domain.label": "UPYUN CDN domain",
+  "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name",
+  "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "For more information, see <a href=\"https://console.upyun.com/services/cdn/\" target=\"_blank\">https://console.upyun.com/services/cdn/</a>",
   "workflow_node.deploy.form.volcengine_cdn_domain.label": "VolcEngine CDN domain",
   "workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "Please enter VolcEngine CDN domain name",
   "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/cdn/homepage\" target=\"_blank\">https://console.volcengine.com/cdn/homepage</a>",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index d72cd259..bb2f829a 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -243,6 +243,12 @@
   "access.form.ucloud_project_id.label": "优刻得项目 ID(可选)",
   "access.form.ucloud_project_id.placeholder": "请输入优刻得项目 ID",
   "access.form.ucloud_project_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/uaccount/iam/project_manage\" target=\"_blank\">https://console.ucloud.cn/uaccount/iam/project_manage</a>",
+  "access.form.upyun_username.label": "又拍云子账号用户名",
+  "access.form.upyun_username.placeholder": "请输入又拍云子账号用户名",
+  "access.form.upyun_username.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/account/subaccount/\" target=\"_blank\">https://console.upyun.com/account/subaccount/</a><br><br>请关闭该账号的二次登录验证。",
+  "access.form.upyun_password.label": "又拍云子账号密码",
+  "access.form.upyun_password.placeholder": "请输入又拍云子账号密码",
+  "access.form.upyun_password.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/account/subaccount/\" target=\"_blank\">https://console.upyun.com/account/subaccount/</a><br><br>请关闭该账号的二次登录验证。",
   "access.form.volcengine_access_key_id.label": "火山引擎 AccessKeyId",
   "access.form.volcengine_access_key_id.placeholder": "请输入火山引擎 AccessKeyId",
   "access.form.volcengine_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index e8580e41..2e830cf3 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -93,6 +93,8 @@
   "provider.ucloud": "优刻得",
   "provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN",
   "provider.ucloud.us3": "优刻得 - 对象存储 US3",
+  "provider.upyun": "又拍云",
+  "provider.upyun.cdn": "又拍云 - 云分发 CDN",
   "provider.volcengine": "火山引擎",
   "provider.volcengine.cdn": "火山引擎 - 内容分发网络 CDN",
   "provider.volcengine.clb": "火山引擎 - 负载均衡 CLB",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index 3144bfa8..63d881f8 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -508,6 +508,9 @@
   "workflow_node.deploy.form.ucloud_us3_domain.label": "优刻得 US3 自定义域名",
   "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "请输入优刻得 US3 自定义域名",
   "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ufile\" target=\"_blank\">https://console.ucloud.cn/ufile</a>",
+  "workflow_node.deploy.form.upyun_cdn_domain.label": "又拍云 CDN 加速域名",
+  "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)",
+  "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/services/cdn/\" target=\"_blank\">https://console.upyun.com/services/cdn/</a>",
   "workflow_node.deploy.form.volcengine_cdn_domain.label": "火山引擎 CDN 加速域名",
   "workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "请输入火山引擎 CDN 加速域名(支持泛域名)",
   "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/cdn/homepage\" target=\"_blank\">https://console.volcengine.com/cdn/homepage</a>",