Best Practices for Configuring Hysteria Core: Production Deployment Guide

Always provide valid TLS certificates, implement a non-nil authenticator, and rely on default QUIC values unless you have specific performance constraints.

The Hysteria proxy server, maintained at apernet/hysteria, relies on a central Config struct defined in core/server/config.go to orchestrate TLS, QUIC, authentication, and congestion control. Following these best practices for configuring Hysteria core ensures secure, high-throughput proxy deployments without runtime validation errors.

Core Configuration Architecture

All server settings aggregate into the Config struct located at [core/server/config.go](https://github.com/apernet/hysteria/blob/master/core/server/config.go#L27-L44). When you instantiate a server via NewServer, the library automatically invokes the fill() method (source), which validates fields and injects sensible defaults. Never manually set values below the validated minima, or NewServer will return a ConfigError before the server starts.

TLS Configuration Requirements

Security rests entirely on proper TLS setup. The server rejects any configuration lacking at least one certificate or a custom GetCertificate callback:

if len(c.TLSConfig.Certificates) == 0 && c.TLSConfig.GetCertificate == nil {
    return errors.ConfigError{Field: "TLSConfig", Reason: "must set at least one of Certificates or GetCertificate"}
}

Production best practices:

  • Use valid X.509 chains via c.TLSConfig.Certificates for static deployments.
  • Implement GetCertificate for dynamic SNI selection or automated certificate management.
  • Do not disable client verification unless you have implemented a separate, robust authentication layer.

QUIC Performance Tuning

Hysteria exposes several QUIC transport parameters through QUICConfig. The fill() method enforces minimum floors to prevent protocol violations:

Field Default Minimum Recommendation
InitialStreamReceiveWindow 8 MiB 16 KiB Retain default unless memory-constrained.
MaxStreamReceiveWindow 8 MiB 16 KiB Retain default.
InitialConnectionReceiveWindow 20 MiB 16 KiB Retain default for most workloads.
MaxConnectionReceiveWindow 20 MiB 16 KiB Retain default.
MaxIdleTimeout 30 s 4 s – 120 s Increase to 60 s or higher for high-latency links.
MaxIncomingStreams 1024 8 Increase only if you expect massive concurrent stream counts.

Stick to the defaults unless you have concrete latency or throughput measurements indicating a bottleneck. Never set receive windows below 16 KiB, or validation will fail.

Congestion Control Strategy

Hysteria supports standard QUIC congestion algorithms and BBR (Bottleneck Bandwidth and Round-trip propagation time). The configuration normalizes the requested type via congestion.NormalizeType and validates BBR profiles through congestion.NormalizeBBRProfile:

c.CongestionConfig.Type, err = congestion.NormalizeType(c.CongestionConfig.Type)
if c.CongestionConfig.Type == congestion.TypeBBR {
    c.CongestionConfig.BBRProfile, err = congestion.NormalizeBBRProfile(c.CongestionConfig.BBRProfile)
}

Selection guidelines:

  • Use "bbr" for high-throughput, low-latency networks where you control the endpoints.
  • Fallback to "cubic" (the post-normalization default) for maximum compatibility with varied network conditions.
  • Always explicitly set CongestionConfig.Type to avoid relying on implicit defaults in future versions.

Bandwidth and UDP Resource Limits

Traffic shaping and UDP relay require careful tuning to prevent resource exhaustion.

Bandwidth caps: Both MaxTx (outbound) and MaxRx (inbound) are expressed in bytes per second. The validator enforces a hard floor of 64 KiB (65536):

if c.BandwidthConfig.MaxTx != 0 && c.BandwidthConfig.MaxTx < 65536 {
    return errors.ConfigError{Field: "BandwidthConfig.MaxTx", Reason: "must be at least 65536"}
}

Set realistic limits matching your ISP contract, or leave values at 0 for unlimited throughput.

UDP configuration:

  • Enable UDP relay only when necessary (DNS, media streaming). Set DisableUDP: true to reduce attack surface if you only need TCP proxying.
  • UDPIdleTimeout defaults to 60 seconds and must reside between 2 s and 600 s. Tune downward on resource-constrained servers to free sockets faster.

Authentication and Request Hooks

The server aborts startup if Authenticator is nil:

if c.Authenticator == nil {
    return errors.ConfigError{Field: "Authenticator", Reason: "must be set"}
}

Implementation advice:

  • Implement lightweight, stateless authenticators (e.g., token-based via extras/auth) to maintain scalability.
  • RequestHook is optional and useful for advanced traffic inspection. Remember it only inspects the first packet of a UDP session. Offload heavy processing to separate goroutines to avoid blocking the event loop.

Production Readiness: Logging and Cleanup

Observability and graceful shutdown prevent data leaks and resource exhaustion.

  • Attach loggers: Implement thread-safe EventLogger and TrafficLogger interfaces. Hysteria invokes these concurrently from many streams.
  • Provide cleanup: If you supply a custom net.PacketConn (e.g., a raw socket) via the Conn field, always provide a corresponding io.Closer via the Cleanup field. The server calls Cleanup.Close() during shutdown to release file descriptors properly.

Minimal Implementation Example

The following Go program demonstrates the minimum required fields—TLSConfig, Conn, and Authenticator—while illustrating where to inject optional settings like BBR or bandwidth caps:

package main

import (
    "crypto/tls"
    "log"
    "net"
    "time"

    "github.com/apernet/hysteria/core/server"
    "github.com/apernet/hysteria/extras/auth"
)

func main() {
    // 1. Load TLS certificates (replace with production paths)
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Fatalf("TLS load failed: %v", err)
    }

    // 2. Create UDP listener for QUIC
    udpConn, err := net.ListenPacket("udp", ":8443")
    if err != nil {
        log.Fatalf("UDP listen failed: %v", err)
    }

    // 3. Assemble configuration
    cfg := &server.Config{
        TLSConfig: server.TLSConfig{
            Certificates: []tls.Certificate{cert},
        },
        QUICConfig: server.QUICConfig{
            MaxIdleTimeout: 60 * time.Second, // Optimized for high-latency links
        },
        Conn:          udpConn,
        Authenticator: auth.NewTokenAuthenticator("my-secret-token"),
        // Optional: BandwidthConfig, CongestionConfig, EventLogger, etc.
    }

    // 4. Initialize server (fill() validates internally)
    srv, err := server.NewServer(cfg)
    if err != nil {
        log.Fatalf("Invalid configuration: %v", err)
    }
    defer srv.Close()

    // 5. Block or integrate with your shutdown logic
    select {}
}

This example leverages the automatic validation in NewServer while explicitly setting a longer idle timeout for transcontinental links.

Summary

  • TLS is mandatory: Provide Certificates or GetCertificate; never run unencrypted in production.
  • Respect QUIC floors: Never set window sizes below 16 KiB or idle timeouts outside the 4 s–120 s range.
  • Use BBR wisely: Deploy "bbr" for controlled high-performance environments; default to "cubic" for general compatibility.
  • Enforce bandwidth floors: Set MaxTx and MaxRx to either 0 (unlimited) or ≥ 64 KiB.
  • Require authentication: The Authenticator field cannot be nil.
  • Clean up resources: Always pair custom PacketConn instances with a Cleanup closer and use thread-safe loggers.

Frequently Asked Questions

What happens if I omit TLS certificates in the Hysteria core configuration?

NewServer returns a ConfigError immediately during initialization. The validation logic in core/server/config.go requires either TLSConfig.Certificates or TLSConfig.GetCertificate to be non-nil. Without one of these, the server cannot establish secure QUIC connections.

Should I modify the default QUIC receive window sizes?

Only if you have specific memory constraints or throughput measurements indicating a bottleneck. The defaults (8 MiB for streams, 20 MiB for connections) suit most production workloads. Reducing these values below 16 KiB triggers a validation error, while increasing them unnecessarily raises memory usage per connection.

How do I enable BBR congestion control in Hysteria?

Set CongestionConfig.Type to "bbr" in your server configuration. The fill() method normalizes this via congestion.NormalizeType and validates the BBR profile. BBR excels in high-throughput, low-loss environments but may behave aggressively on congested public networks compared to the default "cubic" algorithm.

Is UDP relay required for HTTP or SOCKS5 proxy functionality?

No, but disabling it (DisableUDP: true) breaks DNS resolution and any UDP-based traffic (such as video calls or gaming) for clients. Keep UDP enabled unless you explicitly require a TCP-only proxy or need to minimize server attack surface. Adjust UDPIdleTimeout between 2 s and 600 s to balance resource retention against connection churn.

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 →