Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


Hysteria2 server setup needs a reachable UDP path, a valid TLS identity, and a separate authentication secret. This guide installs a pinned native binary, keeps administration independent, and checks application TCP and UDP separately before accepting the service.
Key Takeaways
- TCP application traffic still crosses a UDP-based Hysteria transport.
- A certificate authenticates the server; it does not authorize a client.
- A web response is not proof of successful proxy authentication.
- Reject a wrong secret, then repeat rejection after restart and restoration.
Start with the self-hosting ownership and recovery plan. If your purpose is studying a QUIC-based deployment, this procedure gives a bounded acceptance exercise. If you only want an everyday internet exit, compare VPS ownership with a managed service before taking on certificates and host upkeep.
The Hysteria2 protocol overview explains its transport model. If the client network cannot carry this UDP route, review what UDP blocking changes rather than opening TCP and expecting a repair. The Shadowsocks Rust deployment is a separate implementation choice with a different key model.
Scope and evidence: dedicated Ubuntu 24.04 LTS x86-64 server and separate Linux client, Hysteria v2.13.0, public IPv4 endpoint, and an already issued valid certificate for a domain you control. The author has not performed an end-to-end deployment here. The commands and expected conditions are reader instructions based on official sources, not measured results. Other architectures need their matching release asset, not the binary below.[1][2]
Confirm a new SSH connection and provider console independent of the planned proxy. Record ports, routes, DNS, firewall ownership, services, certificate paths, and renewal jobs privately. Reserve UDP 8443 and, on the client, TCP 1080 and UDP 19001. Stop on any collision or existing hysteria-ops user, path, or unit; do not take over another installation.
The example uses high port 8443 so the dedicated service user does not need root or a low-port capability. Install curl, ca-certificates, and Python 3 from Ubuntu's trusted packages if missing. Download from the selected official release, inspect the release's available integrity evidence before executing it, and stop if verification or project ownership is uncertain. A version string alone is not proof of provenance.
umask 077
curl --fail --show-error --location \
https://github.com/HyNetworks/hysteria/releases/download/app%2Fv2.13.0/hysteria-linux-amd64 \
-o hysteria-v2.13.0
chmod 700 hysteria-v2.13.0
./hysteria-v2.13.0 version
sudo install -m 755 hysteria-v2.13.0 /usr/local/bin/hysteria-ops
sudo useradd --system --home /nonexistent --shell /usr/sbin/nologin hysteria-ops
sudo install -d -o root -g hysteria-ops -m 750 /etc/hysteria-ops
The binary version must match v2.13.0. Keep a protected copy of this trusted binary for recovery. Do not invoke an unreviewed remote installation script or silently substitute latest after an error.
Provision a currently valid certificate for your real domain using an approved issuance method before proceeding. Check subject alternative names, expiry, chain, and key match. Both A/AAAA records that clients may select must represent intended reachable paths. This example listens on IPv4; do not leave a misleading AAAA path and call the resulting failure an authentication problem.
Use an ECDSA or RSA server certificate for this baseline. The v2.13.0 client enables Chrome handshake parroting by default, whose advertised signature algorithms exclude Ed25519. Even a valid Ed25519 certificate can therefore fail the handshake; obtain a compatible certificate instead of disabling certificate verification.[6]
Install the full chain and private key at the following paths. Create /etc/hysteria-ops/config.yaml with a root editor, replace the secret with a distinct random value kept in a password manager, and keep it out of shell arguments, public URIs, and logs. This uses manually managed TLS files and password authentication, not ACME and static TLS simultaneously.[3][4]
listen: 0.0.0.0:8443
tls:
cert: /etc/hysteria-ops/fullchain.pem
key: /etc/hysteria-ops/privkey.pem
auth:
type: password
password: "REPLACE_WITH_DISTINCT_RANDOM_SECRET"
disableUDP: false
The TLS key and authentication secret have different purposes. Do not reuse an administrator password or a certificate private key as the client secret. Do not add undocumented congestion, obfuscation, or bandwidth values to solve a basic startup failure. The baseline intentionally omits bandwidth tuning rather than promising a rate it cannot substantiate.
Save this local service unit as /etc/systemd/system/hysteria-ops.service. It is an explicit unit for this article, not a claim about the upstream installer's unit name.
[Unit]
Description=Dedicated Hysteria2 reader deployment
After=network-online.target
Wants=network-online.target
[Service]
User=hysteria-ops
Group=hysteria-ops
ExecStart=/usr/local/bin/hysteria-ops server -c /etc/hysteria-ops/config.yaml
Restart=no
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
[Install]
WantedBy=multi-user.target
Keep independent SSH rules intact. Through the actual provider and host firewall owners, permit only the authorized test client's source to UDP 8443 initially; do not flush tables or disable the firewall. If NAT is present, its authorized mapping must be UDP and point to this host. TCP 8443 is not a substitute for this listener.[3]
sudo chmod 640 /etc/hysteria-ops/config.yaml /etc/hysteria-ops/privkey.pem /etc/hysteria-ops/fullchain.pem
sudo chown root:hysteria-ops /etc/hysteria-ops/config.yaml /etc/hysteria-ops/privkey.pem /etc/hysteria-ops/fullchain.pem
sudo systemctl daemon-reload
sudo systemctl start hysteria-ops
sudo systemctl status hysteria-ops --no-pager
sudo ss -lunp
Expect the selected unit to remain running and an IPv4 UDP listener on 8443. These checks prove only startup and binding. If it exits, read journalctl -u hysteria-ops, separating YAML, file permissions, certificate, and address conflicts. Keep ingress closed while correcting them. Do not solve a TLS error by setting the client to ignore certificates.
On the separate authorized Linux client, obtain the same version's matching binary by the trusted process above. Prepare client.yaml in a private directory. Replace both domain examples and the secret. For the UDP exercise, you need an authorized echo responder you control at udp-check.example.net:9001, reachable from the server; the example name is not a public test service. Without one, leave UDP acceptance unverified.[5][6]
server: hy.example.com:8443
auth: "REPLACE_WITH_DISTINCT_RANDOM_SECRET"
tls:
sni: hy.example.com
insecure: false
socks5:
listen: 127.0.0.1:1080
udpForwarding:
- listen: 127.0.0.1:19001
remote: udp-check.example.net:9001
Both local listeners must remain loopback-only. The UDP forwarding entry sends to the controlled remote responder through Hysteria; it does not modify system-wide routing or create a general LAN proxy. Start the client in a dedicated terminal and keep output private.
chmod 600 client.yaml
./hysteria-v2.13.0 client -c client.yaml
Configure the test browser to use SOCKS5 127.0.0.1:1080 explicitly and enable proxy-side domain resolution where the browser supports it. Then check the visible exit IP and compare the result with the server's known egress. Confirm the actual browser request, not merely a “connected” message.
Stop that client process. Make a private copy of its configuration, change only auth to a deliberately wrong value, and restart it with the copy. A fresh proxied request must be rejected while the endpoint and certificate remain unchanged. Some errors appear when traffic is attempted rather than when the local listener starts; a listening local port is not acceptance. If the request works, close ingress and check accidental direct fallback, another profile, and server auth configuration.
Return to the correct-secret profile. A browser HTTPS request is the application TCP check even though Hysteria carries it over UDP. The following separate reader command sends one UDP payload through the local forwarding listener; the expected reply is the identical payload from your responder. No actual output is claimed here.
python3 - <<'CHECK'
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.settimeout(3)
sock.sendto(b"reader-check", ("127.0.0.1", 19001))
data, _ = sock.recvfrom(1024)
assert data == b"reader-check", "Unexpected UDP reply"
CHECK
| Layer | Reader evidence | On failure |
|---|---|---|
| Application TCP | Fresh proxied HTTPS request and intended exit | Check browser selection, auth, server egress, and destination separately |
| Application UDP | Controlled echo reply matches payload | Check local forwarding, server disableUDP, responder, and return path |
| DNS | Resolver path matches the browser/application policy | Fix local-versus-remote resolution without inferring it from IP alone |
| IPv6 | Each applicable application path is deliberately routed or excluded | Do not call IPv4 acceptance full-device coverage |
| Disconnect | The explicit proxy fails or falls back exactly as configured | Stop sensitive use until the desired failure policy is separately verified |
A conventional TCP port check and a web/HTTP3 response do not prove a Hysteria client authenticated. A returned error page can coexist with rejected proxy access. Keep the wrong-secret check distinct from reachability and the TCP/UDP application checks. This local SOCKS setup supplies no full-device tunnel or kill switch.[3][5]
Restart the dedicated unit during an approved window and repeat correct/wrong secret plus TCP/UDP checks. Only after acceptance should you run systemctl enable hysteria-ops if automatic startup is wanted. Before that choice, verify certificates and configuration remain readable after a host restart.
For certificate renewal, have your existing issuance owner atomically install a complete matching chain/key pair at the configured paths, reapply root:hysteria-ops ownership and 640 modes, and restart this unit in the approved window. Check the renewed identity with a normal verifying client. Expired certificates require certificate repair, not insecure: true; partial renewal requires rollback to the last valid pair or a closed service.
For immediate revocation, close this listener's ingress, change the shared password to a new distinct random secret, and restart the service so existing sessions are terminated. Distribute the new value only to retained clients. The single-password baseline revokes all clients using the old secret; it does not offer individual-user revocation. Verify the retained client works and the old secret fails after another restart.
Back up the stopped configuration and TLS directory, the selected binary and unit, and renewal/firewall records to an encrypted restricted off-host location. Keep a later rotation/revocation ledger separately. To recover, use an isolated replacement running the same version with ingress closed; restore file ownership, permissions, and the valid certificate pair, then apply current secrets from the ledger before startup. Repeat all positive/negative and restart checks before reopening. Do not restore a stale password just because it is in the archive.
The lifecycle commands below are separate operations: restart for acceptance, inspect, then stop/disable when retiring. Do not paste them as one uninterrupted availability test.
sudo systemctl restart hysteria-ops
sudo journalctl -u hysteria-ops --since "10 minutes ago" --no-pager
sudo systemctl stop hysteria-ops
sudo systemctl disable hysteria-ops
When retiring, close only this deployment's UDP rule, stop the client, remove its browser proxy setting and local forwarding listener, and revoke its secrets. After the retention decision, remove only the dedicated unit, binary, files, and user created here; reload systemd. Review renewal jobs, DNS, and cloud resources separately, and verify a new SSH connection plus original services. Software removal does not end cloud billing. If recovery is uncertain, keep ingress closed and stop further changes.
No. This server listens on UDP, including when the proxied application uses TCP. Diagnose the authorized UDP route rather than replacing it with an unrelated TCP rule.
The certificate authenticates the server to the client. The separate password authorizes client access, so both checks must pass and their secrets must remain distinct.
Do not copy an unmeasured rate. This baseline omits bandwidth tuning; introduce documented settings only after obtaining relevant evidence from your own authorized network.
A successful browser HTTPS request covers a different application path. Test UDP with its own supported client mode and a controlled responder, then record that result separately.
This example shares one password, so rotation invalidates every client using it. Individual revocation requires a separately designed supported authentication model, not a misleading device label.
HTTP responses and authenticated proxy requests are different protocol behaviors. Require a real proxied request with correct credentials and a negative request with wrong credentials.
No end-to-end author test is reported. The table defines expected reader evidence; the actual server, client, network, date, and results must be recorded in your authorized environment.
Sources:
Sources checked 11 October 2026.
Related Articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





