Repository: dragonflyoss/Dragonfly2
Pull request #1617 (2022-08-31 10:20:40)

chore: update debug info
<!--- Provide a general summary of your changes in the Title above -->

## Description

<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.


codecov-commenter: # [Codecov](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) Report
> Merging [#1617](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (5a138f5) into [main](https://codecov.io/gh/dragonflyoss/Dragonfly2/commit/db06594884ef14595249829f7a8b097e434311d0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (db06594) will **increase** coverage by `0.06%`.
> The diff coverage is `0.00%`.

> :exclamation: Current head 5a138f5 differs from pull request most recent head da5257f. Consider uploading reports for the commit da5257f to get more accurate results

[![Impacted file tree graph](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617/graphs/tree.svg?width=650&height=150&src=pr&token=PUICJYSAT7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)

```diff
@@            Coverage Diff             @@
##             main    #1617      +/-   ##
==========================================
+ Coverage   41.62%   41.69%   +0.06%     
==========================================
  Files         125      125              
  Lines       11540    11542       +2     
==========================================
+ Hits         4804     4812       +8     
+ Misses       6227     6225       -2     
+ Partials      509      505       -4     
```

| Flag | Coverage Δ | |
|---|---|---|
| Object-compatibility-e2etests | `?` | |
| e2etests | `?` | |
| unittests | `41.69% <0.00%> (+0.06%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) | Coverage Δ | |
|---|---|---|
| [client/daemon/rpcserver/rpcserver.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9ycGNzZXJ2ZXIvcnBjc2VydmVyLmdv) | `19.13% <0.00%> (-0.09%)` | :arrow_down: |
| [...ent/daemon/peer/peertask\_piecetask\_synchronizer.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1617/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BlZXJ0YXNrX3BpZWNldGFza19zeW5jaHJvbml6ZXIuZ28=) | `49.58% <0.00%> (+3.33%)` | :arrow_up: |


Patch:
diff --git a/client/config/constants.go b/client/config/constants.go
index 0bbcdacd..5e3494d3 100644
--- a/client/config/constants.go
+++ b/client/config/constants.go
@@ -94,3 +94,8 @@ const (
 	DefaultObjectStorageStartPort = 65004
 	DefaultHealthyStartPort       = 40901
 )
+
+var (
+	// DefaultCertValidityPeriod is default validity period of certificate.
+	DefaultCertValidityPeriod = 180 * 24 * time.Hour
+)
diff --git a/client/config/dynconfig_local.go b/client/config/dynconfig_local.go
index 70769453..0e6ff80f 100644
--- a/client/config/dynconfig_local.go
+++ b/client/config/dynconfig_local.go
@@ -59,22 +59,23 @@ func (d *dynconfigLocal) GetResolveSchedulerAddrs() ([]resolver.Address, error)
 		r := reachable.New(&reachable.Config{Address: addr})
 		if err := r.Check(); err != nil {
 			logger.Warnf("scheduler address %s is unreachable", addr)
-		} else {
-			if addrs[addr] {
-				continue
-			}
+			continue
+		}
 
-			host, _, err := net.SplitHostPort(addr)
-			if err != nil {
-				continue
-			}
+		if addrs[addr] {
+			continue
+		}
 
-			resolveAddrs = append(resolveAddrs, resolver.Address{
-				ServerName: host,
-				Addr:       addr,
-			})
-			addrs[addr] = true
+		host, _, err := net.SplitHostPort(addr)
+		if err != nil {
+			continue
 		}
+
+		resolveAddrs = append(resolveAddrs, resolver.Address{
+			ServerName: host,
+			Addr:       addr,
+		})
+		addrs[addr] = true
 	}
 
 	return resolveAddrs, nil
diff --git a/client/config/dynconfig_manager.go b/client/config/dynconfig_manager.go
index 053eaec8..85b08942 100644
--- a/client/config/dynconfig_manager.go
+++ b/client/config/dynconfig_manager.go
@@ -37,7 +37,7 @@ import (
 )
 
 // Daemon cache file name.
-var cacheFileName = "daemon_dynconfig"
+var cacheFileName = "daemon"
 
 type dynconfigManager struct {
 	internaldynconfig.Dynconfig
@@ -74,25 +74,33 @@ func (d *dynconfigManager) GetResolveSchedulerAddrs() ([]resolver.Address, error
 	}
 
 	var (
-		addrs        = map[string]bool{}
-		resolveAddrs = []resolver.Address{}
+		addrs              = map[string]bool{}
+		resolveAddrs       = []resolver.Address{}
+		schedulerClusterID uint64
 	)
 	for _, scheduler := range schedulers {
+		// Check whether scheduler is in the same cluster.
+		if schedulerClusterID != 0 && schedulerClusterID != scheduler.SchedulerClusterId {
+			continue
+		}
+
 		addr := fmt.Sprintf("%s:%d", scheduler.GetIp(), scheduler.GetPort())
 		r := reachable.New(&reachable.Config{Address: addr})
 		if err := r.Check(); err != nil {
 			logger.Warnf("scheduler address %s is unreachable", addr)
-		} else {
-			if addrs[addr] {
-				continue
-			}
+			continue
+		}
 
-			resolveAddrs = append(resolveAddrs, resolver.Address{
-				ServerName: scheduler.GetIp(),
-				Addr:       addr,
-			})
-			addrs[addr] = true
+		if addrs[addr] {
+			continue
 		}
+
+		schedulerClusterID = scheduler.SchedulerClusterId
+		resolveAddrs = append(resolveAddrs, resolver.Address{
+			ServerName: scheduler.GetIp(),
+			Addr:       addr,
+		})
+		addrs[addr] = true
 	}
 
 	return resolveAddrs, nil
diff --git a/client/config/peerhost.go b/client/config/peerhost.go
index 8d9d3a27..0848c399 100644
--- a/client/config/peerhost.go
+++ b/client/config/peerhost.go
@@ -39,7 +39,7 @@ import (
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/pkg/dfnet"
 	netip "d7y.io/dragonfly/v2/pkg/net/ip"
-	"d7y.io/dragonfly/v2/pkg/serialize"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/pkg/unit"
 )
 
@@ -178,14 +178,21 @@ type GlobalSecurityOption struct {
 	// if AutoIssueCert is false, any other option in Security will be ignored
 	AutoIssueCert bool `mapstructure:"autoIssueCert" yaml:"autoIssueCert"`
 	// CACert is the root CA certificate for all grpc tls handshake, it can be path or PEM format string
-	CACert serialize.PEMContent `mapstructure:"caCert" yaml:"caCert"`
-	// TLSPrefer indicates to verify client certificates for grpc ServerHandshake
+	CACert types.PEMContent `mapstructure:"caCert" yaml:"caCert"`
+	// TLSVerify indicates to verify client certificates.
 	TLSVerify bool `mapstructure:"tlsVerify" yaml:"tlsVerify"`
 	// TLSPolicy controls the grpc shandshake behaviors:
 	// force: both ClientHandshake and ServerHandshake are only support tls
 	// prefer: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support tls
-	// default or empty: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls)
+	// default: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls)
 	TLSPolicy string `mapstructure:"tlsPolicy" yaml:"tlsPolicy"`
+	// CertSpec is the desired state of certificate.
+	CertSpec *CertSpec `mapstructure:"certSpec" yaml:"certSpec"`
+}
+
+type CertSpec struct {
+	// ValidityPeriod is the validity period of certificate.
+	ValidityPeriod time.Duration `mapstructure:"validityPeriod" yaml:"validityPeriod"`
 }
 
 type SchedulerOption struct {
@@ -536,12 +543,12 @@ type UnixListenOption struct {
 
 type SecurityOption struct {
 	// Insecure indicate enable tls or not
-	Insecure  bool                 `mapstructure:"insecure" yaml:"insecure"`
-	CACert    serialize.PEMContent `mapstructure:"caCert" yaml:"caCert"`
-	Cert      serialize.PEMContent `mapstructure:"cert" yaml:"cert"`
-	Key       serialize.PEMContent `mapstructure:"key" yaml:"key"`
-	TLSVerify bool                 `mapstructure:"tlsVerify" yaml:"tlsVerify"`
-	TLSConfig *tls.Config          `mapstructure:"tlsConfig" yaml:"tlsConfig"`
+	Insecure  bool             `mapstructure:"insecure" yaml:"insecure"`
+	CACert    types.PEMContent `mapstructure:"caCert" yaml:"caCert"`
+	Cert      types.PEMContent `mapstructure:"cert" yaml:"cert"`
+	Key       types.PEMContent `mapstructure:"key" yaml:"key"`
+	TLSVerify bool             `mapstructure:"tlsVerify" yaml:"tlsVerify"`
+	TLSConfig *tls.Config      `mapstructure:"tlsConfig" yaml:"tlsConfig"`
 }
 
 type StorageOption struct {
@@ -572,9 +579,9 @@ type ReloadOption struct {
 }
 
 type tlsConfigFiles struct {
-	Cert   serialize.PEMContent `yaml:"cert" json:"cert"`
-	Key    serialize.PEMContent `yaml:"key" json:"key"`
-	CACert serialize.PEMContent `yaml:"caCert" json:"caCert"`
+	Cert   types.PEMContent `yaml:"cert" json:"cert"`
+	Key    types.PEMContent `yaml:"key" json:"key"`
+	CACert types.PEMContent `yaml:"caCert" json:"caCert"`
 }
 
 type TLSConfig struct {
diff --git a/client/config/peerhost_darwin.go b/client/config/peerhost_darwin.go
index 6ce3f919..e295c4b2 100644
--- a/client/config/peerhost_darwin.go
+++ b/client/config/peerhost_darwin.go
@@ -30,7 +30,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/net/fqdn"
 	"d7y.io/dragonfly/v2/pkg/net/ip"
 	"d7y.io/dragonfly/v2/pkg/rpc"
-	"d7y.io/dragonfly/v2/pkg/serialize"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 var peerHostConfig = func() *DaemonOption {
@@ -180,9 +180,12 @@ var peerHostConfig = func() *DaemonOption {
 		},
 		Security: GlobalSecurityOption{
 			AutoIssueCert: false,
-			CACert:        serialize.PEMContent(""),
+			CACert:        types.PEMContent(""),
 			TLSVerify:     false,
-			TLSPolicy:     rpc.DefaultTLSPolicy,
+			TLSPolicy:     rpc.PreferTLSPolicy,
+			CertSpec: &CertSpec{
+				ValidityPeriod: DefaultCertValidityPeriod,
+			},
 		},
 	}
 }
diff --git a/client/config/peerhost_linux.go b/client/config/peerhost_linux.go
index 12d6427d..f8247b21 100644
--- a/client/config/peerhost_linux.go
+++ b/client/config/peerhost_linux.go
@@ -30,7 +30,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/net/fqdn"
 	"d7y.io/dragonfly/v2/pkg/net/ip"
 	"d7y.io/dragonfly/v2/pkg/rpc"
-	"d7y.io/dragonfly/v2/pkg/serialize"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 var peerHostConfig = func() *DaemonOption {
@@ -179,9 +179,12 @@ var peerHostConfig = func() *DaemonOption {
 		},
 		Security: GlobalSecurityOption{
 			AutoIssueCert: false,
-			CACert:        serialize.PEMContent(""),
+			CACert:        types.PEMContent(""),
 			TLSVerify:     false,
 			TLSPolicy:     rpc.DefaultTLSPolicy,
+			CertSpec: &CertSpec{
+				ValidityPeriod: DefaultCertValidityPeriod,
+			},
 		},
 	}
 }
diff --git a/client/daemon/daemon.go b/client/daemon/daemon.go
index 327ff627..1fa39111 100644
--- a/client/daemon/daemon.go
+++ b/client/daemon/daemon.go
@@ -26,6 +26,7 @@ import (
 	"net/http"
 	"os"
 	"path"
+	"path/filepath"
 	"runtime"
 	"sync"
 	"time"
@@ -55,6 +56,7 @@ import (
 	"d7y.io/dragonfly/v2/client/util"
 	"d7y.io/dragonfly/v2/cmd/dependency"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
+	internaldynconfig "d7y.io/dragonfly/v2/internal/dynconfig"
 	"d7y.io/dragonfly/v2/pkg/cache"
 	"d7y.io/dragonfly/v2/pkg/dfnet"
 	"d7y.io/dragonfly/v2/pkg/dfpath"
@@ -65,6 +67,7 @@ import (
 	managerclient "d7y.io/dragonfly/v2/pkg/rpc/manager/client"
 	schedulerclient "d7y.io/dragonfly/v2/pkg/rpc/scheduler/client"
 	"d7y.io/dragonfly/v2/pkg/source"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 type Daemon interface {
@@ -149,33 +152,30 @@ func New(opt *config.DaemonOption, d dfpath.Dfpath) (Daemon, error) {
 		if opt.Security.AutoIssueCert {
 			certifyClient = &certify.Certify{
 				CommonName:   ip.IPv4,
-				Issuer:       issuer.NewDragonflyIssuer(managerClient),
+				Issuer:       issuer.NewDragonflyIssuer(managerClient, issuer.WithValidityPeriod(opt.Security.CertSpec.ValidityPeriod)),
 				RenewBefore:  time.Hour,
 				CertConfig:   nil,
 				IssueTimeout: 0,
 				Logger:       zapadapter.New(logger.CoreLogger.Desugar()),
 				Cache: cache.NewCertifyMutliCache(
 					certify.NewMemCache(),
-					certify.DirCache(path.Join(d.CacheDir(), cache.DfdaemonCertifyCacheDirName))),
+					certify.DirCache(path.Join(d.CacheDir(), cache.CertifyCacheDirName, types.DfdaemonName))),
 			}
 
 			// issue a certificate to reduce first time delay
-			cert, err := certifyClient.GetCertificate(&tls.ClientHelloInfo{
+			if _, err := certifyClient.GetCertificate(&tls.ClientHelloInfo{
 				ServerName: ip.IPv4,
-			})
-			if err != nil {
+			}); err != nil {
 				logger.Errorf("issue certificate error: %s", err.Error())
 				return nil, err
 			}
-			logger.Debugf("request cert from manager, common name: %s, issuer: %s",
-				cert.Leaf.Subject.CommonName, cert.Leaf.Issuer.CommonName)
 		}
 
 		// New dynconfig manager client.
 		dynconfig, err = config.NewDynconfig(
 			config.ManagerSourceType, opt,
 			config.WithManagerClient(managerClient),
-			config.WithCacheDir(d.CacheDir()),
+			config.WithCacheDir(filepath.Join(d.CacheDir(), internaldynconfig.CacheDirName)),
 			config.WithExpireTime(opt.Scheduler.Manager.RefreshInterval),
 		)
 		if err != nil {
diff --git a/client/daemon/metrics/metrics.go b/client/daemon/metrics/metrics.go
index a9d399a5..5cee8efd 100644
--- a/client/daemon/metrics/metrics.go
+++ b/client/daemon/metrics/metrics.go
@@ -23,7 +23,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus/promauto"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 
-	"d7y.io/dragonfly/v2/internal/constants"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 const (
@@ -45,120 +45,120 @@ const (
 
 var (
 	ProxyRequestCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "proxy_request_total",
 		Help:      "Counter of the total proxy request.",
 	}, []string{"method"})
 
 	ProxyRequestViaDragonflyCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "proxy_request_via_dragonfly_total",
 		Help:      "Counter of the total proxy request via Dragonfly.",
 	})
 
 	ProxyRequestNotViaDragonflyCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "proxy_request_not_via_dragonfly_total",
 		Help:      "Counter of the total proxy request not via Dragonfly.",
 	})
 
 	ProxyRequestRunningCount = promauto.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "proxy_request_running_total",
 		Help:      "Current running count of proxy request.",
 	}, []string{"method"})
 
 	ProxyRequestBytesCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "proxy_request_bytes_total",
 		Help:      "Counter of the total byte of all proxy request.",
 	}, []string{"method"})
 
 	PeerTaskCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "peer_task_total",
 		Help:      "Counter of the total peer tasks.",
 	})
 
 	PeerTaskFailedCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "peer_task_failed_total",
 		Help:      "Counter of the total failed peer tasks.",
 	}, []string{"type"})
 
 	PieceTaskCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "piece_task_total",
 		Help:      "Counter of the total failed piece tasks.",
 	})
 
 	PieceTaskFailedCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "piece_task_failed_total",
 		Help:      "Counter of the total failed piece tasks.",
 	})
 
 	FileTaskCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "file_task_total",
 		Help:      "Counter of the total file tasks.",
 	})
 
 	StreamTaskCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "stream_task_total",
 		Help:      "Counter of the total stream tasks.",
 	})
 
 	SeedPeerDownloadCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "seed_peer_download_total",
 		Help:      "Counter of the number of the seed peer downloading.",
 	})
 
 	SeedPeerDownloadFailureCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "seed_peer_download_failure_total",
 		Help:      "Counter of the number of failed of the seed peer downloading.",
 	})
 
 	SeedPeerDownloadTraffic = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "seed_peer_download_traffic",
 		Help:      "Counter of the number of seed peer download traffic.",
 	}, []string{"type"})
 
 	SeedPeerConcurrentDownloadGauge = promauto.NewGauge(prometheus.GaugeOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "seed_peer_concurrent_download_total",
 		Help:      "Gauger of the number of concurrent of the seed peer downloading.",
 	})
 
 	PeerTaskCacheHitCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "peer_task_cache_hit_total",
 		Help:      "Counter of the total cache hit peer tasks.",
 	})
 
 	PrefetchTaskCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.DfdaemonMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.DfdaemonMetricsName,
 		Name:      "prefetch_task_total",
 		Help:      "Counter of the total prefetched tasks.",
 	})
diff --git a/client/daemon/proxy/proxy.go b/client/daemon/proxy/proxy.go
index 8cb8267e..ac6b0dd6 100644
--- a/client/daemon/proxy/proxy.go
+++ b/client/daemon/proxy/proxy.go
@@ -597,10 +597,6 @@ func (proxy *Proxy) checkWhiteList(r *http.Request) bool {
 // also changes the scheme of the given request if the matched rule has
 // UseHTTPS = true
 func (proxy *Proxy) shouldUseDragonfly(req *http.Request) bool {
-	if req.Method != http.MethodGet {
-		return false
-	}
-
 	for _, rule := range proxy.rules.Load().([]*config.ProxyRule) {
 		if rule.Match(req.URL.String()) {
 			if rule.UseHTTPS {
@@ -619,6 +615,10 @@ func (proxy *Proxy) shouldUseDragonfly(req *http.Request) bool {
 				req.URL.Host = rule.Redirect
 				req.Host = rule.Redirect
 			}
+
+			if req.Method != http.MethodGet {
+				return false
+			}
 			return !rule.Direct
 		}
 	}
diff --git a/client/daemon/rpcserver/rpcserver.go b/client/daemon/rpcserver/rpcserver.go
index c0fada39..b25184ea 100644
--- a/client/daemon/rpcserver/rpcserver.go
+++ b/client/daemon/rpcserver/rpcserver.go
@@ -220,7 +220,11 @@ func printAuthInfo(ctx context.Context) {
 				logger.Debugf("peer cert depth %d, issuer: %#v", i, pc.Issuer.CommonName)
 				logger.Debugf("peer cert depth %d, common name: %#v", i, pc.Subject.CommonName)
 				if len(pc.IPAddresses) > 0 {
-					logger.Debugf("peer cert depth %d, ip: %#v", i, pc.IPAddresses)
+					var ips []string
+					for _, ip := range pc.IPAddresses {
+						ips = append(ips, ip.String())
+					}
+					logger.Debugf("peer cert depth %d, ip: %s", i, strings.Join(ips, " "))
 				}
 				if len(pc.DNSNames) > 0 {
 					logger.Debugf("peer cert depth %d, dns: %#v", i, pc.DNSNames)
diff --git a/cmd/dependency/dependency.go b/cmd/dependency/dependency.go
index a1538cc3..974da1a2 100644
--- a/cmd/dependency/dependency.go
+++ b/cmd/dependency/dependency.go
@@ -50,7 +50,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/dfpath"
 	"d7y.io/dragonfly/v2/pkg/net/fqdn"
 	"d7y.io/dragonfly/v2/pkg/net/ip"
-	"d7y.io/dragonfly/v2/pkg/serialize"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/pkg/unit"
 	"d7y.io/dragonfly/v2/version"
 )
@@ -239,7 +239,7 @@ func initDecoderConfig(dc *mapstructure.DecoderConfig) {
 			reflect.TypeOf(util.Duration{}),
 			reflect.TypeOf(&config.ProxyOption{}),
 			reflect.TypeOf(config.TCPListenPortRange{}),
-			reflect.TypeOf(serialize.PEMContent("")),
+			reflect.TypeOf(types.PEMContent("")),
 			reflect.TypeOf(config.URL{}),
 			reflect.TypeOf(config.CertPool{}),
 			reflect.TypeOf(config.Regexp{}):
diff --git a/cmd/dfget/cmd/daemon.go b/cmd/dfget/cmd/daemon.go
index f91d4129..2bb61ec2 100644
--- a/cmd/dfget/cmd/daemon.go
+++ b/cmd/dfget/cmd/daemon.go
@@ -36,6 +36,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/dfnet"
 	"d7y.io/dragonfly/v2/pkg/dfpath"
 	"d7y.io/dragonfly/v2/pkg/rpc/dfdaemon/client"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/version"
 )
 
@@ -75,7 +76,7 @@ it supports container engine, wget and other downloading tools through proxy fun
 		if err := logger.InitDaemon(cfg.Verbose, cfg.Console, d.LogDir()); err != nil {
 			return fmt.Errorf("init client daemon logger: %w", err)
 		}
-		logger.RedirectStdoutAndStderr(cfg.Console, path.Join(d.LogDir(), "daemon"))
+		logger.RedirectStdoutAndStderr(cfg.Console, path.Join(d.LogDir(), types.DaemonName))
 
 		return runDaemon(d)
 	},
@@ -112,11 +113,9 @@ func initDaemonDfpath(cfg *config.DaemonOption) (dfpath.Dfpath, error) {
 		options = append(options, dfpath.WithDownloadUnixSocketPath(cfg.Download.DownloadGRPC.UnixListen.Socket))
 	}
 
-	cacheDir := dfpath.DefaultCacheDir
 	if cfg.CacheDir != "" {
-		cacheDir = cfg.CacheDir
+		options = append(options, dfpath.WithCacheDir(cfg.CacheDir))
 	}
-	options = append(options, dfpath.WithCacheDir(cacheDir))
 
 	dataDir := dfpath.DefaultDataDir
 	if cfg.DataDir != "" {
diff --git a/cmd/dfget/cmd/root.go b/cmd/dfget/cmd/root.go
index 5298fb36..e566d244 100644
--- a/cmd/dfget/cmd/root.go
+++ b/cmd/dfget/cmd/root.go
@@ -34,7 +34,6 @@ import (
 	"d7y.io/dragonfly/v2/client/config"
 	"d7y.io/dragonfly/v2/client/dfget"
 	"d7y.io/dragonfly/v2/cmd/dependency"
-	"d7y.io/dragonfly/v2/internal/constants"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/pkg/basic"
 	"d7y.io/dragonfly/v2/pkg/dfnet"
@@ -42,6 +41,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/net/ip"
 	"d7y.io/dragonfly/v2/pkg/rpc/dfdaemon/client"
 	"d7y.io/dragonfly/v2/pkg/source"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/pkg/unit"
 	"d7y.io/dragonfly/v2/version"
 )
@@ -230,7 +230,7 @@ func runDfget(dfgetLockPath, daemonSockPath string) error {
 		err            error
 	)
 
-	if dfgetConfig.Pattern != constants.SourcePattern {
+	if dfgetConfig.Pattern != types.SourcePattern {
 		logger.Info("start to check and spawn daemon")
 		if dfdaemonClient, err = checkAndSpawnDaemon(dfgetLockPath, daemonSockPath); err != nil {
 			logger.Errorf("check and spawn daemon error: %v", err)
diff --git a/cmd/manager/cmd/root.go b/cmd/manager/cmd/root.go
index a2a2cdef..d4dc3df9 100644
--- a/cmd/manager/cmd/root.go
+++ b/cmd/manager/cmd/root.go
@@ -29,6 +29,7 @@ import (
 	"d7y.io/dragonfly/v2/manager"
 	"d7y.io/dragonfly/v2/manager/config"
 	"d7y.io/dragonfly/v2/pkg/dfpath"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/version"
 )
 
@@ -61,7 +62,7 @@ for managing schedulers and seed peers, offering http apis and portal, etc.`,
 		if err := logger.InitManager(cfg.Verbose, cfg.Console, d.LogDir()); err != nil {
 			return fmt.Errorf("init manager logger: %w", err)
 		}
-		logger.RedirectStdoutAndStderr(cfg.Console, path.Join(d.LogDir(), "manager"))
+		logger.RedirectStdoutAndStderr(cfg.Console, path.Join(d.LogDir(), types.ManagerName))
 
 		return runManager(d)
 	},
@@ -90,6 +91,10 @@ func initDfpath(cfg *config.ServerConfig) (dfpath.Dfpath, error) {
 		options = append(options, dfpath.WithLogDir(cfg.LogDir))
 	}
 
+	if cfg.CacheDir != "" {
+		options = append(options, dfpath.WithCacheDir(cfg.CacheDir))
+	}
+
 	return dfpath.New(options...)
 }
 
diff --git a/cmd/scheduler/cmd/root.go b/cmd/scheduler/cmd/root.go
index c3550ea9..9eb74a12 100644
--- a/cmd/scheduler/cmd/root.go
+++ b/cmd/scheduler/cmd/root.go
@@ -28,6 +28,7 @@ import (
 	"d7y.io/dragonfly/v2/cmd/dependency"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/pkg/dfpath"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/scheduler"
 	"d7y.io/dragonfly/v2/scheduler/config"
 	"d7y.io/dragonfly/v2/version"
@@ -65,7 +66,7 @@ generate and maintain a P2P network during the download process, and push suitab
 		if err := logger.InitScheduler(cfg.Verbose, cfg.Console, d.LogDir()); err != nil {
 			return fmt.Errorf("init scheduler logger: %w", err)
 		}
-		logger.RedirectStdoutAndStderr(cfg.Console, path.Join(d.LogDir(), "scheduler"))
+		logger.RedirectStdoutAndStderr(cfg.Console, path.Join(d.LogDir(), types.SchedulerName))
 
 		return runScheduler(ctx, d)
 	},
@@ -97,11 +98,9 @@ func initDfpath(cfg *config.ServerConfig) (dfpath.Dfpath, error) {
 		options = append(options, dfpath.WithLogDir(cfg.LogDir))
 	}
 
-	cacheDir := dfpath.DefaultCacheDir
 	if cfg.CacheDir != "" {
-		cacheDir = cfg.CacheDir
+		options = append(options, dfpath.WithCacheDir(cfg.CacheDir))
 	}
-	options = append(options, dfpath.WithCacheDir(cacheDir))
 
 	dataDir := dfpath.DefaultDataDir
 	if cfg.DataDir != "" {
diff --git a/go.mod b/go.mod
index 6db3df70..152cecdd 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module d7y.io/dragonfly/v2
 go 1.18
 
 require (
-	d7y.io/api v1.1.2
+	d7y.io/api v1.1.4
 	github.com/RichardKnop/machinery v1.10.6
 	github.com/Showmax/go-fqdn v1.0.0
 	github.com/VividCortex/mysqlerr v1.0.0
@@ -74,7 +74,7 @@ require (
 	golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704
 	golang.org/x/time v0.0.0-20220609170525-579cf78fd858
 	google.golang.org/api v0.92.0
-	google.golang.org/grpc v1.48.0
+	google.golang.org/grpc v1.49.0
 	google.golang.org/protobuf v1.28.1
 	gopkg.in/natefinch/lumberjack.v2 v2.0.0
 	gopkg.in/yaml.v3 v3.0.1
diff --git a/go.sum b/go.sum
index ff6b9ef4..c14d7cea 100644
--- a/go.sum
+++ b/go.sum
@@ -71,8 +71,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
-d7y.io/api v1.1.2 h1:aBS5r3XYgieji3OfcJgPnBPu3JbovW0H2GyG+FIfKM4=
-d7y.io/api v1.1.2/go.mod h1:GFnWPZFe4DUW70aOQikRZF0pvXpbUwAsGSCAZFFitPo=
+d7y.io/api v1.1.4 h1:0q03DhwleypyXwzK/LfNiR6BMgGYuL3jAGPsaeuJgJ8=
+d7y.io/api v1.1.4/go.mod h1:hAXXMIDqH3Wpbx6dK+cWUqS0mgVNbM9AOcOxD9OmU/I=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
@@ -1853,8 +1853,8 @@ google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11
 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
-google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
+google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/internal/constants/constants.go b/internal/constants/constants.go
deleted file mode 100644
index bd68d207..00000000
--- a/internal/constants/constants.go
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *     Copyright 2020 The Dragonfly Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package constants
-
-const (
-	// download pattern
-	SourcePattern   = "source"
-	SeedPeerPattern = "seed-peer"
-	P2PPattern      = "p2p"
-)
-
-const (
-	MetricsNamespace     = "dragonfly"
-	ManagerMetricsName   = "manager"
-	SchedulerMetricsName = "scheduler"
-	DfdaemonMetricsName  = "dfdaemon"
-)
diff --git a/internal/dflog/loginit.go b/internal/dflog/loginit.go
index 7cdfa00c..38ea9974 100644
--- a/internal/dflog/loginit.go
+++ b/internal/dflog/loginit.go
@@ -21,6 +21,8 @@ import (
 	"path/filepath"
 
 	"go.uber.org/zap"
+
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 type logInitMeta struct {
@@ -34,7 +36,7 @@ func InitManager(verbose, console bool, dir string) error {
 		return createConsoleLogger(verbose)
 	}
 
-	logDir := filepath.Join(dir, "manager")
+	logDir := filepath.Join(dir, types.ManagerName)
 
 	var meta = []logInitMeta{
 		{
@@ -63,7 +65,7 @@ func InitScheduler(verbose, console bool, dir string) error {
 		return createConsoleLogger(verbose)
 	}
 
-	logDir := filepath.Join(dir, "scheduler")
+	logDir := filepath.Join(dir, types.SchedulerName)
 
 	var meta = []logInitMeta{
 		{
@@ -92,7 +94,7 @@ func InitDaemon(verbose, console bool, dir string) error {
 		return createConsoleLogger(verbose)
 	}
 
-	logDir := filepath.Join(dir, "daemon")
+	logDir := filepath.Join(dir, types.DaemonName)
 
 	var meta = []logInitMeta{
 		{
@@ -117,7 +119,7 @@ func InitDfget(verbose, console bool, dir string) error {
 		return createConsoleLogger(verbose)
 	}
 
-	logDir := filepath.Join(dir, "dfget")
+	logDir := filepath.Join(dir, types.DfgetName)
 
 	var meta = []logInitMeta{
 		{
@@ -178,7 +180,7 @@ func createFileLogger(verbose bool, meta []logInitMeta, logDir string) error {
 }
 
 func InitDfcache(console bool, dir string) error {
-	logDir := filepath.Join(dir, "dfcache")
+	logDir := filepath.Join(dir, types.DfcacheName)
 
 	var meta = []logInitMeta{
 		{
diff --git a/internal/dynconfig/dynconfig.go b/internal/dynconfig/dynconfig.go
index b548aa42..4ff95f57 100644
--- a/internal/dynconfig/dynconfig.go
+++ b/internal/dynconfig/dynconfig.go
@@ -30,6 +30,11 @@ import (
 
 type SourceType string
 
+const (
+	// CacheDirName is dir name of dynconfig cache.
+	CacheDirName = "dynconfig"
+)
+
 const (
 	// defaultCacheKey represents cache key of dynconfig.
 	defaultCacheKey = "dynconfig"
diff --git a/manager/config/config.go b/manager/config/config.go
index 6d03c3f5..206343f5 100644
--- a/manager/config/config.go
+++ b/manager/config/config.go
@@ -26,8 +26,8 @@ import (
 	"d7y.io/dragonfly/v2/cmd/dependency/base"
 	"d7y.io/dragonfly/v2/pkg/objectstorage"
 	"d7y.io/dragonfly/v2/pkg/rpc"
-	"d7y.io/dragonfly/v2/pkg/serialize"
 	"d7y.io/dragonfly/v2/pkg/slices"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 type Config struct {
@@ -57,6 +57,9 @@ type ServerConfig struct {
 	// Server name.
 	Name string `yaml:"name" mapstructure:"name"`
 
+	// Server dynamic config cache directory.
+	CacheDir string `yaml:"cacheDir" mapstructure:"cacheDir"`
+
 	// Server log directory.
 	LogDir string `yaml:"logDir" mapstructure:"logDir"`
 
@@ -252,17 +255,31 @@ type SecurityConfig struct {
 	// AutoIssueCert indicates to issue client certificates for all grpc call.
 	AutoIssueCert bool `yaml:"autoIssueCert" mapstructure:"autoIssueCert"`
 
-	// CACert is file path PEM-encoded certificate
-	CACert serialize.PEMContent `mapstructure:"caCert" yaml:"caCert"`
+	// CACert is the CA certificate for all grpc tls handshake, it can be path or PEM format string.
+	CACert types.PEMContent `mapstructure:"caCert" yaml:"caCert"`
 
-	// CAKey is file path of PEM-encoded private key.
-	CAKey serialize.PEMContent `mapstructure:"caKey" yaml:"caKey"`
+	// CAKey is the CA private key, it can be path or PEM format string.
+	CAKey types.PEMContent `mapstructure:"caKey" yaml:"caKey"`
 
 	// TLSPolicy controls the grpc shandshake behaviors:
 	// force: both ClientHandshake and ServerHandshake are only support tls
 	// prefer: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support tls
-	// default or empty: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls)
+	// default: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls)
 	TLSPolicy string `mapstructure:"tlsPolicy" yaml:"tlsPolicy"`
+
+	// CertSpec is the desired state of certificate.
+	CertSpec *CertSpec `mapstructure:"certSpec" yaml:"certSpec"`
+}
+
+type CertSpec struct {
+	// DNSNames is a list of dns names be set on the certificate.
+	DNSNames []string `mapstructure:"dnsNames" yaml:"dnsNames"`
+
+	// IPAddresses is a list of ip addresses be set on the certificate.
+	IPAddresses []string `mapstructure:"ipAddresses" yaml:"ipAddresses"`
+
+	// ValidityPeriod is the validity period  of certificate.
+	ValidityPeriod time.Duration `mapstructure:"validityPeriod" yaml:"validityPeriod"`
 }
 
 // New config instance.
@@ -314,7 +331,12 @@ func New() *Config {
 		},
 		Security: &SecurityConfig{
 			AutoIssueCert: false,
-			TLSPolicy:     rpc.DefaultTLSPolicy,
+			TLSPolicy:     rpc.PreferTLSPolicy,
+			CertSpec: &CertSpec{
+				IPAddresses:    DefaultCertIPAddresses,
+				DNSNames:       DefaultCertDNSNames,
+				ValidityPeriod: DefaultCertValidityPeriod,
+			},
 		},
 		Metrics: &MetricsConfig{
 			Enable:          false,
@@ -502,6 +524,18 @@ func (cfg *Config) Validate() error {
 		if !slices.Contains([]string{rpc.DefaultTLSPolicy, rpc.ForceTLSPolicy, rpc.PreferTLSPolicy}, cfg.Security.TLSPolicy) {
 			return errors.New("security requires parameter tlsPolicy")
 		}
+
+		if cfg.Security.CertSpec == nil {
+			return errors.New("security requires parameter certSpec")
+		}
+
+		if len(cfg.Security.CertSpec.IPAddresses) == 0 {
+			return errors.New("certSpec requires parameter ipAddresses")
+		}
+
+		if len(cfg.Security.CertSpec.DNSNames) == 0 {
+			return errors.New("certSpec requires parameter dnsNames")
+		}
 	}
 
 	if cfg.Metrics == nil {
diff --git a/manager/config/constants.go b/manager/config/constants.go
index 3b6ea169..e6dd8a4a 100644
--- a/manager/config/constants.go
+++ b/manager/config/constants.go
@@ -16,7 +16,11 @@
 
 package config
 
-import "time"
+import (
+	"time"
+
+	"d7y.io/dragonfly/v2/pkg/net/ip"
+)
 
 const (
 	// DatabaseTypeMysql is database type of mysql.
@@ -88,3 +92,14 @@ const (
 	// DefaultMetricsAddr is default address for metrics server.
 	DefaultMetricsAddr = ":8000"
 )
+
+var (
+	// DefaultCertIPAddresses is default ip addresses of certificate.
+	DefaultCertIPAddresses = []string{ip.IPv4}
+
+	// DefaultCertDNSNames is default dns names of certificate.
+	DefaultCertDNSNames = []string{"dragonfly-manager", "dragonfly-manager.dragonfly-system.svc", "dragonfly-manager.dragonfly-system.svc.cluster.local"}
+
+	// DefaultCertValidityPeriod is default validity period of certificate.
+	DefaultCertValidityPeriod = 10 * 365 * 24 * time.Hour
+)
diff --git a/manager/manager.go b/manager/manager.go
index a886cfcc..5de0cee5 100644
--- a/manager/manager.go
+++ b/manager/manager.go
@@ -47,6 +47,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/net/ip"
 	"d7y.io/dragonfly/v2/pkg/objectstorage"
 	"d7y.io/dragonfly/v2/pkg/rpc"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 const (
@@ -168,19 +169,34 @@ func New(cfg *config.Config, d dfpath.Dfpath) (*Server, error) {
 			return nil, err
 		}
 
-		// Manager GRPC server's tls varify must be false. If ClientCAs are required for client verification,
-		// the client cannot call the IssueCertificate api.
-		transportCredentials, err := rpc.NewServerCredentialsByCertify(cfg.Security.TLSPolicy, false, cert.Certificate, &certify.Certify{
-			CommonName:   ip.IPv4,
-			Issuer:       issuer.NewDragonflyManagerIssuer(&cert),
+		certifyClient := &certify.Certify{
+			CommonName: ip.IPv4,
+			Issuer: issuer.NewDragonflyManagerIssuer(
+				&cert,
+				issuer.WithManagerIPAddresses(cfg.Security.CertSpec.IPAddresses),
+				issuer.WithManagerDNSNames(cfg.Security.CertSpec.DNSNames),
+				issuer.WithManagerValidityPeriod(cfg.Security.CertSpec.ValidityPeriod),
+			),
 			RenewBefore:  time.Hour,
 			CertConfig:   nil,
 			IssueTimeout: 0,
 			Logger:       zapadapter.New(logger.CoreLogger.Desugar()),
 			Cache: pkgcache.NewCertifyMutliCache(
 				certify.NewMemCache(),
-				certify.DirCache(path.Join(d.CacheDir(), pkgcache.ManagerCertifyCacheDirName))),
-		})
+				certify.DirCache(path.Join(d.CacheDir(), pkgcache.CertifyCacheDirName, types.ManagerName))),
+		}
+
+		// Issue a certificate to reduce first time delay.
+		if _, err := certifyClient.GetCertificate(&tls.ClientHelloInfo{
+			ServerName: ip.IPv4,
+		}); err != nil {
+			logger.Errorf("issue certificate error: %s", err.Error())
+			return nil, err
+		}
+
+		// Manager GRPC server's tls varify must be false. If ClientCAs are required for client verification,
+		// the client cannot call the IssueCertificate api.
+		transportCredentials, err := rpc.NewServerCredentialsByCertify(cfg.Security.TLSPolicy, false, []byte(cfg.Security.CACert), certifyClient)
 		if err != nil {
 			return nil, err
 		}
diff --git a/manager/metrics/metrics.go b/manager/metrics/metrics.go
index 236cfd1d..74e1acf7 100644
--- a/manager/metrics/metrics.go
+++ b/manager/metrics/metrics.go
@@ -25,8 +25,8 @@ import (
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"google.golang.org/grpc"
 
-	"d7y.io/dragonfly/v2/internal/constants"
 	"d7y.io/dragonfly/v2/manager/config"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 func New(cfg *config.MetricsConfig, grpcServer *grpc.Server) *http.Server {
@@ -43,8 +43,8 @@ func New(cfg *config.MetricsConfig, grpcServer *grpc.Server) *http.Server {
 
 var (
 	PeerGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.ManagerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.ManagerMetricsName,
 		Name:      "peer_total",
 		Help:      "Gauge of the number of peer.",
 	}, []string{"version", "commit"})
diff --git a/manager/router/router.go b/manager/router/router.go
index ca5011fc..23494ac1 100644
--- a/manager/router/router.go
+++ b/manager/router/router.go
@@ -35,6 +35,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/handlers"
 	"d7y.io/dragonfly/v2/manager/middlewares"
 	"d7y.io/dragonfly/v2/manager/service"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 const (
@@ -53,7 +54,7 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
 	// Logging to a file.
 	if !cfg.Console {
 		gin.DisableConsoleColor()
-		logDir := filepath.Join(logDir, "manager")
+		logDir := filepath.Join(logDir, types.ManagerName)
 		f, _ := os.Create(filepath.Join(logDir, GinLogFileName))
 		gin.DefaultWriter = io.MultiWriter(f)
 	}
diff --git a/manager/rpcserver/cert.go b/manager/rpcserver/cert.go
index 8be893b6..f28bc68e 100644
--- a/manager/rpcserver/cert.go
+++ b/manager/rpcserver/cert.go
@@ -17,11 +17,9 @@
 package rpcserver
 
 import (
-	"bytes"
 	"context"
 	"crypto/rand"
 	"crypto/x509"
-	"encoding/pem"
 	"fmt"
 	"math/big"
 	"net"
@@ -36,9 +34,6 @@ import (
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 )
 
-// defaultValidityDuration is default validity duration of certificate.
-const defaultValidityDuration = 365 * 24 * time.Hour
-
 func (s *Server) IssueCertificate(ctx context.Context, req *securityv1.CertificateRequest) (*securityv1.CertificateResponse, error) {
 	if s.selfSignedCert == nil {
 		return nil, status.Errorf(codes.Unavailable, "ca is missing for this manager instance")
@@ -62,14 +57,8 @@ func (s *Server) IssueCertificate(ctx context.Context, req *securityv1.Certifica
 		}
 	}
 
-	// Decode csr pem.
-	block, _ := pem.Decode([]byte(req.Csr))
-	if block == nil {
-		return nil, status.Errorf(codes.InvalidArgument, "invalid csr format")
-	}
-
 	// Parse csr.
-	csr, err := x509.ParseCertificateRequest(block.Bytes)
+	csr, err := x509.ParseCertificateRequest(req.Csr)
 	if err != nil {
 		return nil, status.Errorf(codes.InvalidArgument, "invalid csr format: %s", err.Error())
 	}
@@ -79,27 +68,20 @@ func (s *Server) IssueCertificate(ctx context.Context, req *securityv1.Certifica
 	if err = csr.CheckSignature(); err != nil {
 		return nil, err
 	}
+	logger.Infof("valid csr: %#v", csr.Subject)
 
 	serial, err := rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
 	if err != nil {
 		return nil, err
 	}
 
-	// Check certificate duration.
 	now := time.Now()
-	duration := time.Duration(req.ValidityDuration) * time.Second
-	if duration == 0 {
-		duration = defaultValidityDuration
-	}
-
-	logger.Infof("valid csr: %#v", csr.Subject)
-
 	template := x509.Certificate{
 		SerialNumber:          serial,
 		Subject:               csr.Subject,
 		IPAddresses:           []net.IP{net.ParseIP(ip)}, // only valid for peer ip
 		NotBefore:             now.Add(-10 * time.Minute).UTC(),
-		NotAfter:              now.Add(duration).UTC(),
+		NotAfter:              now.Add(req.ValidityPeriod.AsDuration()).UTC(),
 		BasicConstraintsValid: true,
 		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment | x509.KeyUsageKeyEncipherment,
 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
@@ -110,13 +92,7 @@ func (s *Server) IssueCertificate(ctx context.Context, req *securityv1.Certifica
 		return nil, fmt.Errorf("failed to generate certificate, error: %s", err)
 	}
 
-	// Build the certificate chain.
-	var certPEM bytes.Buffer
-	if err = pem.Encode(&certPEM, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil {
-		return nil, err
-	}
-
 	return &securityv1.CertificateResponse{
-		CertificateChain: append([]string{certPEM.String()}, s.selfSignedCert.PEMCertChain...),
+		CertificateChain: append([][]byte{cert}, s.selfSignedCert.CertChain...),
 	}, nil
 }
diff --git a/manager/rpcserver/rpcserver.go b/manager/rpcserver/rpcserver.go
index f6d6b149..236a43ac 100644
--- a/manager/rpcserver/rpcserver.go
+++ b/manager/rpcserver/rpcserver.go
@@ -17,11 +17,9 @@
 package rpcserver
 
 import (
-	"bytes"
 	"context"
 	"crypto/tls"
 	"crypto/x509"
-	"encoding/pem"
 	"errors"
 	"io"
 	"time"
@@ -58,8 +56,8 @@ type SelfSignedCert struct {
 	// X509Cert is certificate of x509.
 	X509Cert *x509.Certificate
 
-	// PEMCertChain is certificate chain of pem.
-	PEMCertChain []string
+	// CertChain is certificate chain of ASN.1 DER form.
+	CertChain [][]byte
 }
 
 // Server is grpc server.
@@ -103,20 +101,10 @@ func WithSelfSignedCert(tlsCert *tls.Certificate) Option {
 			return err
 		}
 
-		var pemCertChain []string
-		for _, cert := range tlsCert.Certificate {
-			var certPEM bytes.Buffer
-			if err := pem.Encode(&certPEM, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil {
-				return err
-			}
-
-			pemCertChain = append(pemCertChain, certPEM.String())
-		}
-
 		s.selfSignedCert = &SelfSignedCert{
-			TLSCert:      tlsCert,
-			X509Cert:     x509CACert,
-			PEMCertChain: pemCertChain,
+			TLSCert:   tlsCert,
+			X509Cert:  x509CACert,
+			CertChain: tlsCert.Certificate,
 		}
 
 		return nil
diff --git a/pkg/cache/certify.go b/pkg/cache/certify.go
index e8a0e385..fcb2559b 100644
--- a/pkg/cache/certify.go
+++ b/pkg/cache/certify.go
@@ -24,14 +24,8 @@ import (
 )
 
 const (
-	// SchedulerCertifyCacheDirName is dir name of scheduler certify cache.
-	SchedulerCertifyCacheDirName = "scheduler-certs"
-
-	// ManagerCertifyCacheDirName is dir name of manager certify cache.
-	ManagerCertifyCacheDirName = "manager-certs"
-
-	// DfdaemonCertifyCacheDirName is dir name of dfdaemon certify cache.
-	DfdaemonCertifyCacheDirName = "dfdaemon-certs"
+	// CertifyCacheDirName is dir name of certify cache.
+	CertifyCacheDirName = "certs"
 )
 
 type certifyCache struct {
diff --git a/pkg/dfpath/dfpath.go b/pkg/dfpath/dfpath.go
index 10596817..6cafd379 100644
--- a/pkg/dfpath/dfpath.go
+++ b/pkg/dfpath/dfpath.go
@@ -111,6 +111,7 @@ func New(options ...Option) (Dfpath, error) {
 			workHome:       DefaultWorkHome,
 			logDir:         DefaultLogDir,
 			pluginDir:      DefaultPluginDir,
+			cacheDir:       DefaultCacheDir,
 			daemonSockPath: DefaultDownloadUnixSocketPath,
 		}
 
@@ -143,10 +144,8 @@ func New(options ...Option) (Dfpath, error) {
 		}
 
 		// Create cache directory.
-		if d.cacheDir != "" {
-			if err := os.MkdirAll(d.cacheDir, fs.FileMode(0755)); err != nil {
-				cache.err = multierror.Append(cache.err, err)
-			}
+		if err := os.MkdirAll(d.cacheDir, fs.FileMode(0755)); err != nil {
+			cache.err = multierror.Append(cache.err, err)
 		}
 
 		// Create data directory.
diff --git a/pkg/issuer/dragonfly.go b/pkg/issuer/dragonfly.go
index c41b363e..314980ee 100644
--- a/pkg/issuer/dragonfly.go
+++ b/pkg/issuer/dragonfly.go
@@ -19,18 +19,14 @@ package issuer
 import (
 	"context"
 	"crypto"
-	"crypto/ecdsa"
 	"crypto/rand"
-	"crypto/rsa"
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509/pkix"
-	"encoding/pem"
-	"errors"
-	"strings"
 	"time"
 
 	"github.com/johanbrandhorst/certify"
+	"google.golang.org/protobuf/types/known/durationpb"
 
 	securityv1 "d7y.io/api/pkg/apis/security/v1"
 
@@ -38,61 +34,81 @@ import (
 	managerclient "d7y.io/dragonfly/v2/pkg/rpc/manager/client"
 )
 
+var (
+	// defaultSubjectOrganization is default organization of subject.
+	defaultSubjectOrganization = []string{"Dragonfly"}
+
+	// defaultValidityPeriod is default validity period of certificate.
+	defaultValidityPeriod time.Duration = 180 * 24 * time.Hour
+)
+
+// dragonflyIssuer provides issuer function.
 type dragonflyIssuer struct {
-	client managerclient.Client
+	client         managerclient.Client
+	validityPeriod time.Duration
 }
 
-func NewDragonflyIssuer(client managerclient.Client) certify.Issuer {
-	return &dragonflyIssuer{client: client}
+// Option is a functional option for configuring the dragonflyIssuer.
+type Option func(i *dragonflyIssuer)
+
+// WithValidityPeriod set the validityPeriod for dragonflyIssuer.
+func WithValidityPeriod(d time.Duration) Option {
+	return func(i *dragonflyIssuer) {
+		i.validityPeriod = d
+	}
 }
 
-func (d *dragonflyIssuer) Issue(ctx context.Context, commonName string, certConfig *certify.CertConfig) (*tls.Certificate, error) {
-	csrPEM, privateKeyPEM, err := fromCertifyCertConfig(commonName, certConfig)
-	if err != nil {
-		logger.Errorf("gen csr and private pem error: %s", err.Error())
-		return nil, err
+// NewDragonflyIssuer returns a new certify.Issuer instence.
+func NewDragonflyIssuer(client managerclient.Client, opts ...Option) certify.Issuer {
+	i := &dragonflyIssuer{
+		client:         client,
+		validityPeriod: defaultValidityPeriod,
 	}
 
-	request := &securityv1.CertificateRequest{
-		Csr:              string(csrPEM),
-		ValidityDuration: 180 * 24 * int64(time.Hour.Seconds()),
+	for _, opt := range opts {
+		opt(i)
 	}
 
-	response, err := d.client.IssueCertificate(ctx, request)
+	return i
+}
+
+// Issue returns tls Certificate of issuing.
+func (i *dragonflyIssuer) Issue(ctx context.Context, commonName string, certConfig *certify.CertConfig) (*tls.Certificate, error) {
+	csr, privateKey, err := fromCertifyCertConfig(commonName, certConfig)
 	if err != nil {
-		logger.Errorf("call manager IssueCertificate error: %s", err.Error())
 		return nil, err
 	}
 
-	certPEM := []byte(strings.Join(response.CertificateChain, "\n"))
-	tlsCert, err := tls.X509KeyPair(certPEM, privateKeyPEM)
+	resp, err := i.client.IssueCertificate(ctx, &securityv1.CertificateRequest{
+		Csr:            csr,
+		ValidityPeriod: durationpb.New(i.validityPeriod),
+	})
 	if err != nil {
-		logger.Errorf("load x509 key pair error: %s", err.Error())
 		return nil, err
 	}
 
-	tlsCert.Leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])
+	leaf, err := x509.ParseCertificate(resp.CertificateChain[0])
 	if err != nil {
 		return nil, err
 	}
+	logger.Debugf("issue certificate from manager, common name: %s, issuer: %s", commonName, leaf.Issuer.CommonName)
 
-	return &tlsCert, nil
+	return &tls.Certificate{
+		Certificate: resp.CertificateChain,
+		PrivateKey:  privateKey,
+		Leaf:        leaf,
+	}, nil
 }
 
 // TODO this func is copied from internal package github.com/johanbrandhorst/certify@v1.9.0/internal/csr/csr.go
 // fromCertifyCertConfig creates a CSR and private key from the input config and common name.
 // It returns the CSR and private key in PEM format.
-func fromCertifyCertConfig(commonName string, conf *certify.CertConfig) ([]byte, []byte, error) {
+func fromCertifyCertConfig(commonName string, conf *certify.CertConfig) ([]byte, crypto.PrivateKey, error) {
 	pk, err := conf.KeyGenerator.Generate()
 	if err != nil {
 		return nil, nil, err
 	}
 
-	keyPEM, err := marshal(pk)
-	if err != nil {
-		return nil, nil, err
-	}
-
 	template := &x509.CertificateRequest{
 		Subject: pkix.Name{
 			CommonName:   commonName,
@@ -111,36 +127,5 @@ func fromCertifyCertConfig(commonName string, conf *certify.CertConfig) ([]byte,
 		return nil, nil, err
 	}
 
-	csrPEM := pem.EncodeToMemory(&pem.Block{
-		Type:  "CERTIFICATE REQUEST",
-		Bytes: csr,
-	})
-
-	return csrPEM, keyPEM, nil
-}
-
-// TODO this func is copied from internal package github.com/johanbrandhorst/certify@v1.9.0/internal/keys/keys.go
-// marshal a private key to PEM format.
-func marshal(pk crypto.PrivateKey) ([]byte, error) {
-	switch pk := pk.(type) {
-	case *rsa.PrivateKey:
-		keyBytes := x509.MarshalPKCS1PrivateKey(pk)
-		block := pem.Block{
-			Type:  "RSA PRIVATE KEY",
-			Bytes: keyBytes,
-		}
-		return pem.EncodeToMemory(&block), nil
-	case *ecdsa.PrivateKey:
-		keyBytes, err := x509.MarshalECPrivateKey(pk)
-		if err != nil {
-			return nil, err
-		}
-		block := pem.Block{
-			Type:  "EC PRIVATE KEY",
-			Bytes: keyBytes,
-		}
-		return pem.EncodeToMemory(&block), nil
-	}
-
-	return nil, errors.New("unsupported private key type")
+	return csr, pk, nil
 }
diff --git a/pkg/issuer/dragonfly_manager.go b/pkg/issuer/dragonfly_manager.go
index 5b652875..8b0602b6 100644
--- a/pkg/issuer/dragonfly_manager.go
+++ b/pkg/issuer/dragonfly_manager.go
@@ -25,83 +25,60 @@ import (
 	"crypto/x509/pkix"
 	"math/big"
 	"net"
-	"net/url"
 	"time"
 
 	"github.com/johanbrandhorst/certify"
 
-	"d7y.io/dragonfly/v2/pkg/net/ip"
+	logger "d7y.io/dragonfly/v2/internal/dflog"
 )
 
-var (
-	// defaultSubjectOrganization is default organization of subject.
-	defaultSubjectOrganization = []string{"Dragonfly"}
-
-	// defaultIPAddresses is default ip addresses of certificate.
-	defaultIPAddresses = []net.IP{net.ParseIP(ip.IPv4)}
-
-	// defaultDNSNames is default dns names of certificate.
-	defaultDNSNames = []string{"dragonfly-manager", "dragonfly-manager.dragonfly-system.svc"}
-
-	// defaultValidityDuration is default validity duration of certificate.
-	defaultValidityDuration = 10 * 365 * 24 * time.Hour
+const (
+	// defaultManagerValidityPeriod is default manager validity period of certificate.
+	defaultManagerValidityPeriod = 10 * 365 * 24 * time.Hour
 )
 
-// GC provides issuer function.
+// dragonflyManagerIssuer provides manager issuer function.
 type dragonflyManagerIssuer struct {
-	tlsCACert        *tls.Certificate
-	dnsNames         []string
-	emailAddresses   []string
-	ipAddresses      []net.IP
-	uris             []*url.URL
-	validityDuration time.Duration
+	tlsCACert      *tls.Certificate
+	dnsNames       []string
+	ipAddresses    []net.IP
+	validityPeriod time.Duration
 }
 
-// Option is a functional option for configuring the dragonflyManagerIssuer.
-type Option func(i *dragonflyManagerIssuer)
+// ManagerOption is a functional option for configuring the dragonflyManagerIssuer.
+type ManagerOption func(i *dragonflyManagerIssuer)
 
-// WithDNSNames set the dnsNames for dragonflyManagerIssuer.
-func WithDNSNames(dnsNames []string) Option {
+// WithManagerDNSNames set the manager dnsNames for dragonflyManagerIssuer.
+func WithManagerDNSNames(dnsNames []string) ManagerOption {
 	return func(i *dragonflyManagerIssuer) {
 		i.dnsNames = dnsNames
 	}
 }
 
-// WithEmailAddresses set the emailAddresses for dragonflyManagerIssuer.
-func WithEmailAddresses(emailAddrs []string) Option {
+// WithManagerIPAddresses set the manager ipAddresses for dragonflyManagerIssuer.
+func WithManagerIPAddresses(rawIPAddrs []string) ManagerOption {
 	return func(i *dragonflyManagerIssuer) {
-		i.emailAddresses = emailAddrs
-	}
-}
+		var ipAddrs []net.IP
+		for _, rawIPAddr := range rawIPAddrs {
+			ipAddrs = append(ipAddrs, net.ParseIP(rawIPAddr))
+		}
 
-// WithIPAddresses set the ipAddresses for dragonflyManagerIssuer.
-func WithIPAddresses(ipAddrs []net.IP) Option {
-	return func(i *dragonflyManagerIssuer) {
 		i.ipAddresses = ipAddrs
 	}
 }
 
-// WithURIs set the uris for dragonflyManagerIssuer.
-func WithURIs(uris []*url.URL) Option {
-	return func(i *dragonflyManagerIssuer) {
-		i.uris = uris
-	}
-}
-
-// WithValidityDuration set the validityDuration for dragonflyManagerIssuer.
-func WithValidityDuration(d time.Duration) Option {
+// WithManagerValidityPeriod set the manager validityPeriod for dragonflyManagerIssuer.
+func WithManagerValidityPeriod(d time.Duration) ManagerOption {
 	return func(i *dragonflyManagerIssuer) {
-		i.validityDuration = d
+		i.validityPeriod = d
 	}
 }
 
 // NewDragonflyManagerIssuer returns a new certify.Issuer instence.
-func NewDragonflyManagerIssuer(tlsCACert *tls.Certificate, opts ...Option) certify.Issuer {
+func NewDragonflyManagerIssuer(tlsCACert *tls.Certificate, opts ...ManagerOption) certify.Issuer {
 	i := &dragonflyManagerIssuer{
-		tlsCACert:        tlsCACert,
-		dnsNames:         defaultDNSNames,
-		ipAddresses:      defaultIPAddresses,
-		validityDuration: defaultValidityDuration,
+		tlsCACert:      tlsCACert,
+		validityPeriod: defaultManagerValidityPeriod,
 	}
 
 	for _, opt := range opts {
@@ -136,11 +113,10 @@ func (i *dragonflyManagerIssuer) Issue(ctx context.Context, commonName string, c
 			Organization: defaultSubjectOrganization,
 		},
 		DNSNames:              append(certConfig.SubjectAlternativeNames, i.dnsNames...),
-		EmailAddresses:        i.emailAddresses,
 		IPAddresses:           append(certConfig.IPSubjectAlternativeNames, i.ipAddresses...),
-		URIs:                  append(certConfig.URISubjectAlternativeNames, i.uris...),
+		URIs:                  certConfig.URISubjectAlternativeNames,
 		NotBefore:             now.Add(-10 * time.Minute).UTC(),
-		NotAfter:              now.Add(i.validityDuration).UTC(),
+		NotAfter:              now.Add(i.validityPeriod).UTC(),
 		BasicConstraintsValid: true,
 		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment | x509.KeyUsageKeyEncipherment,
 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
@@ -155,6 +131,7 @@ func (i *dragonflyManagerIssuer) Issue(ctx context.Context, commonName string, c
 	if err != nil {
 		return nil, err
 	}
+	logger.Debugf("issue certificate from manager, common name: %s, issuer: %s", commonName, leaf.Issuer.CommonName)
 
 	return &tls.Certificate{
 		Certificate: append([][]byte{cert}, i.tlsCACert.Certificate...),
diff --git a/pkg/rpc/credential.go b/pkg/rpc/credential.go
index 0be133de..6b1e38e1 100644
--- a/pkg/rpc/credential.go
+++ b/pkg/rpc/credential.go
@@ -43,7 +43,12 @@ const (
 )
 
 // NewServerCredentialsByCertify returns server transport credentials by certify.
-func NewServerCredentialsByCertify(tlsPolicy string, tlsVerify bool, clientCAs [][]byte, certifyClient *certify.Certify) (credentials.TransportCredentials, error) {
+func NewServerCredentialsByCertify(tlsPolicy string, tlsVerify bool, pemClientCAs []byte, certifyClient *certify.Certify) (credentials.TransportCredentials, error) {
+	certPool := x509.NewCertPool()
+	if !certPool.AppendCertsFromPEM(pemClientCAs) {
+		return nil, errors.New("invalid CA Cert")
+	}
+
 	tlsConfig := &tls.Config{
 		GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
 			if hello.ServerName == "" {
@@ -52,6 +57,11 @@ func NewServerCredentialsByCertify(tlsPolicy string, tlsVerify bool, clientCAs [
 
 			return certifyClient.GetCertificate(hello)
 		},
+		ClientCAs: certPool,
+	}
+
+	if tlsVerify {
+		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
 	}
 
 	switch tlsPolicy {
@@ -59,19 +69,6 @@ func NewServerCredentialsByCertify(tlsPolicy string, tlsVerify bool, clientCAs [
 		return NewMuxTransportCredentials(tlsConfig,
 			WithTLSPreferClientHandshake(tlsPolicy == PreferTLSPolicy)), nil
 	case ForceTLSPolicy:
-		if !tlsVerify {
-			return credentials.NewTLS(tlsConfig), nil
-		}
-
-		certPool := x509.NewCertPool()
-		for _, cert := range clientCAs {
-			if !certPool.AppendCertsFromPEM(cert) {
-				return nil, errors.New("invalid CA Cert")
-			}
-		}
-
-		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
-		tlsConfig.ClientCAs = certPool
 		return credentials.NewTLS(tlsConfig), nil
 	default:
 		return nil, fmt.Errorf("invalid tlsPolicy: %s", tlsPolicy)
@@ -79,13 +76,15 @@ func NewServerCredentialsByCertify(tlsPolicy string, tlsVerify bool, clientCAs [
 }
 
 // NewClientCredentialsByCertify returns client transport credentials by certify.
-func NewClientCredentialsByCertify(tlsPolicy string, tlsVerify bool, rootCAs [][]byte, certifyClient *certify.Certify) (credentials.TransportCredentials, error) {
-	tlsConfig := &tls.Config{
-		GetClientCertificate: certifyClient.GetClientCertificate,
+func NewClientCredentialsByCertify(tlsPolicy string, pemRootCAs []byte, certifyClient *certify.Certify) (credentials.TransportCredentials, error) {
+	certPool := x509.NewCertPool()
+	if !certPool.AppendCertsFromPEM(pemRootCAs) {
+		return nil, errors.New("invalid CA Cert")
 	}
 
-	if !tlsVerify {
-		tlsConfig.InsecureSkipVerify = true
+	tlsConfig := &tls.Config{
+		GetClientCertificate: certifyClient.GetClientCertificate,
+		RootCAs:              certPool,
 	}
 
 	switch tlsPolicy {
@@ -93,14 +92,6 @@ func NewClientCredentialsByCertify(tlsPolicy string, tlsVerify bool, rootCAs [][
 		return NewMuxTransportCredentials(tlsConfig,
 			WithTLSPreferClientHandshake(tlsPolicy == PreferTLSPolicy)), nil
 	case ForceTLSPolicy:
-		certPool := x509.NewCertPool()
-		for _, cert := range rootCAs {
-			if !certPool.AppendCertsFromPEM(cert) {
-				return nil, errors.New("invalid CA Cert")
-			}
-		}
-
-		tlsConfig.RootCAs = certPool
 		return credentials.NewTLS(tlsConfig), nil
 	default:
 		return nil, fmt.Errorf("invalid tlsPolicy: %s", tlsPolicy)
@@ -108,10 +99,14 @@ func NewClientCredentialsByCertify(tlsPolicy string, tlsVerify bool, rootCAs [][
 }
 
 // NewClientCredentials returns client transport credentials.
-func NewClientCredentials(tlsPolicy string, tlsVerify bool, certs []tls.Certificate, rootCAs [][]byte) (credentials.TransportCredentials, error) {
-	tlsConfig := &tls.Config{}
-	if !tlsVerify {
-		tlsConfig.InsecureSkipVerify = true
+func NewClientCredentials(tlsPolicy string, certs []tls.Certificate, pemRootCAs []byte) (credentials.TransportCredentials, error) {
+	certPool := x509.NewCertPool()
+	if !certPool.AppendCertsFromPEM(pemRootCAs) {
+		return nil, errors.New("invalid CA Cert")
+	}
+
+	tlsConfig := &tls.Config{
+		RootCAs: certPool,
 	}
 
 	if len(certs) > 0 {
@@ -123,14 +118,6 @@ func NewClientCredentials(tlsPolicy string, tlsVerify bool, certs []tls.Certific
 		return NewMuxTransportCredentials(tlsConfig,
 			WithTLSPreferClientHandshake(tlsPolicy == PreferTLSPolicy)), nil
 	case ForceTLSPolicy:
-		certPool := x509.NewCertPool()
-		for _, cert := range rootCAs {
-			if !certPool.AppendCertsFromPEM(cert) {
-				return nil, errors.New("invalid CA Cert")
-			}
-		}
-
-		tlsConfig.RootCAs = certPool
 		return credentials.NewTLS(tlsConfig), nil
 	default:
 		return nil, fmt.Errorf("invalid tlsPolicy: %s", tlsPolicy)
diff --git a/pkg/types/constants.go b/pkg/types/constants.go
new file mode 100644
index 00000000..56f1f69a
--- /dev/null
+++ b/pkg/types/constants.go
@@ -0,0 +1,65 @@
+/*
+ *     Copyright 2022 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package types
+
+const (
+	// ManagerName is name of manager.
+	ManagerName = "manager"
+
+	// SchedulerName is name of scheduler.
+	SchedulerName = "scheduler"
+
+	// DfdaemonName is name of dfdaemon.
+	DfdaemonName = "dfdaemon"
+
+	// DaemonName is daemon name of dfdaemon.
+	DaemonName = "daemon"
+
+	// DfgetName is dfget name of dfdaemon.
+	DfgetName = "dfget"
+
+	// DfcacheName is dfcache name of dfdaemon.
+	DfcacheName = "dfcache"
+
+	// DfstoreName is dfstore name of dfdaemon.
+	DfstoreName = "dfstore"
+)
+
+const (
+	// SourcePattern is back-to-source download pattern.
+	SourcePattern = "source"
+
+	// SeedPeerPattern is seed peer download pattern.
+	SeedPeerPattern = "seed-peer"
+
+	// P2PPattern is p2p download pattern.
+	P2PPattern = "p2p"
+)
+
+const (
+	// MetricsNamespace is namespace of metrics.
+	MetricsNamespace = "dragonfly"
+
+	// ManagerMetricsName is name of manager metrics.
+	ManagerMetricsName = "manager"
+
+	// SchedulerMetricsName is name of scheduelr metrics.
+	SchedulerMetricsName = "scheduler"
+
+	// DfdaemonMetricsName is name of dfdaemon metrics.
+	DfdaemonMetricsName = "dfdaemon"
+)
diff --git a/pkg/serialize/pemcontent.go b/pkg/types/types.go
similarity index 98%
rename from pkg/serialize/pemcontent.go
rename to pkg/types/types.go
index 5a728b40..72bf626d 100644
--- a/pkg/serialize/pemcontent.go
+++ b/pkg/types/types.go
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package serialize
+package types
 
 import (
 	"encoding/json"
diff --git a/scheduler/config/config.go b/scheduler/config/config.go
index d6cb3360..fe55d9d4 100644
--- a/scheduler/config/config.go
+++ b/scheduler/config/config.go
@@ -24,7 +24,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/net/fqdn"
 	"d7y.io/dragonfly/v2/pkg/net/ip"
 	"d7y.io/dragonfly/v2/pkg/rpc"
-	"d7y.io/dragonfly/v2/pkg/serialize"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/scheduler/storage"
 )
 
@@ -63,6 +63,209 @@ type Config struct {
 	Security *SecurityConfig `yaml:"security" mapstructure:"security"`
 }
 
+type ServerConfig struct {
+	// Server ip.
+	IP string `yaml:"ip" mapstructure:"ip"`
+
+	// Server hostname.
+	Host string `yaml:"host" mapstructure:"host"`
+
+	// Listen stands listen interface, like: 0.0.0.0, 192.168.0.1.
+	Listen string `yaml:"listen" mapstructure:"listen"`
+
+	// Server port.
+	Port int `yaml:"port" mapstructure:"port"`
+
+	// Server work directory.
+	WorkHome string `yaml:"workHome" mapstructure:"workHome"`
+
+	// Server dynamic config cache directory.
+	CacheDir string `yaml:"cacheDir" mapstructure:"cacheDir"`
+
+	// Server log directory.
+	LogDir string `yaml:"logDir" mapstructure:"logDir"`
+
+	// Server storage data directory.
+	DataDir string `yaml:"dataDir" mapstructure:"dataDir"`
+}
+
+type SchedulerConfig struct {
+	// Scheduling algorithm used by the scheduler.
+	Algorithm string `yaml:"algorithm" mapstructure:"algorithm"`
+
+	// Single task allows the client to back-to-source count.
+	BackSourceCount int `yaml:"backSourceCount" mapstructure:"backSourceCount"`
+
+	// Retry scheduling back-to-source limit times.
+	RetryBackSourceLimit int `yaml:"retryBackSourceLimit" mapstructure:"retryBackSourceLimit"`
+
+	// Retry scheduling limit times.
+	RetryLimit int `yaml:"retryLimit" mapstructure:"retryLimit"`
+
+	// Retry scheduling interval.
+	RetryInterval time.Duration `yaml:"retryInterval" mapstructure:"retryInterval"`
+
+	// Task and peer gc configuration.
+	GC *GCConfig `yaml:"gc" mapstructure:"gc"`
+
+	// Training configuration.
+	Training *TrainingConfig `yaml:"training" mapstructure:"training"`
+}
+
+type TrainingConfig struct {
+	// Enable training.
+	Enable bool `yaml:"enable" mapstructure:"enable"`
+
+	// Enable auto refresh model.
+	EnableAutoRefresh bool `yaml:"enableAutoRefresh" mapstructure:"enableAutoRefresh"`
+
+	// RefreshModelInterval is refresh interval for refreshing model.
+	RefreshModelInterval time.Duration `yaml:"refreshModelInterval" mapstructure:"refreshModelInterval"`
+
+	// CPU limit while training.
+	CPU int `yaml:"cpu" mapstructure:"cpu"`
+}
+
+type GCConfig struct {
+	// Peer gc interval.
+	PeerGCInterval time.Duration `yaml:"peerGCInterval" mapstructure:"peerGCInterval"`
+
+	// Peer time to live.
+	PeerTTL time.Duration `yaml:"peerTTL" mapstructure:"peerTTL"`
+
+	// Task gc interval.
+	TaskGCInterval time.Duration `yaml:"taskGCInterval" mapstructure:"taskGCInterval"`
+
+	// Task time to live.
+	TaskTTL time.Duration `yaml:"taskTTL" mapstructure:"taskTTL"`
+
+	// Host gc interval.
+	HostGCInterval time.Duration `yaml:"hostGCInterval" mapstructure:"hostGCInterval"`
+
+	// Host time to live.
+	HostTTL time.Duration `yaml:"hostTTL" mapstructure:"hostTTL"`
+}
+
+type DynConfig struct {
+	// RefreshInterval is refresh interval for manager cache.
+	RefreshInterval time.Duration `yaml:"refreshInterval" mapstructure:"refreshInterval"`
+}
+
+type HostConfig struct {
+	// IDC for scheduler.
+	IDC string `mapstructure:"idc" yaml:"idc"`
+
+	// NetTopology for scheduler.
+	NetTopology string `mapstructure:"netTopology" yaml:"netTopology"`
+
+	// Location for scheduler.
+	Location string `mapstructure:"location" yaml:"location"`
+}
+
+type ManagerConfig struct {
+	// Addr is manager address.
+	Addr string `yaml:"addr" mapstructure:"addr"`
+
+	// SchedulerClusterID is scheduler cluster id.
+	SchedulerClusterID uint `yaml:"schedulerClusterID" mapstructure:"schedulerClusterID"`
+
+	// KeepAlive configuration.
+	KeepAlive KeepAliveConfig `yaml:"keepAlive" mapstructure:"keepAlive"`
+}
+
+type SeedPeerConfig struct {
+	// Enable is to enable seed peer as P2P peer.
+	Enable bool `yaml:"enable" mapstructure:"enable"`
+}
+
+type KeepAliveConfig struct {
+	// Keep alive interval.
+	Interval time.Duration `yaml:"interval" mapstructure:"interval"`
+}
+
+type JobConfig struct {
+	// Enable job service.
+	Enable bool `yaml:"enable" mapstructure:"enable"`
+
+	// Number of workers in global queue.
+	GlobalWorkerNum uint `yaml:"globalWorkerNum" mapstructure:"globalWorkerNum"`
+
+	// Number of workers in scheduler queue.
+	SchedulerWorkerNum uint `yaml:"schedulerWorkerNum" mapstructure:"schedulerWorkerNum"`
+
+	// Number of workers in local queue.
+	LocalWorkerNum uint `yaml:"localWorkerNum" mapstructure:"localWorkerNum"`
+
+	// Redis configuration.
+	Redis *RedisConfig `yaml:"redis" mapstructure:"redis"`
+}
+
+type StorageConfig struct {
+	// MaxSize sets the maximum size in megabytes of storage file.
+	MaxSize int `yaml:"maxSize" mapstructure:"maxSize"`
+
+	// MaxBackups sets the maximum number of storage files to retain.
+	MaxBackups int `yaml:"maxBackups" mapstructure:"maxBackups"`
+
+	// BufferSize sets the size of buffer container,
+	// if the buffer is full, write all the records in the buffer to the file.
+	BufferSize int `yaml:"bufferSize" mapstructure:"bufferSize"`
+}
+
+type RedisConfig struct {
+	// Server hostname.
+	Host string `yaml:"host" mapstructure:"host"`
+
+	// Server port.
+	Port int `yaml:"port" mapstructure:"port"`
+
+	// Server password.
+	Password string `yaml:"password" mapstructure:"password"`
+
+	// Broker database name.
+	BrokerDB int `yaml:"brokerDB" mapstructure:"brokerDB"`
+
+	// Backend database name.
+	BackendDB int `yaml:"backendDB" mapstructure:"backendDB"`
+}
+
+type MetricsConfig struct {
+	// Enable metrics service.
+	Enable bool `yaml:"enable" mapstructure:"enable"`
+
+	// Metrics service address.
+	Addr string `yaml:"addr" mapstructure:"addr"`
+
+	// Enable peer host metrics.
+	EnablePeerHost bool `yaml:"enablePeerHost" mapstructure:"enablePeerHost"`
+}
+
+type SecurityConfig struct {
+	// AutoIssueCert indicates to issue client certificates for all grpc call
+	// if AutoIssueCert is false, any other option in Security will be ignored.
+	AutoIssueCert bool `mapstructure:"autoIssueCert" yaml:"autoIssueCert"`
+
+	// CACert is the root CA certificate for all grpc tls handshake, it can be path or PEM format string.
+	CACert types.PEMContent `mapstructure:"caCert" yaml:"caCert"`
+
+	// TLSVerify indicates to verify client certificates.
+	TLSVerify bool `mapstructure:"tlsVerify" yaml:"tlsVerify"`
+
+	// TLSPolicy controls the grpc shandshake behaviors:
+	// force: both ClientHandshake and ServerHandshake are only support tls.
+	// prefer: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support tls.
+	// default: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls).
+	TLSPolicy string `mapstructure:"tlsPolicy" yaml:"tlsPolicy"`
+
+	// CertSpec is the desired state of certificate.
+	CertSpec *CertSpec `mapstructure:"certSpec" yaml:"certSpec"`
+}
+
+type CertSpec struct {
+	// ValidityPeriod is the validity period of certificate.
+	ValidityPeriod time.Duration `mapstructure:"validityPeriod" yaml:"validityPeriod"`
+}
+
 // New default configuration.
 func New() *Config {
 	return &Config{
@@ -130,7 +333,10 @@ func New() *Config {
 		Security: &SecurityConfig{
 			AutoIssueCert: false,
 			TLSVerify:     true,
-			TLSPolicy:     rpc.DefaultTLSPolicy,
+			TLSPolicy:     rpc.PreferTLSPolicy,
+			CertSpec: &CertSpec{
+				ValidityPeriod: DefaultCertValidityPeriod,
+			},
 		},
 	}
 }
@@ -271,202 +477,15 @@ func (cfg *Config) Validate() error {
 		if cfg.Security.CACert == "" {
 			return errors.New("security requires parameter caCert")
 		}
-	}
-
-	return nil
-}
-
-type ServerConfig struct {
-	// Server ip.
-	IP string `yaml:"ip" mapstructure:"ip"`
-
-	// Server hostname.
-	Host string `yaml:"host" mapstructure:"host"`
-
-	// Listen stands listen interface, like: 0.0.0.0, 192.168.0.1.
-	Listen string `yaml:"listen" mapstructure:"listen"`
-
-	// Server port.
-	Port int `yaml:"port" mapstructure:"port"`
-
-	// Server work directory.
-	WorkHome string `yaml:"workHome" mapstructure:"workHome"`
-
-	// Server dynamic config cache directory.
-	CacheDir string `yaml:"cacheDir" mapstructure:"cacheDir"`
-
-	// Server log directory.
-	LogDir string `yaml:"logDir" mapstructure:"logDir"`
-
-	// Server storage data directory.
-	DataDir string `yaml:"dataDir" mapstructure:"dataDir"`
-}
-
-type SchedulerConfig struct {
-	// Scheduling algorithm used by the scheduler.
-	Algorithm string `yaml:"algorithm" mapstructure:"algorithm"`
-
-	// Single task allows the client to back-to-source count.
-	BackSourceCount int `yaml:"backSourceCount" mapstructure:"backSourceCount"`
-
-	// Retry scheduling back-to-source limit times.
-	RetryBackSourceLimit int `yaml:"retryBackSourceLimit" mapstructure:"retryBackSourceLimit"`
-
-	// Retry scheduling limit times.
-	RetryLimit int `yaml:"retryLimit" mapstructure:"retryLimit"`
-
-	// Retry scheduling interval.
-	RetryInterval time.Duration `yaml:"retryInterval" mapstructure:"retryInterval"`
-
-	// Task and peer gc configuration.
-	GC *GCConfig `yaml:"gc" mapstructure:"gc"`
-
-	// Training configuration.
-	Training *TrainingConfig `yaml:"training" mapstructure:"training"`
-}
-
-type TrainingConfig struct {
-	// Enable training.
-	Enable bool `yaml:"enable" mapstructure:"enable"`
-
-	// Enable auto refresh model.
-	EnableAutoRefresh bool `yaml:"enableAutoRefresh" mapstructure:"enableAutoRefresh"`
-
-	// RefreshModelInterval is refresh interval for refreshing model.
-	RefreshModelInterval time.Duration `yaml:"refreshModelInterval" mapstructure:"refreshModelInterval"`
-
-	// CPU limit while training.
-	CPU int `yaml:"cpu" mapstructure:"cpu"`
-}
-
-type GCConfig struct {
-	// Peer gc interval.
-	PeerGCInterval time.Duration `yaml:"peerGCInterval" mapstructure:"peerGCInterval"`
-
-	// Peer time to live.
-	PeerTTL time.Duration `yaml:"peerTTL" mapstructure:"peerTTL"`
-
-	// Task gc interval.
-	TaskGCInterval time.Duration `yaml:"taskGCInterval" mapstructure:"taskGCInterval"`
-
-	// Task time to live.
-	TaskTTL time.Duration `yaml:"taskTTL" mapstructure:"taskTTL"`
-
-	// Host gc interval.
-	HostGCInterval time.Duration `yaml:"hostGCInterval" mapstructure:"hostGCInterval"`
-
-	// Host time to live.
-	HostTTL time.Duration `yaml:"hostTTL" mapstructure:"hostTTL"`
-}
-
-type DynConfig struct {
-	// RefreshInterval is refresh interval for manager cache.
-	RefreshInterval time.Duration `yaml:"refreshInterval" mapstructure:"refreshInterval"`
-}
-
-type HostConfig struct {
-	// IDC for scheduler.
-	IDC string `mapstructure:"idc" yaml:"idc"`
-
-	// NetTopology for scheduler.
-	NetTopology string `mapstructure:"netTopology" yaml:"netTopology"`
-
-	// Location for scheduler.
-	Location string `mapstructure:"location" yaml:"location"`
-}
-
-type ManagerConfig struct {
-	// Addr is manager address.
-	Addr string `yaml:"addr" mapstructure:"addr"`
-
-	// SchedulerClusterID is scheduler cluster id.
-	SchedulerClusterID uint `yaml:"schedulerClusterID" mapstructure:"schedulerClusterID"`
-
-	// KeepAlive configuration.
-	KeepAlive KeepAliveConfig `yaml:"keepAlive" mapstructure:"keepAlive"`
-}
-
-type SeedPeerConfig struct {
-	// Enable is to enable seed peer as P2P peer.
-	Enable bool `yaml:"enable" mapstructure:"enable"`
-}
-
-type KeepAliveConfig struct {
-	// Keep alive interval.
-	Interval time.Duration `yaml:"interval" mapstructure:"interval"`
-}
-
-type JobConfig struct {
-	// Enable job service.
-	Enable bool `yaml:"enable" mapstructure:"enable"`
-
-	// Number of workers in global queue.
-	GlobalWorkerNum uint `yaml:"globalWorkerNum" mapstructure:"globalWorkerNum"`
-
-	// Number of workers in scheduler queue.
-	SchedulerWorkerNum uint `yaml:"schedulerWorkerNum" mapstructure:"schedulerWorkerNum"`
-
-	// Number of workers in local queue.
-	LocalWorkerNum uint `yaml:"localWorkerNum" mapstructure:"localWorkerNum"`
-
-	// Redis configuration.
-	Redis *RedisConfig `yaml:"redis" mapstructure:"redis"`
-}
-
-type StorageConfig struct {
-	// MaxSize sets the maximum size in megabytes of storage file.
-	MaxSize int `yaml:"maxSize" mapstructure:"maxSize"`
-
-	// MaxBackups sets the maximum number of storage files to retain.
-	MaxBackups int `yaml:"maxBackups" mapstructure:"maxBackups"`
-
-	// BufferSize sets the size of buffer container,
-	// if the buffer is full, write all the records in the buffer to the file.
-	BufferSize int `yaml:"bufferSize" mapstructure:"bufferSize"`
-}
-
-type RedisConfig struct {
-	// Server hostname.
-	Host string `yaml:"host" mapstructure:"host"`
-
-	// Server port.
-	Port int `yaml:"port" mapstructure:"port"`
 
-	// Server password.
-	Password string `yaml:"password" mapstructure:"password"`
-
-	// Broker database name.
-	BrokerDB int `yaml:"brokerDB" mapstructure:"brokerDB"`
-
-	// Backend database name.
-	BackendDB int `yaml:"backendDB" mapstructure:"backendDB"`
-}
-
-type MetricsConfig struct {
-	// Enable metrics service.
-	Enable bool `yaml:"enable" mapstructure:"enable"`
-
-	// Metrics service address.
-	Addr string `yaml:"addr" mapstructure:"addr"`
-
-	// Enable peer host metrics.
-	EnablePeerHost bool `yaml:"enablePeerHost" mapstructure:"enablePeerHost"`
-}
-
-type SecurityConfig struct {
-	// AutoIssueCert indicates to issue client certificates for all grpc call
-	// if AutoIssueCert is false, any other option in Security will be ignored.
-	AutoIssueCert bool `mapstructure:"autoIssueCert" yaml:"autoIssueCert"`
-
-	// CACert is the root CA certificate for all grpc tls handshake, it can be path or PEM format string.
-	CACert serialize.PEMContent `mapstructure:"caCert" yaml:"caCert"`
+		if cfg.Security.CertSpec == nil {
+			return errors.New("security requires parameter certSpec")
+		}
 
-	// TLSPrefer indicates to verify client certificates for grpc ServerHandshake.
-	TLSVerify bool `mapstructure:"tlsVerify" yaml:"tlsVerify"`
+		if cfg.Security.CertSpec.ValidityPeriod <= 0 {
+			return errors.New("certSpec requires parameter validityPeriod")
+		}
+	}
 
-	// TLSPolicy controls the grpc shandshake behaviors:
-	// force: both ClientHandshake and ServerHandshake are only support tls.
-	// prefer: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support tls.
-	// default or empty: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls).
-	TLSPolicy string `mapstructure:"tlsPolicy" yaml:"tlsPolicy"`
+	return nil
 }
diff --git a/scheduler/config/constants.go b/scheduler/config/constants.go
index 56d1697e..767ce181 100644
--- a/scheduler/config/constants.go
+++ b/scheduler/config/constants.go
@@ -124,3 +124,8 @@ const (
 	// DefaultMetricsAddr is default address for metrics server.
 	DefaultMetricsAddr = ":8000"
 )
+
+var (
+	// DefaultCertValidityPeriod is default validity period of certificate.
+	DefaultCertValidityPeriod = 180 * 24 * time.Hour
+)
diff --git a/scheduler/config/dynconfig.go b/scheduler/config/dynconfig.go
index 8ad125d5..2d5b69f0 100644
--- a/scheduler/config/dynconfig.go
+++ b/scheduler/config/dynconfig.go
@@ -39,7 +39,7 @@ import (
 
 var (
 	// Cache filename.
-	cacheFileName = "scheduler_dynconfig"
+	cacheFileName = "scheduler"
 
 	// Notify observer interval.
 	watchInterval = 10 * time.Second
@@ -185,17 +185,18 @@ func (d *dynconfig) GetResolveSeedPeerAddrs() ([]resolver.Address, error) {
 		r := reachable.New(&reachable.Config{Address: addr})
 		if err := r.Check(); err != nil {
 			logger.Warnf("seed peer address %s is unreachable", addr)
-		} else {
-			if addrs[addr] {
-				continue
-			}
+			continue
+		}
 
-			resolveAddrs = append(resolveAddrs, resolver.Address{
-				ServerName: seedPeer.IP,
-				Addr:       addr,
-			})
-			addrs[addr] = true
+		if addrs[addr] {
+			continue
 		}
+
+		resolveAddrs = append(resolveAddrs, resolver.Address{
+			ServerName: seedPeer.IP,
+			Addr:       addr,
+		})
+		addrs[addr] = true
 	}
 
 	return resolveAddrs, nil
diff --git a/scheduler/metrics/metrics.go b/scheduler/metrics/metrics.go
index cceb8a87..5baecbe8 100644
--- a/scheduler/metrics/metrics.go
+++ b/scheduler/metrics/metrics.go
@@ -25,7 +25,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"google.golang.org/grpc"
 
-	"d7y.io/dragonfly/v2/internal/constants"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/scheduler/config"
 )
 
@@ -52,114 +52,114 @@ var (
 // Variables declared for metrics.
 var (
 	RegisterPeerTaskCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "register_peer_task_total",
 		Help:      "Counter of the number of the register peer task.",
 	}, []string{"tag", "app"})
 
 	RegisterPeerTaskFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "register_peer_task_failure_total",
 		Help:      "Counter of the number of failed of the register peer task.",
 	}, []string{"tag", "app"})
 
 	DownloadCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "download_total",
 		Help:      "Counter of the number of the downloading.",
 	}, []string{"tag", "app"})
 
 	DownloadFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "download_failure_total",
 		Help:      "Counter of the number of failed of the downloading.",
 	}, []string{"tag", "app", "type"})
 
 	StatTaskCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "stat_task_total",
 		Help:      "Counter of the number of the stat task.",
 	})
 
 	StatTaskFailureCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "stat_task_failure_total",
 		Help:      "Counter of the number of failed of the stat task.",
 	})
 
 	AnnounceCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "announce_task_total",
 		Help:      "Counter of the number of the announce task.",
 	})
 
 	AnnounceFailureCount = promauto.NewCounter(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "announce_task_failure_total",
 		Help:      "Counter of the number of failed of the announce task.",
 	})
 
 	LeaveTaskCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "leave_task_total",
 		Help:      "Counter of the number of the leaving task.",
 	}, []string{"tag", "app"})
 
 	LeaveTaskFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "leave_task_failure_total",
 		Help:      "Counter of the number of failed of the leaving task.",
 	}, []string{"tag", "app"})
 
 	Traffic = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "traffic",
 		Help:      "Counter of the number of traffic.",
 	}, []string{"tag", "app", "type"})
 
 	PeerHostTraffic = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "peer_host_traffic",
 		Help:      "Counter of the number of per peer host traffic.",
 	}, []string{"tag", "app", "traffic_type", "peer_host_id", "peer_host_ip"})
 
 	PeerTaskCounter = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "peer_task_total",
 		Help:      "Counter of the number of peer task.",
 	}, []string{"tag", "app", "type"})
 
 	PeerTaskSourceErrorCounter = promauto.NewCounterVec(prometheus.CounterOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "peer_task_source_error_total",
 		Help:      "Counter of the source error code number of peer task.",
 	}, []string{"tag", "app", "protocol", "code"})
 
 	PeerTaskDownloadDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "peer_task_download_duration_milliseconds",
 		Help:      "Histogram of the time each peer task downloading.",
 		Buckets:   []float64{100, 200, 500, 1000, 1500, 2 * 1000, 3 * 1000, 5 * 1000, 10 * 1000, 20 * 1000, 60 * 1000, 120 * 1000, 300 * 1000},
 	}, []string{"tag", "app"})
 
 	ConcurrentScheduleGauge = promauto.NewGauge(prometheus.GaugeOpts{
-		Namespace: constants.MetricsNamespace,
-		Subsystem: constants.SchedulerMetricsName,
+		Namespace: types.MetricsNamespace,
+		Subsystem: types.SchedulerMetricsName,
 		Name:      "concurrent_schedule_total",
 		Help:      "Gauge of the number of concurrent of the scheduling.",
 	})
diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go
index 04bfd12f..513b44f2 100644
--- a/scheduler/scheduler.go
+++ b/scheduler/scheduler.go
@@ -18,10 +18,11 @@ package scheduler
 
 import (
 	"context"
+	"crypto/tls"
 	"fmt"
 	"net"
 	"net/http"
-	"path"
+	"path/filepath"
 	"time"
 
 	"github.com/johanbrandhorst/certify"
@@ -32,6 +33,7 @@ import (
 	managerv1 "d7y.io/api/pkg/apis/manager/v1"
 
 	logger "d7y.io/dragonfly/v2/internal/dflog"
+	"d7y.io/dragonfly/v2/internal/dynconfig"
 	"d7y.io/dragonfly/v2/pkg/cache"
 	"d7y.io/dragonfly/v2/pkg/dfpath"
 	"d7y.io/dragonfly/v2/pkg/gc"
@@ -39,6 +41,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/net/ip"
 	"d7y.io/dragonfly/v2/pkg/rpc"
 	managerclient "d7y.io/dragonfly/v2/pkg/rpc/manager/client"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/scheduler/config"
 	"d7y.io/dragonfly/v2/scheduler/job"
 	"d7y.io/dragonfly/v2/scheduler/metrics"
@@ -87,7 +90,7 @@ func New(ctx context.Context, cfg *config.Config, d dfpath.Dfpath) (*Server, err
 	// Initialize manager client and dial options of manager grpc client.
 	managerDialOptions := []grpc.DialOption{}
 	if cfg.Security.AutoIssueCert {
-		clientTransportCredentials, err := rpc.NewClientCredentials(cfg.Security.TLSPolicy, cfg.Security.TLSVerify, nil, [][]byte{[]byte(cfg.Security.CACert)})
+		clientTransportCredentials, err := rpc.NewClientCredentials(cfg.Security.TLSPolicy, nil, []byte(cfg.Security.CACert))
 		if err != nil {
 			return nil, err
 		}
@@ -117,7 +120,7 @@ func New(ctx context.Context, cfg *config.Config, d dfpath.Dfpath) (*Server, err
 	}
 
 	// Initialize dynconfig client.
-	dynconfig, err := config.NewDynconfig(s.managerClient, d.CacheDir(), cfg)
+	dynconfig, err := config.NewDynconfig(s.managerClient, filepath.Join(d.CacheDir(), dynconfig.CacheDirName), cfg)
 	if err != nil {
 		return nil, err
 	}
@@ -131,21 +134,29 @@ func New(ctx context.Context, cfg *config.Config, d dfpath.Dfpath) (*Server, err
 	if cfg.Security.AutoIssueCert {
 		certifyClient = &certify.Certify{
 			CommonName:   ip.IPv4,
-			Issuer:       issuer.NewDragonflyIssuer(managerClient),
+			Issuer:       issuer.NewDragonflyIssuer(managerClient, issuer.WithValidityPeriod(cfg.Security.CertSpec.ValidityPeriod)),
 			RenewBefore:  time.Hour,
 			CertConfig:   nil,
 			IssueTimeout: 0,
 			Logger:       zapadapter.New(logger.CoreLogger.Desugar()),
 			Cache: cache.NewCertifyMutliCache(
 				certify.NewMemCache(),
-				certify.DirCache(path.Join(d.CacheDir(), cache.SchedulerCertifyCacheDirName))),
+				certify.DirCache(filepath.Join(d.CacheDir(), cache.CertifyCacheDirName, types.SchedulerName))),
+		}
+
+		// Issue a certificate to reduce first time delay.
+		if _, err := certifyClient.GetCertificate(&tls.ClientHelloInfo{
+			ServerName: ip.IPv4,
+		}); err != nil {
+			logger.Errorf("issue certificate error: %s", err.Error())
+			return nil, err
 		}
 	}
 
 	// Initialize resource and dial options of seed peer grpc client.
 	seedPeerDialOptions := []grpc.DialOption{}
 	if certifyClient != nil {
-		clientTransportCredentials, err := rpc.NewClientCredentialsByCertify(cfg.Security.TLSPolicy, cfg.Security.TLSVerify, [][]byte{[]byte(cfg.Security.CACert)}, certifyClient)
+		clientTransportCredentials, err := rpc.NewClientCredentialsByCertify(cfg.Security.TLSPolicy, []byte(cfg.Security.CACert), certifyClient)
 		if err != nil {
 			return nil, err
 		}
@@ -176,7 +187,7 @@ func New(ctx context.Context, cfg *config.Config, d dfpath.Dfpath) (*Server, err
 	// Initialize grpc service and server options of scheduler grpc server.
 	schedulerServerOptions := []grpc.ServerOption{}
 	if certifyClient != nil {
-		serverTransportCredentials, err := rpc.NewServerCredentialsByCertify(cfg.Security.TLSPolicy, cfg.Security.TLSVerify, [][]byte{[]byte(cfg.Security.CACert)}, certifyClient)
+		serverTransportCredentials, err := rpc.NewServerCredentialsByCertify(cfg.Security.TLSPolicy, cfg.Security.TLSVerify, []byte(cfg.Security.CACert), certifyClient)
 		if err != nil {
 			return nil, err
 		}
diff --git a/version/version.go b/version/version.go
index 47be4ea2..7215169a 100644
--- a/version/version.go
+++ b/version/version.go
@@ -22,7 +22,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
 
-	"d7y.io/dragonfly/v2/internal/constants"
+	"d7y.io/dragonfly/v2/pkg/types"
 )
 
 var (
@@ -39,7 +39,7 @@ var (
 
 var (
 	versionGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: constants.MetricsNamespace,
+		Namespace: types.MetricsNamespace,
 		Name:      "version",
 		Help:      "Version info of dragonfly components.",
 	}, []string{"major", "minor", "git_version", "git_commit", "platform", "build_time", "go_version", "go_tags", "go_gcflags"})


Test patch:
diff --git a/client/config/peerhost_test.go b/client/config/peerhost_test.go
index 7c657e1b..4bbc0836 100644
--- a/client/config/peerhost_test.go
+++ b/client/config/peerhost_test.go
@@ -31,7 +31,7 @@ import (
 	"d7y.io/dragonfly/v2/cmd/dependency/base"
 	"d7y.io/dragonfly/v2/manager/model"
 	"d7y.io/dragonfly/v2/pkg/dfnet"
-	"d7y.io/dragonfly/v2/pkg/serialize"
+	"d7y.io/dragonfly/v2/pkg/types"
 	"d7y.io/dragonfly/v2/pkg/unit"
 )
 
@@ -226,9 +226,9 @@ func TestPeerHostOption_Load(t *testing.T) {
 	_cert, _ := os.ReadFile("./testdata/certs/sca.crt")
 	_key, _ := os.ReadFile("./testdata/certs/sca.key")
 
-	caCert := serialize.PEMContent(strings.TrimSpace(string(_caCert)))
-	cert := serialize.PEMContent(strings.TrimSpace(string(_cert)))
-	key := serialize.PEMContent(strings.TrimSpace(string(_key)))
+	caCert := types.PEMContent(strings.TrimSpace(string(_caCert)))
+	cert := types.PEMContent(strings.TrimSpace(string(_cert)))
+	key := types.PEMContent(strings.TrimSpace(string(_key)))
 
 	peerHostOption := &DaemonOption{
 		Options: base.Options{
@@ -497,6 +497,15 @@ func TestPeerHostOption_Load(t *testing.T) {
 				Duration: 180000000000,
 			},
 		},
+		Security: GlobalSecurityOption{
+			AutoIssueCert: true,
+			CACert:        "-----BEGIN CERTIFICATE-----",
+			TLSVerify:     true,
+			TLSPolicy:     "force",
+			CertSpec: &CertSpec{
+				ValidityPeriod: 1000000000,
+			},
+		},
 	}
 
 	peerHostOptionYAML := &DaemonOption{}
diff --git a/client/config/testdata/config/daemon.yaml b/client/config/testdata/config/daemon.yaml
index 8a2dc308..3bbd7bc0 100644
--- a/client/config/testdata/config/daemon.yaml
+++ b/client/config/testdata/config/daemon.yaml
@@ -170,3 +170,11 @@ proxy:
   dumpHTTPContent: true
 reload:
   interval: 3m0s
+
+security:
+  autoIssueCert: true
+  caCert: -----BEGIN CERTIFICATE-----
+  tlsVerify: true
+  tlsPolicy: force
+  certSpec:
+    validityPeriod: 1s
diff --git a/manager/config/config_test.go b/manager/config/config_test.go
index 75143707..f219ea47 100644
--- a/manager/config/config_test.go
+++ b/manager/config/config_test.go
@@ -32,8 +32,9 @@ func TestManagerConfig_Load(t *testing.T) {
 
 	config := &Config{
 		Server: &ServerConfig{
-			Name:   "foo",
-			LogDir: "foo",
+			Name:     "foo",
+			CacheDir: "foo",
+			LogDir:   "foo",
 			GRPC: &TCPListenConfig{
 				Listen: "127.0.0.1",
 				PortRange: TCPListenPortRange{
@@ -103,6 +104,11 @@ func TestManagerConfig_Load(t *testing.T) {
 			CACert:        "foo",
 			CAKey:         "bar",
 			TLSPolicy:     "force",
+			CertSpec: &CertSpec{
+				IPAddresses:    []string{"127.0.0.1"},
+				DNSNames:       []string{"foo"},
+				ValidityPeriod: 1000,
+			},
 		},
 		Metrics: &MetricsConfig{
 			Enable:          true,
diff --git a/manager/config/testdata/manager.yaml b/manager/config/testdata/manager.yaml
index 7b25ffe3..effc94ea 100644
--- a/manager/config/testdata/manager.yaml
+++ b/manager/config/testdata/manager.yaml
@@ -1,5 +1,6 @@
 server:
   name: foo
+  cacheDir: foo
   logDir: foo
   grpc:
     listen: 127.0.0.1
@@ -61,6 +62,12 @@ security:
   caCert: foo
   caKey: bar
   tlsPolicy: force
+  certSpec:
+    ipAddresses:
+      - "127.0.0.1"
+    dnsNames:
+      - "foo"
+    validityPeriod: 1000
 
 metrics:
   enable: true
diff --git a/manager/rpcserver/cert_test.go b/manager/rpcserver/cert_test.go
index 84bdeabc..afd318dd 100644
--- a/manager/rpcserver/cert_test.go
+++ b/manager/rpcserver/cert_test.go
@@ -34,6 +34,7 @@ import (
 	testifyassert "github.com/stretchr/testify/assert"
 	testifyrequire "github.com/stretchr/testify/require"
 	"google.golang.org/grpc/peer"
+	"google.golang.org/protobuf/types/known/durationpb"
 	"gorm.io/gorm"
 
 	securityv1 "d7y.io/api/pkg/apis/security/v1"
@@ -77,10 +78,6 @@ func TestIssueCertificate(t *testing.T) {
 			csr, err := x509.CreateCertificateRequest(rand.Reader, template, pk)
 			require.Nilf(err, "CreateCertificateRequest should be ok")
 
-			var csrPEM bytes.Buffer
-			err = pem.Encode(&csrPEM, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr})
-			require.Nilf(err, "pem.Encode should be ok")
-
 			ca, err := tls.X509KeyPair([]byte(caCert), []byte(caKey))
 			require.Nilf(err, "parse cert and private key should be ok")
 
@@ -104,8 +101,8 @@ func TestIssueCertificate(t *testing.T) {
 			resp, err := server.IssueCertificate(
 				ctx,
 				&securityv1.CertificateRequest{
-					Csr:              csrPEM.String(),
-					ValidityDuration: 0,
+					Csr:            csr,
+					ValidityPeriod: durationpb.New(time.Hour),
 				})
 
 			assert.Nilf(err, "IssueCertificate should be ok")
@@ -167,11 +164,11 @@ func genCA() (cert, key string) {
 	return
 }
 
-func readCert(certPEM string) *x509.Certificate {
-	p, _ := pem.Decode([]byte(certPEM))
-	cert, err := x509.ParseCertificate(p.Bytes)
+func readCert(der []byte) *x509.Certificate {
+	cert, err := x509.ParseCertificate(der)
 	if err != nil {
 		panic(err)
 	}
+
 	return cert
 }
diff --git a/pkg/dfpath/dfpath_test.go b/pkg/dfpath/dfpath_test.go
index f0c5ba94..7feaa5f8 100644
--- a/pkg/dfpath/dfpath_test.go
+++ b/pkg/dfpath/dfpath_test.go
@@ -48,7 +48,7 @@ func TestNew(t *testing.T) {
 				d, err := New(options...)
 				assert.NoError(err)
 				assert.Equal(d.WorkHome(), DefaultWorkHome)
-				assert.Equal(d.CacheDir(), "")
+				assert.Equal(d.CacheDir(), DefaultCacheDir)
 				assert.Equal(d.LogDir(), DefaultLogDir)
 				assert.Equal(d.DataDir(), "")
 				assert.Equal(d.PluginDir(), DefaultPluginDir)
@@ -65,7 +65,7 @@ func TestNew(t *testing.T) {
 				d, err := New(options...)
 				assert.NoError(err)
 				assert.Equal(d.WorkHome(), "foo")
-				assert.Equal(d.CacheDir(), "")
+				assert.Equal(d.CacheDir(), DefaultCacheDir)
 				assert.Equal(d.LogDir(), DefaultLogDir)
 				assert.Equal(d.DataDir(), "")
 				assert.Equal(d.PluginDir(), DefaultPluginDir)
@@ -98,7 +98,7 @@ func TestNew(t *testing.T) {
 				d, err := New(options...)
 				assert.NoError(err)
 				assert.Equal(d.WorkHome(), DefaultWorkHome)
-				assert.Equal(d.CacheDir(), "")
+				assert.Equal(d.CacheDir(), DefaultCacheDir)
 				assert.Equal(d.LogDir(), "foo")
 				assert.Equal(d.DataDir(), "")
 				assert.Equal(d.PluginDir(), DefaultPluginDir)
@@ -115,7 +115,7 @@ func TestNew(t *testing.T) {
 				d, err := New(options...)
 				assert.NoError(err)
 				assert.Equal(d.WorkHome(), DefaultWorkHome)
-				assert.Equal(d.CacheDir(), "")
+				assert.Equal(d.CacheDir(), DefaultCacheDir)
 				assert.Equal(d.LogDir(), DefaultLogDir)
 				assert.Equal(d.DataDir(), "")
 				assert.Equal(d.PluginDir(), "foo")
@@ -132,7 +132,7 @@ func TestNew(t *testing.T) {
 				d, err := New(options...)
 				assert.NoError(err)
 				assert.Equal(d.WorkHome(), DefaultWorkHome)
-				assert.Equal(d.CacheDir(), "")
+				assert.Equal(d.CacheDir(), DefaultCacheDir)
 				assert.Equal(d.LogDir(), DefaultLogDir)
 				assert.Equal(d.DataDir(), "")
 				assert.Equal(d.PluginDir(), DefaultPluginDir)
diff --git a/scheduler/config/config_test.go b/scheduler/config/config_test.go
index b1d54a2f..d4c2031d 100644
--- a/scheduler/config/config_test.go
+++ b/scheduler/config/config_test.go
@@ -24,11 +24,6 @@ import (
 	"github.com/mitchellh/mapstructure"
 	testifyassert "github.com/stretchr/testify/assert"
 	"gopkg.in/yaml.v3"
-
-	"d7y.io/dragonfly/v2/pkg/net/fqdn"
-	"d7y.io/dragonfly/v2/pkg/net/ip"
-	"d7y.io/dragonfly/v2/pkg/rpc"
-	"d7y.io/dragonfly/v2/scheduler/storage"
 )
 
 func TestConfig_Load(t *testing.T) {
@@ -112,6 +107,9 @@ func TestConfig_Load(t *testing.T) {
 			CACert:        "foo",
 			TLSVerify:     true,
 			TLSPolicy:     "force",
+			CertSpec: &CertSpec{
+				ValidityPeriod: 1000,
+			},
 		},
 	}
 
@@ -128,78 +126,3 @@ func TestConfig_Load(t *testing.T) {
 
 	assert.EqualValues(config, schedulerConfigYAML)
 }
-
-func TestConfig_New(t *testing.T) {
-	assert := testifyassert.New(t)
-	config := New()
-
-	assert.EqualValues(config, &Config{
-		Server: &ServerConfig{
-			IP:     ip.IPv4,
-			Host:   fqdn.FQDNHostname,
-			Listen: "0.0.0.0",
-			Port:   8002,
-		},
-		Scheduler: &SchedulerConfig{
-			Algorithm:            "default",
-			BackSourceCount:      3,
-			RetryBackSourceLimit: 5,
-			RetryLimit:           10,
-			RetryInterval:        50 * time.Millisecond,
-			GC: &GCConfig{
-				PeerGCInterval: 10 * time.Minute,
-				PeerTTL:        24 * time.Hour,
-				TaskGCInterval: 10 * time.Minute,
-				TaskTTL:        24 * time.Hour,
-				HostGCInterval: 30 * time.Minute,
-				HostTTL:        48 * time.Hour,
-			},
-			Training: &TrainingConfig{
-				Enable:               false,
-				EnableAutoRefresh:    false,
-				RefreshModelInterval: 168 * time.Hour,
-				CPU:                  1,
-			},
-		},
-		DynConfig: &DynConfig{
-			RefreshInterval: 10 * time.Second,
-		},
-		Host: &HostConfig{},
-		Manager: &ManagerConfig{
-			SchedulerClusterID: 1,
-			KeepAlive: KeepAliveConfig{
-				Interval: 5 * time.Second,
-			},
-		},
-		SeedPeer: &SeedPeerConfig{
-			Enable: true,
-		},
-		Job: &JobConfig{
-			Enable:             true,
-			GlobalWorkerNum:    10,
-			SchedulerWorkerNum: 10,
-			LocalWorkerNum:     10,
-			Redis: &RedisConfig{
-				Port:      6379,
-				BrokerDB:  1,
-				BackendDB: 2,
-			},
-		},
-		Storage: &StorageConfig{
-			MaxSize:    storage.DefaultMaxSize,
-			MaxBackups: storage.DefaultMaxBackups,
-			BufferSize: storage.DefaultBufferSize,
-		},
-		Metrics: &MetricsConfig{
-			Enable:         false,
-			Addr:           ":8000",
-			EnablePeerHost: false,
-		},
-		Security: &SecurityConfig{
-			AutoIssueCert: false,
-			CACert:        "",
-			TLSVerify:     true,
-			TLSPolicy:     rpc.DefaultTLSPolicy,
-		},
-	})
-}
diff --git a/scheduler/config/testdata/scheduler.yaml b/scheduler/config/testdata/scheduler.yaml
index 507d1a4e..d342470d 100644
--- a/scheduler/config/testdata/scheduler.yaml
+++ b/scheduler/config/testdata/scheduler.yaml
@@ -71,3 +71,5 @@ security:
   caCert: foo
   tlsVerify: true
   tlsPolicy: force
+  certSpec:
+    validityPeriod: 1000