# Best Practices for Configuring Hysteria Core: Production Deployment Guide

> Learn best practices for configuring Hysteria core for production deployment. Secure your deployment with TLS certificates and reliable authentication.

- Repository: [Aperture Internet Laboratory/hysteria](https://github.com/apernet/hysteria)
- Tags: best-practices
- Published: 2026-05-13

---

**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`](https://github.com/apernet/hysteria/blob/main/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/main/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](https://github.com/apernet/hysteria/blob/master/core/server/config.go#L45-L73)), 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:

```go
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`:

```go
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`):

```go
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`:

```go
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:

```go
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`](https://github.com/apernet/hysteria/blob/main/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.