diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..80bf7fc7
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+charts
\ No newline at end of file
diff --git a/charts/frpc/.helmignore b/charts/frpc/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/charts/frpc/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/frpc/Chart.yaml b/charts/frpc/Chart.yaml
new file mode 100644
index 00000000..000f58bf
--- /dev/null
+++ b/charts/frpc/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: frpc
+description: A Helm chart Fast Reverse Proxy Client
+
+type: application
+version: 0.1.0
+appVersion: "v0.56.0"
diff --git a/charts/frpc/templates/_helpers.tpl b/charts/frpc/templates/_helpers.tpl
new file mode 100644
index 00000000..5867efad
--- /dev/null
+++ b/charts/frpc/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "frpc.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "frpc.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "frpc.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "frpc.labels" -}}
+helm.sh/chart: {{ include "frpc.chart" . }}
+{{ include "frpc.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "frpc.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "frpc.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "frpc.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "frpc.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/charts/frpc/templates/certificate.yaml b/charts/frpc/templates/certificate.yaml
new file mode 100644
index 00000000..18d8f804
--- /dev/null
+++ b/charts/frpc/templates/certificate.yaml
@@ -0,0 +1,41 @@
+{{- if and .Values.mTLS.enabled (eq .Values.mTLS.existingSecret "") }}
+{{- if eq .Values.mTLS.certificatePEM "" }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: {{ printf "%s-client" (include "frpc.fullname" .) }}
+spec:
+  commonName: {{ .Values.mTLS.commonName }}
+  issuerRef:
+    {{- .Values.mTLS.issuerRef | toYaml | nindent 4 }}
+  {{- with .Values.mTLS.subject }}
+  subject:
+    {{- . | toYaml | nindent 4 }}
+  {{- end }}
+  privateKey:
+    algorithm: ECDSA
+    size: 256
+  usages:
+    - client auth
+  secretName: {{ printf "%s-client" (include "frpc.fullname" .) }}
+---
+{{- else }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ printf "%s-client-ca" (include "frpc.fullname" .) }}
+type: kubernetes.io/tls
+data:
+  tls.crt: {{ .Values.mTLS.certificatePEM | b64enc | quote }}
+  tls.key: {{ required "Both PEM and KEY are required" .Values.mTLS.certificateKEY | b64enc | quote }}
+---
+{{- end }}
+{{- end }}
+{{- if ne .Values.mTLS.trustedCA "" }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ printf "%s-ca" (include "frpc.fullname" .) }}
+data:
+  ca.crt: {{ .Values.mTLS.trustedCA | b64enc | quote }}
+{{- end}}
\ No newline at end of file
diff --git a/charts/frpc/templates/config.yaml b/charts/frpc/templates/config.yaml
new file mode 100644
index 00000000..9a79d508
--- /dev/null
+++ b/charts/frpc/templates/config.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "frpc.fullname" . }}
+data:
+  frpc.yaml: |
+    {{- tpl (.Values.config | toYaml) $  | nindent 4 }}
\ No newline at end of file
diff --git a/charts/frpc/templates/dashboard-service.yaml b/charts/frpc/templates/dashboard-service.yaml
new file mode 100644
index 00000000..8cbc1102
--- /dev/null
+++ b/charts/frpc/templates/dashboard-service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "frpc.fullname" . }}-dashboard
+  labels:
+    {{- include "frpc.labels" . | nindent 4 }}
+  {{- with .Values.dashboardService.annotations }}
+  annotations:
+    {{- . | toYaml | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.dashboardService.type }}
+  ports:
+    - port: {{ .Values.dashboardService.port }}
+      targetPort: dashboard
+      protocol: TCP
+      name: http
+  selector:
+    {{- include "frpc.selectorLabels" . | nindent 4 }}
diff --git a/charts/frpc/templates/deployment.yaml b/charts/frpc/templates/deployment.yaml
new file mode 100644
index 00000000..f3d3618a
--- /dev/null
+++ b/charts/frpc/templates/deployment.yaml
@@ -0,0 +1,85 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "frpc.fullname" . }}
+  labels:
+    {{- include "frpc.labels" . | nindent 4 }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "frpc.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      annotations:
+        checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
+      {{- with .Values.podAnnotations }}
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "frpc.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "frpc.serviceAccountName" . }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      containers:
+        - name: {{ .Chart.Name }}
+          securityContext:
+            {{- toYaml .Values.securityContext | nindent 12 }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          args:
+            - "-c"
+            - "/var/frp/conf/frpc.yaml"
+          ports:
+            - name: dashboard
+              containerPort: {{ .Values.config.webServer.port }}
+              protocol: TCP
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+          volumeMounts:
+            - mountPath: /var/frp/conf
+              name: config
+          {{- if ne .Values.mTLS.trustedCA "" }}
+            - mountPath: /var/frp/tls
+              name: tls
+          {{- end }}
+          {{- if .Values.mTLS.enabled}}
+            - mountPath: /var/frp/mtls
+              name: mtls
+          {{- end }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      volumes:
+        - name: config
+          configMap:
+            name: {{ include "frpc.fullname" . }}
+        {{- if ne .Values.mTLS.trustedCA "" }}
+        - name: tls
+          secret:
+            secretName: {{ printf "%s-ca" (include "frpc.fullname" .) }}
+        {{- end }}
+        {{- if .Values.mTLS.enabled}}
+        - name: mtls
+          secret:
+            secretName: {{ .Values.mTLS.existingSecret | default (printf "%s-client-ca" (include "frpc.fullname" .)) }}
+            items:
+              - key: tls.crt
+                path: tls.crt
+              - key: tls.key
+                path: tls.key
+        {{- end }}
diff --git a/charts/frpc/templates/serviceaccount.yaml b/charts/frpc/templates/serviceaccount.yaml
new file mode 100644
index 00000000..e3df1722
--- /dev/null
+++ b/charts/frpc/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "frpc.serviceAccountName" . }}
+  labels:
+    {{- include "frpc.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/charts/frpc/values.yaml b/charts/frpc/values.yaml
new file mode 100644
index 00000000..b4611b08
--- /dev/null
+++ b/charts/frpc/values.yaml
@@ -0,0 +1,126 @@
+replicaCount: 1
+
+image:
+  repository: fatedier/frpc
+  pullPolicy: IfNotPresent
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: ""
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: true
+  # Annotations to add to the service account
+  annotations: {}
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: {}
+
+podSecurityContext: {}
+  # fsGroup: 2000
+
+securityContext: {}
+  # capabilities:
+  #   drop:
+  #   - ALL
+  # readOnlyRootFilesystem: true
+  # runAsNonRoot: true
+  # runAsUser: 1000
+
+dashboardService:
+  type: ClusterIP
+  port: 8080
+  containerPort: 7500
+  annotations: {}
+
+resources: {}
+  # We usually recommend not to specify default resources and to leave this as a conscious
+  # choice for the user. This also increases chances charts run on environments with little
+  # resources, such as Minikube. If you do want to specify resources, uncomment the following
+  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+mTLS:
+  enabled: false
+  existingSecret: ""
+  certificatePEM: ""
+  certificateKEY: ""
+  trustedCA: ""
+  commonName: "Fast Reverse Proxy mTLS client"
+  subject: {}
+  issuerRef:
+    group: cert-manager.io
+    kind: Issuer
+    name: frps-client-ca
+
+config:
+  serverAddr: "0.0.0.0"
+  serverPort: 443
+
+  webServer:
+    addr: "0.0.0.0"
+    port: 7500
+    user: "admin"
+    password: "admin"
+
+  transport:
+    protocol: "wss"
+    tls:
+      certFile: '{{ .Values.mTLS.enabled | ternary "/var/frp/mtls/tls.crt" "" }}'
+      keyFile: '{{ .Values.mTLS.enabled | ternary "/var/frp/mtls/tls.key" "" }}'
+      trustedCaFile : '{{ eq .Values.mTLS.trustedCA "" | ternary "" "/var/frp/tls/ca.crt" }}'
+      disableCustomTLSFirstByte: true
+
+
+  proxies: []
+#    - name: "ssh"
+#      type: "tcp"
+#      localIP: "127.0.0.1"
+#      localPort: 22
+#      transport:
+#        # Limit bandwidth for this proxy, unit is KB and MB
+#        bandwidthLimit: "1MB"
+#        # Where to limit bandwidth, can be 'client' or 'server', default is 'client'
+#        bandwidthLimitMode: "client"
+#        # If true, traffic of this proxy will be encrypted, default is false
+#        tuseEncryption: false
+#        # If true, traffic will be compressed
+#        useCompression: false
+#      # Remote port listen by frps
+#      remotePort: 6001
+#
+#      loadBalancer:
+#        # frps will load balancing connections for proxies in same group
+#        group: "test_group"
+#        # group should have same group key
+#        groupKey: "123456"
+#      # Enable health check for the backend service, it supports 'tcp' and 'http' now.
+#      # frpc will connect local service's port to detect it's healthy status
+#      healthCheck:
+#        type: "tcp"
+#        # Health check connection timeout
+#        timeoutSeconds: 3
+#        # If continuous failed in 3 times, the proxy will be removed from frps
+#        maxFailed: 3
+#        # Every 10 seconds will do a health check
+#        intervalSeconds: 10
+#      # Additional meta info for each proxy. It will be passed to the server-side plugin for use.
+#      metadatas:
+#        var1: "abc"
+#        var2: "123"
diff --git a/charts/frps/.helmignore b/charts/frps/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/charts/frps/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/frps/Chart.yaml b/charts/frps/Chart.yaml
new file mode 100644
index 00000000..be6f2f19
--- /dev/null
+++ b/charts/frps/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: frps
+description: A Helm chart Fast Reverse Proxy Server
+
+type: application
+version: 0.1.0
+appVersion: "v0.56.0"
diff --git a/charts/frps/templates/_helpers.tpl b/charts/frps/templates/_helpers.tpl
new file mode 100644
index 00000000..616b1ed4
--- /dev/null
+++ b/charts/frps/templates/_helpers.tpl
@@ -0,0 +1,68 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "frps.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "frps.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "frps.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "frps.labels" -}}
+helm.sh/chart: {{ include "frps.chart" . }}
+{{ include "frps.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "frps.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "frps.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "frps.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "frps.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
+
+{{- define "frps.trustedCaFile" -}}
+{{- if .Values.mTLS.enabled }}
+{{- printf "/var/frp/mtls/%s" .Values.mTLS.key }}
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/frps/templates/certificate.yaml b/charts/frps/templates/certificate.yaml
new file mode 100644
index 00000000..53248544
--- /dev/null
+++ b/charts/frps/templates/certificate.yaml
@@ -0,0 +1,56 @@
+{{- if eq .Values.certificate.existingSecret "" }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: {{ include "frps.fullname" . }}
+spec:
+  issuerRef:
+    {{- .Values.certificate.issuerRef | toYaml | nindent 4 }}
+  dnsNames:
+    {{- .Values.certificate.dnsNames | toYaml | nindent 4 }}
+  privateKey:
+    algorithm: ECDSA
+    size: 256
+  secretName: {{ include "frps.fullname" . }}
+---
+{{- end }}
+{{- if and .Values.mTLS.enabled (eq .Values.mTLS.existingSecret "") }}
+{{- if eq .Values.mTLS.certificatePEM "" }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: {{ printf "%s-client-ca" (include "frps.fullname" .) }}
+spec:
+  commonName: {{ .Values.mTLS.commonName }}
+  issuerRef:
+    {{- .Values.mTLS.issuerRef | toYaml | nindent 4 }}
+  {{- with .Values.mTLS.subject }}
+  subject:
+    {{- . | toYaml | nindent 4 }}
+  {{- end }}
+  isCA: true
+  privateKey:
+    algorithm: ECDSA
+    size: 256
+  secretName: {{ printf "%s-client-ca" (include "frps.fullname" .) }}
+---
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+  name: {{ printf "%s-client-ca" (include "frps.fullname" .) }}
+spec:
+  ca:
+    secretName: {{ printf "%s-client-ca" (include "frps.fullname" .) }}
+---
+{{- else }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ printf "%s-client-ca" (include "frps.fullname" .) }}
+type: kubernetes.io/tls
+data:
+  tls.crt: {{ .Values.mTLS.certificatePEM | b64enc | quote }}
+  tls.key: ""
+---
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/frps/templates/config.yaml b/charts/frps/templates/config.yaml
new file mode 100644
index 00000000..e32bf2e4
--- /dev/null
+++ b/charts/frps/templates/config.yaml
@@ -0,0 +1,43 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "frps.fullname" . }}
+data:
+  frps.toml: |
+  {{- range $elem, $elemVal := .Values.config }}
+    {{- if not (kindIs "map" $elemVal) }}
+    {{- if kindIs "invalid" $elemVal }}
+    {{ $elem }} =
+    {{- else if kindIs "string" $elemVal }}
+    {{- $tplElemVal := tpl $elemVal $ }}
+    {{- if eq $tplElemVal $elemVal }}
+    {{ $elem }} = {{ $elemVal | quote }}
+    {{- else }}
+    {{ $elem }} = {{ $tplElemVal }}
+    {{- end }}
+    {{- else }}
+    {{ $elem }} = {{ $elemVal }}
+    {{- end }}
+    {{- end }}
+  {{- end }}
+  {{- range $key, $value := .Values.config }}
+    {{- if kindIs "map" $value }}
+
+    [[{{ $key }}]]
+    {{- range $elem, $elemVal := $value }}
+    {{- if kindIs "invalid" $elemVal }}
+    {{ $elem }} =
+    {{- else if kindIs "string" $elemVal }}
+    {{- $tplElemVal := tpl $elemVal $ }}
+    {{- if eq $tplElemVal $elemVal }}
+    {{ $elem }} = {{ $elemVal | quote }}
+    {{- else }}
+    {{ $elem }} = {{ $tplElemVal }}
+    {{- end }}
+    {{- else }}
+    {{ $elem }} = {{ $elemVal }}
+    {{- end }}
+    {{- end }}
+    {{- end }}
+  {{- end }}
+
diff --git a/charts/frps/templates/dashboard-service.yaml b/charts/frps/templates/dashboard-service.yaml
new file mode 100644
index 00000000..040414d1
--- /dev/null
+++ b/charts/frps/templates/dashboard-service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "frps.fullname" . }}-dashboard
+  labels:
+    {{- include "frps.labels" . | nindent 4 }}
+  {{- with .Values.dashboardService.annotations }}
+  annotations:
+    {{- . | toYaml | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.dashboardService.type }}
+  ports:
+    - port: {{ .Values.dashboardService.port }}
+      targetPort: dashboard
+      protocol: TCP
+      name: http
+  selector:
+    {{- include "frps.selectorLabels" . | nindent 4 }}
diff --git a/charts/frps/templates/deployment.yaml b/charts/frps/templates/deployment.yaml
new file mode 100644
index 00000000..02a2c88d
--- /dev/null
+++ b/charts/frps/templates/deployment.yaml
@@ -0,0 +1,95 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "frps.fullname" . }}
+  labels:
+    {{- include "frps.labels" . | nindent 4 }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "frps.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      annotations:
+        checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
+      {{- with .Values.podAnnotations }}
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "frps.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "frps.serviceAccountName" . }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      containers:
+        - name: {{ .Chart.Name }}
+          securityContext:
+            {{- toYaml .Values.securityContext | nindent 12 }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          args:
+            - "-c"
+            - "/var/frp/conf/frps.toml"
+          ports:
+            - name: bind
+              containerPort: {{ .Values.service.containerPort }}
+              protocol: TCP
+            - name: dashboard
+              containerPort: {{ .Values.dashboardService.containerPort }}
+              protocol: TCP
+              {{- if .Values.internalService.enabled }}
+            - name: https
+              containerPort: {{ .Values.internalService.containerPort }}
+              protocol: TCP
+              {{- end }}
+{{/*          livenessProbe:*/}}
+{{/*            httpGet:*/}}
+{{/*              path: /*/}}
+{{/*              port: http*/}}
+{{/*          readinessProbe:*/}}
+{{/*            httpGet:*/}}
+{{/*              path: /*/}}
+{{/*              port: http*/}}
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+          volumeMounts:
+            - mountPath: /var/frp/conf
+              name: config
+            - mountPath: /var/frp/tls
+              name: tls
+          {{- if .Values.mTLS.enabled}}
+            - mountPath: /var/frp/mtls
+              name: mtls
+          {{- end }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      volumes:
+        - name: config
+          configMap:
+            name: {{ include "frps.fullname" . }}
+        - name: tls
+          secret:
+            secretName: {{ .Values.certificate.existingSecret | default (include "frps.fullname" .) }}
+        {{- if .Values.mTLS.enabled}}
+        - name: mtls
+          secret:
+            secretName: {{ .Values.mTLS.existingSecret | default (printf "%s-client-ca" (include "frps.fullname" .)) }}
+            items:
+              - key: {{ .Values.mTLS.key }}
+                path: {{ .Values.mTLS.key }}
+        {{- end }}
diff --git a/charts/frps/templates/internal-service.yaml b/charts/frps/templates/internal-service.yaml
new file mode 100644
index 00000000..e354c040
--- /dev/null
+++ b/charts/frps/templates/internal-service.yaml
@@ -0,0 +1,21 @@
+{{- if .Values.internalService.enabled }}
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "frps.fullname" . }}-internal
+  labels:
+    {{- include "frps.labels" . | nindent 4 }}
+  {{- with .Values.internalService.annotations }}
+  annotations:
+    {{- . | toYaml | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.internalService.type }}
+  ports:
+    - port: {{ .Values.internalService.port }}
+      targetPort: https
+      protocol: TCP
+      name: https
+  selector:
+    {{- include "frps.selectorLabels" . | nindent 4 }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/frps/templates/service.yaml b/charts/frps/templates/service.yaml
new file mode 100644
index 00000000..30787635
--- /dev/null
+++ b/charts/frps/templates/service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "frps.fullname" . }}
+  labels:
+    {{- include "frps.labels" . | nindent 4 }}
+  {{- with .Values.service.annotations }}
+  annotations:
+    {{- . | toYaml | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+    - port: {{ .Values.service.port }}
+      targetPort: bind
+      protocol: TCP
+      name: http
+  selector:
+    {{- include "frps.selectorLabels" . | nindent 4 }}
diff --git a/charts/frps/templates/serviceaccount.yaml b/charts/frps/templates/serviceaccount.yaml
new file mode 100644
index 00000000..602ff8f3
--- /dev/null
+++ b/charts/frps/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "frps.serviceAccountName" . }}
+  labels:
+    {{- include "frps.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/charts/frps/values.yaml b/charts/frps/values.yaml
new file mode 100644
index 00000000..616327a8
--- /dev/null
+++ b/charts/frps/values.yaml
@@ -0,0 +1,106 @@
+replicaCount: 1
+
+image:
+  repository: fatedier/frps
+  pullPolicy: IfNotPresent
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: ""
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: true
+  # Annotations to add to the service account
+  annotations: {}
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: {}
+
+podSecurityContext: {}
+  # fsGroup: 2000
+
+securityContext: {}
+  # capabilities:
+  #   drop:
+  #   - ALL
+  # readOnlyRootFilesystem: true
+  # runAsNonRoot: true
+  # runAsUser: 1000
+
+service:
+  type: LoadBalancer
+  port: 443
+  containerPort: 7000
+  annotations: {}
+
+dashboardService:
+  type: ClusterIP
+  port: 8080
+  containerPort: 7500
+  annotations: {}
+
+internalService:
+  enabled: false
+  type: ClusterIP
+  port: 443
+  containerPort: 443
+  annotations: {}
+
+resources: {}
+  # We usually recommend not to specify default resources and to leave this as a conscious
+  # choice for the user. This also increases chances charts run on environments with little
+  # resources, such as Minikube. If you do want to specify resources, uncomment the following
+  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+certificate:
+  existingSecret: ""
+  dnsNames: []
+  issuerRef:
+    group: cert-manager.io
+    kind: ClusterIssuer
+    name: letsencrypt-prod
+
+mTLS:
+  enabled: false
+  existingSecret: ""
+  certificatePEM: ""
+  key: tls.crt
+  commonName: "Fast Reverse Proxy mTLS"
+  subject: {}
+  issuerRef:
+    group: cert-manager.io
+    kind: ClusterIssuer
+    name: selfsigned
+
+config:
+  bindAddr: "0.0.0.0"
+  bindPort: "{{ .Values.service.containerPort }}"
+
+  webServer.addr: "0.0.0.0"
+  webServer.port: "{{ .Values.dashboardService.containerPort }}"
+  webServer.user: "admin"
+  webServer.password: "admin"
+
+  vhostHTTPSPort: "{{ .Values.internalService.enabled | ternary .Values.internalService.containerPort .Values.service.containerPort }}"
+
+  transport.tls.force: true
+  transport.tls.certFile: "/var/frp/tls/tls.crt"
+  transport.tls.keyFile: "/var/frp/tls/tls.key"
+  transport.tls.trustedCaFile : '{{ include "frps.trustedCaFile" . | quote }}'
\ No newline at end of file