How to Implement Load Balancing Across Multiple FRP Proxies Using Groups
FRP (Fast Reverse Proxy) enables native load balancing across multiple client proxies by configuring them with identical group and groupKey values, allowing the server to distribute incoming connections via round-robin logic without external load balancers.
The fatedier/frp repository provides a built-in group mechanism that allows several proxies to share a single public listener on the FRP server. When you implement load balancing across multiple FRP proxies using groups, the server creates one real listener and distributes traffic among group members using round-robin selection. This architecture eliminates the need for separate load balancing infrastructure while providing high availability for your services.
Understanding the FRP Group Load Balancing Architecture
Client-Side Configuration Structure
The group configuration originates in pkg/config/v1/proxy.go, where the LoadBalancer field holds the group and groupKey parameters. When a client connects to the server, it transmits these values as part of the proxy registration message, signaling its intent to participate in a load-balanced group.
Server-Side Group Controllers
On the server side, dedicated controllers manage group lifecycle and traffic distribution:
- TCP Groups: Implemented in
server/group/tcp.go, theTCPGroupCtlhandles TCP proxy registration and creates the real listening socket. - HTTP Groups: Implemented in
server/group/http.go, the HTTP group controller manages virtual host routing and load balancing for HTTP-based services.
These controllers validate that all group members share identical address, port, and authentication credentials before allowing registration.
Round-Robin Distribution Logic
The actual load balancing uses atomic round-robin selection. In server/group/tcp.go, the code uses atomic.AddUint64(&g.index, 1) to cycle through registered proxies sequentially. When a new connection arrives on the shared listener, the controller selects the next proxy in the rotation and forwards the connection to that client's tunnel.
How Group Registration Works in FRP
The group mechanism operates through a specific four-phase registration process:
-
First Proxy Initialization: When the first client proxy registers with a unique
groupname, the server creates a real listener on the requested address and port via the port manager (ports.Manager). This listening socket is stored within the group object and becomes the shared entry point for all subsequent traffic. -
Subsequent Proxy Validation: For additional proxies joining the same
group, the server performs strict validation:- Address and Port Matching: The
remotePortand bind address must exactly match the first proxy's configuration. If they differ, the server returnsErrGroupParamsInvalid. - Authentication Verification: The
groupKeymust be identical across all members. A mismatch triggersErrGroupAuthFailed.
- Address and Port Matching: The
-
Connection Distribution: Once registered, incoming connections hit the shared listener. The group's
Acceptmethod (implemented inTCPGroupListenerfor TCP orcreateConn/chooseEndpointfor HTTP) selects the next available proxy using round-robin logic and hands off the connection to that proxy's tunnel. -
Cleanup and Removal: When a proxy disconnects or is stopped, the server removes it from the group. If the group becomes empty (no remaining members), the server closes the real listener and releases the port back to the port manager, ensuring efficient resource utilization.
Implementing TCP Load Balancing with FRP Groups
To implement TCP load balancing, configure multiple client proxies with identical group and groupKey values, ensuring they request the same remotePort.
Server Configuration
The server requires no special configuration to support groups. A standard frps.toml suffices:
# frps.toml
bindPort = 7000
dashboardPort = 7500
Client Configuration
Configure two or more clients to share the load:
# frpc.toml
[common]
serverAddr = "x.x.x.x"
serverPort = 7000
# First proxy member
[ssh_proxy_a]
type = "tcp"
localPort = 22
remotePort = 6000
loadBalancer.group = "mygroup"
loadBalancer.groupKey = "secret123"
# Second proxy member
[ssh_proxy_b]
type = "tcp"
localPort = 2222
remotePort = 6000
loadBalancer.group = "mygroup"
loadBalancer.groupKey = "secret123"
Both proxies request remotePort = 6000. The server creates a single listener on port 6000 and distributes incoming connections alternately between the SSH service on port 22 (first proxy) and port 2222 (second proxy).
Implementing HTTP Load Balancing with FRP Groups
HTTP groups function similarly but operate through the virtual host router in server/group/http.go. This enables load balancing for web services behind the same domain.
Client Configuration for HTTP Groups
# frpc.toml
[common]
serverAddr = "x.x.x.x"
serverPort = 7000
[web_a]
type = "http"
customDomains = ["example.com"]
location = "/app1"
loadBalancer.group = "http_grp"
loadBalancer.groupKey = "http_key"
[web_b]
type = "http"
customDomains = ["example.com"]
location = "/app1"
loadBalancer.group = "http_grp"
loadBalancer.groupKey = "http_key"
The HTTPGroupController registers the first proxy with the vhost router. Subsequent proxies join the same group, and the chooseEndpoint method distributes requests round-robin between web_a and web_b for traffic hitting http://example.com/app1.
Validating Your FRP Group Configuration
The fatedier/frp repository includes end-to-end tests that verify group behavior. The test suite in test/e2e/v1/features/group.go confirms that:
- Connections distribute evenly across group members
- Mismatched
remotePortor bind addresses triggerErrGroupParamsInvalid - Incorrect
groupKeyvalues result inErrGroupAuthFailed
Run the specific group tests to validate your setup:
# Execute only the group-related e2e tests
make test-e2e TEST=TestGroup
Summary
- FRP groups enable native load balancing by allowing multiple proxies to share a single public listener on the server.
- Configuration requires setting identical
loadBalancer.groupandloadBalancer.groupKeyvalues across all participating client proxies. - The server validates that all group members use the same
remotePortandgroupKey, rejecting mismatches withErrGroupParamsInvalidorErrGroupAuthFailed. - Traffic distribution uses round-robin selection implemented in
server/group/tcp.goandserver/group/http.go. - No special server configuration is required; group handling activates automatically when clients present
loadBalancerfields.
Frequently Asked Questions
What happens if two proxies in the same group specify different remote ports?
The FRP server rejects the second proxy's registration and returns ErrGroupParamsInvalid. According to the validation logic in server/group/tcp.go, all members of a group must bind to the identical address and port to ensure the server can maintain a single shared listener.
Can I use different groupKey values for proxies in the same group?
No. The groupKey serves as an authentication credential for the group. If a proxy attempts to join with a groupKey that differs from the existing group's key, the server responds with ErrGroupAuthFailed. This security measure prevents unauthorized proxies from hijacking traffic intended for your load-balanced service.
Does FRP support other load balancing algorithms besides round-robin?
The current implementation in fatedier/frp uses round-robin selection via atomic.AddUint64 to cycle through proxy endpoints. The source code in server/group/tcp.go and server/group/http.go does not currently expose configuration for alternative algorithms like least-connections or IP hashing. For advanced load balancing requirements, you would need to implement a custom group controller or place an external load balancer in front of the FRP server.
Is group load balancing available for all proxy types?
Group load balancing is implemented for TCP, HTTP, and HTTPS proxy types in the FRP server. The architecture uses specific controllers (TCPGroupCtl, HTTPGroupCtl) to handle the nuances of each protocol. UDP proxy groups follow a similar pattern but require state management for session tracking. Check the specific group controller files in server/group/ to confirm support for your specific use case.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →