Is your feature request related to a problem? Please describe.
The Client controller currently builds a single bare corev1.Pod per Client CR (pkg/client/builder/pod_builder.go). For multi-worker Kubernetes clusters that want one frpc per node — so frps can round-robin incoming connections across multiple registered members of a loadBalancer.group — this forces either:
- Creating N
Client CRs with manual node-pinning (drifts as the worker count changes, ugly to template), or
- Running a single
Client and losing the HA + load-spreading benefit of loadBalancer.group.
A bare Pod also has no rolling update story and is not rescheduled on NodeLost: the reconciler's Get returns the stuck pod (not NotFound), so the recreate branch never fires. This is worse than the equivalent hand-rolled DaemonSet today.
Describe the solution you'd like
Add a workload-kind selector to Client.spec.podTemplate (or a sibling Client.spec.workload) supporting:
Pod (current behavior, default)
Deployment with replicas
DaemonSet (one pod per matching node)
When combined with Upstream.spec.tcp.loadBalancer.group + groupKey, each replica registers itself as a member of the same group and frps spreads traffic across them — which is exactly the production HA story loadBalancer.group was designed for, but it's currently unreachable through this operator.
Suggested CRD shape (additive, non-breaking):
apiVersion: frp.zufardhiyaulhaq.com/v1alpha1
kind: Client
metadata:
name: edge
spec:
server: { ... }
workload:
kind: DaemonSet # Pod | Deployment | DaemonSet
# for Deployment only:
replicas: 3
# nodeSelector / affinity / tolerations remain on podTemplate
podTemplate:
nodeSelector:
node-role.kubernetes.io/worker: ""
Describe alternatives you've considered
- Generating N
Client CRs externally (Helm/Kustomize/Flux templating). Works but defeats the operator abstraction and complicates pod identity for loadBalancer.group membership.
- Wrapping the operator-managed Pod in an external DaemonSet that shares the same ConfigMap. Bypasses the operator entirely.
- Switching to a different frp operator. Aureum-Cloud/frp-Operator is TCP-only with no
loadBalancer.group, so it's not a viable alternative for HA workloads.
Additional context
loadBalancer.group + groupKey are already supported in UpstreamSpec_TCP.LoadBalancer (api/v1alpha1/upstream_types.go), so the proxy side is ready — only the client workload model needs to scale out. Happy to send a PR if the API shape is agreed.
Is your feature request related to a problem? Please describe.
The
Clientcontroller currently builds a single barecorev1.PodperClientCR (pkg/client/builder/pod_builder.go). For multi-worker Kubernetes clusters that want one frpc per node — so frps can round-robin incoming connections across multiple registered members of aloadBalancer.group— this forces either:ClientCRs with manual node-pinning (drifts as the worker count changes, ugly to template), orClientand losing the HA + load-spreading benefit ofloadBalancer.group.A bare Pod also has no rolling update story and is not rescheduled on
NodeLost: the reconciler'sGetreturns the stuck pod (notNotFound), so the recreate branch never fires. This is worse than the equivalent hand-rolled DaemonSet today.Describe the solution you'd like
Add a workload-kind selector to
Client.spec.podTemplate(or a siblingClient.spec.workload) supporting:Pod(current behavior, default)DeploymentwithreplicasDaemonSet(one pod per matching node)When combined with
Upstream.spec.tcp.loadBalancer.group+groupKey, each replica registers itself as a member of the same group and frps spreads traffic across them — which is exactly the production HA storyloadBalancer.groupwas designed for, but it's currently unreachable through this operator.Suggested CRD shape (additive, non-breaking):
Describe alternatives you've considered
ClientCRs externally (Helm/Kustomize/Flux templating). Works but defeats the operator abstraction and complicates pod identity forloadBalancer.groupmembership.loadBalancer.group, so it's not a viable alternative for HA workloads.Additional context
loadBalancer.group+groupKeyare already supported inUpstreamSpec_TCP.LoadBalancer(api/v1alpha1/upstream_types.go), so the proxy side is ready — only the client workload model needs to scale out. Happy to send a PR if the API shape is agreed.