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, the TCPGroupCtl handles 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:

  1. First Proxy Initialization: When the first client proxy registers with a unique group name, 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.

  2. Subsequent Proxy Validation: For additional proxies joining the same group, the server performs strict validation:

    • Address and Port Matching: The remotePort and bind address must exactly match the first proxy's configuration. If they differ, the server returns ErrGroupParamsInvalid.
    • Authentication Verification: The groupKey must be identical across all members. A mismatch triggers ErrGroupAuthFailed.
  3. Connection Distribution: Once registered, incoming connections hit the shared listener. The group's Accept method (implemented in TCPGroupListener for TCP or createConn/chooseEndpoint for HTTP) selects the next available proxy using round-robin logic and hands off the connection to that proxy's tunnel.

  4. 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 remotePort or bind addresses trigger ErrGroupParamsInvalid
  • Incorrect groupKey values result in ErrGroupAuthFailed

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.group and loadBalancer.groupKey values across all participating client proxies.
  • The server validates that all group members use the same remotePort and groupKey, rejecting mismatches with ErrGroupParamsInvalid or ErrGroupAuthFailed.
  • Traffic distribution uses round-robin selection implemented in server/group/tcp.go and server/group/http.go.
  • No special server configuration is required; group handling activates automatically when clients present loadBalancer fields.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →