fatedier/frp · critical
create nat hole controller error
Error message
create nat hole controller error, %v
What it means
The last step of frps service creation builds the NAT hole controller with nathole.NewController; failure aborts startup. Without this controller, xtcp proxies cannot work (they later fail with "xtcp is not supported in frps"), so frps chooses to fail fast instead.
Solutions
- Check the wrapped %v cause in the frps log to identify which resource failed
- Ensure the process may create UDP sockets in its environment
- Relax the container/SELinux constraints or move frps to a host that permits UDP
Defensive patterns
Strategy: try-catch
Try / catch
if err := newService(cfg); err != nil && strings.Contains(err.Error(), "create nat hole controller error") {
// environment cannot support xtcp: report and fix host/container UDP policy
} Prevention
- Smoke-test frps startup in the target container image before deploying xtcp clients
- Ensure UDP socket creation is permitted (SELinux, seccomp profiles)
- Monitor boot logs for nathole controller creation failures
When it happens
Trigger: nathole.NewController failing, typically because it cannot set up its UDP listeners/analysis data store on the host; restricted container or firewall environments.
Common situations: Deploying frps into containers that block UDP socket creation; hosts with strict SELinux; resource limits at startup.
Related errors
- xtcp is not supported in frps
- create server listener error
- create ssh gateway error
- create vhost http listener error
- create vhost httpsMuxer error
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/fad71e463a0acac4.
Report an issue: GitHub.
Appendix: source
Thrown at server/service.go:360
svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout)
if err != nil {
return nil, fmt.Errorf("create vhost httpsMuxer error, %v", err)
}
// Init HTTPS group controller after HTTPSMuxer is created
svr.rc.HTTPSGroupCtl = group.NewHTTPSGroupController(svr.rc.VhostHTTPSMuxer)
}
// frp tls listener
svr.tlsListener = svr.muxer.Listen(2, 1, func(data []byte) bool {
// tls first byte can be 0x16 only when vhost https port is not same with bind port
return int(data[0]) == netpkg.FRPTLSHeadByte || int(data[0]) == 0x16
})
// Create nat hole controller.
nc, err := nathole.NewController(time.Duration(cfg.NatHoleAnalysisDataReserveHours) * time.Hour)
if err != nil {
return nil, fmt.Errorf("create nat hole controller error, %v", err)
}
svr.rc.NatHoleController = nc
return svr, nil
}
func (svr *Service) Run(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
svr.ctx = ctx
svr.cancel = cancel
// run dashboard web server.
if svr.webServer != nil {
go func() {
log.Infof("dashboard listen on %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil {
log.Warnf("dashboard server exit with error: %v", err)
}
}()View on GitHub (pinned to 6c8a8d0a97)