Skip to content

Latest commit

 

History

60 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Xray + Hysteria Proxy via Docker

Deploy a Docker proxy stack with VLESS + WebSocket + CDN, VLESS + XTLS-Reality, and/or Hysteria 2.

The stack is built around one .env file. WebSocket mode gets a Let's Encrypt certificate through nginx/acme, and Hysteria reuses that same certificate from ./certs/<domain>.crt and ./certs/<domain>.key.


Modes

Mode Transport Public port Cloudflare/CDN Domain Notes
VLESS + WS + CDN WebSocket over TLS TCP 443 Yes Required Nginx terminates TLS and proxies to Xray
VLESS + Reality TCP + Reality TLS camouflage TCP 443 or 2083 No Not required Direct to Xray; use server IP
Hysteria 2 QUIC/UDP UDP 443 No Required for cert/SNI Direct UDP; reuses the nginx/acme cert

You can install one mode, WS + Reality together, or all three together.


Quick Install

Before running the installer for WS/Both/All modes, point your domain's DNS A record to the server IP and keep it DNS only (grey cloud) in Cloudflare — the installer verifies this and waits for the Let's Encrypt certificate. You enable the orange-cloud proxy after the certificate is issued.

Run the current branch installer:

sudo bash <(curl -fsSL https://raw.githubusercontent.com/samrand96/v2ray-nginx-cloudflare/backup/install.sh)

The installer asks you to choose:

  1. VLESS + WebSocket + CDN
  2. VLESS + XTLS-Reality
  3. Hysteria 2 only, using an existing cert in ./certs
  4. WS + Reality
  5. All three

For Hysteria-only, run WS/Both/All first or copy an existing certificate into ./certs before starting Hysteria.


How It Works

VLESS + WebSocket + CDN

Client -> Cloudflare/CDN -> Nginx TLS -> Xray VLESS-WS on 1310

Use this when you want CDN-fronted HTTPS traffic. During certificate issuance, keep the DNS record pointed to the server and disable Cloudflare proxy until Let's Encrypt succeeds.

VLESS + XTLS-Reality

Client -> Xray Reality TCP port

Use this direct to the server IP. Do not put Reality behind nginx or Cloudflare.

Hysteria 2

Client -> Hysteria UDP port -> direct IPv4 outbound

Hysteria uses the same certificate files that nginx/acme created for your domain. It can share numeric port 443 with nginx because nginx listens on TCP and Hysteria listens on UDP. Cloudflare proxy does not carry this UDP traffic, so connect to the server IP or a DNS-only hostname and keep SNI set to the certificate domain.


Project Structure

.env                                  # Generated runtime configuration
.env.example                          # Example configuration
install.sh                            # Main interactive installer
install-hysteria.sh                   # Standalone Hysteria installer
tiny-vps-mode.sh                      # Stop helper containers after cert issuance
generate-config.sh                    # Regenerate Xray config from .env
generate-hysteria-config.sh           # Regenerate Hysteria config from .env
docker-compose.yml                    # WS-only stack
docker-compose.reality.yml            # Reality-only stack
docker-compose.modular.yml            # WS + Reality stack
docker-compose.hysteria.yml           # Hysteria-only stack
hysteria/config.template.yaml         # Hysteria template
hysteria/config.yaml                  # Generated Hysteria config, ignored by git
vhost/default                         # Server-level nginx include
vhost/default_location                # Location-level nginx include
v2ray/config/*.template.json          # Xray templates
v2ray/config/config.json              # Generated Xray config, ignored by git

Client Links

VLESS + WebSocket

vless://<UUID>@<CLOUDFLARE-IP>:443?type=ws&security=tls&path=%2F&host=<DOMAIN>&sni=<DOMAIN>&encryption=none#VLESS-WS

Settings: network ws, path /, TLS enabled, Host/SNI set to your domain.

VLESS + Reality

vless://<UUID>@<SERVER-IP>:<REALITY-PORT>?type=tcp&security=reality&pbk=<PUBLIC-KEY>&fp=chrome&sni=<REALITY-SNI>&sid=<SHORT-ID>&flow=xtls-rprx-vision#VLESS-Reality

Settings: address is the server IP, not the CDN domain. Use the generated public key, short ID, and Reality SNI from .env.

Hysteria 2

hysteria2://<PASSWORD>@<SERVER-IP>:<UDP-PORT>/?sni=<DOMAIN>#Hysteria2

Settings: address is the server IP or a DNS-only hostname, SNI is the domain whose cert exists in ./certs, and the firewall must allow UDP on the chosen port.


Manual Commands

Regenerate Xray:

./generate-config.sh
docker compose -f docker-compose.modular.yml restart v2ray

Regenerate Hysteria:

./generate-hysteria-config.sh
docker compose -f docker-compose.hysteria.yml restart hysteria

Install Hysteria separately after certs exist:

sudo bash install-hysteria.sh

View logs:

docker compose -f docker-compose.modular.yml logs -f
docker compose -f docker-compose.hysteria.yml logs -f hysteria

Tiny VPS Performance

The default configs are tuned for small VPS boxes:

  • Docker logs are capped at 2m x 2 files per container.
  • Xray uses plain IPv4 DNS instead of loading geosite/geoip databases.
  • Xray log level is warning and nginx access logs are disabled.
  • Hysteria uses direct IPv4 outbound and a short sniff timeout.
  • Compose applies conservative memory guardrails: nginx 96m, Xray 128m, Hysteria 128m, docker-gen 64m, ACME 96m.

After certificates are issued, you can stop the nginx helper containers to save RAM:

./tiny-vps-mode.sh docker-compose.modular.yml

That keeps nginx and Xray running, but stops dockergen and nginx-proxy-acme. Restart those helpers before changing proxy config or renewing certificates.


Troubleshooting

Nginx/WS

docker compose -f docker-compose.modular.yml ps
docker compose -f docker-compose.modular.yml logs nginx
docker compose -f docker-compose.modular.yml logs nginx-proxy-acme
docker compose -f docker-compose.modular.yml exec nginx nginx -t

If ACME fails, confirm port 80/tcp is open, DNS points to the server IP, and Cloudflare proxy is disabled during issuance.

To test port 80 from another machine: curl -I http://<domain>/ — any HTTP response (even 403/503) means the port is open; a timeout means it is blocked. Open it with ufw allow 80/tcp or firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --reload, and remember that provider firewalls (AWS security groups, Oracle, Hetzner, GCP, ...) must be opened in the provider's panel as well.

The installer uses ACME_FORCE_IPV4=auto by default: it leaves the ACME companion dual-stack when IPv6 connectivity works, and only forces outbound Let's Encrypt API calls over IPv4 when IPv6 routing is broken. Set ACME_FORCE_IPV4=1 to always force IPv4, or ACME_FORCE_IPV4=0 to never force it.

If the certificate is missing after a failed attempt, the ACME companion only retries once per hour on its own. Trigger an immediate retry with:

docker exec nginx-proxy-acme /app/signal_le_service

The installer waits up to 300 seconds for the certificate; on slow networks you can extend this with CERT_WAIT_TIMEOUT=600 sudo -E bash install.sh.

Reality

grep REALITY .env
docker compose -f docker-compose.modular.yml logs -f v2ray
ss -lntp | grep -E ':(443|2083)\b'

Reality must be tested against the server IP and the exact generated REALITY_PUBLIC_KEY, REALITY_SHORT_ID, and REALITY_SERVER_NAME.

Hysteria

grep HYSTERIA .env
ls -l certs/${DOMAIN}.crt certs/${DOMAIN}.key
docker compose -f docker-compose.hysteria.yml ps
docker compose -f docker-compose.hysteria.yml logs -f hysteria
ss -lunp | grep ":${HYSTERIA_PORT:-443}"

If Hysteria does not connect, check that UDP is open on the server firewall/provider firewall. If the domain is orange-cloud proxied in Cloudflare, still use the server IP as the client address and set sni=<DOMAIN>.


Uninstall

docker compose -f docker-compose.modular.yml down
docker compose -f docker-compose.reality.yml down
docker compose -f docker-compose.hysteria.yml down

About

Install v2ray using docker with compatibility with Cloudflare and other CDN easily

Topics

Resources

Stars

21 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages