Tunnel Server
    SSH-secured

    Deploy Chisel on a RamNode VPS

    A fast TCP/UDP tunnel transported over HTTP and secured with SSH — SOCKS5, reverse forwards, multiplexed tunnels, and a self-hosted ngrok replacement with no rate limits.

    At a Glance

    ProjectChisel v1.11.5
    LicenseMIT
    Recommended PlanRamNode KVM 1–2 GB (over-provisioned)
    OSUbuntu 24.04 / Debian 12 / AlmaLinux 9
    Listen Port443 (HTTPS, WebSocket upgrade)
    Estimated Setup Time20–30 minutes

    Common Use Cases

    • Exposing services behind CGNAT, residential ISPs, or restrictive corporate firewalls
    • Personal SOCKS5 proxy for browsing on untrusted networks
    • Tunneling SSH, databases, or admin panels to a developer's workstation
    • Bridging two private networks through a single public relay
    • Replacing ngrok for development tunneling — no rate limits, no session timeouts
    1

    Initial Server Preparation

    Patch + utilities
    apt update && apt upgrade -y
    apt install -y curl wget ufw fail2ban
    hostnamectl set-hostname chisel.example.com
    timedatectl set-timezone America/Chicago
    Dedicated unprivileged user + dirs
    useradd --system --no-create-home --shell /usr/sbin/nologin chisel
    mkdir -p /etc/chisel /var/lib/chisel
    chown chisel:chisel /etc/chisel /var/lib/chisel
    chmod 750 /etc/chisel /var/lib/chisel

    The daemon never needs root — a systemd capability will grant it 443 binding without it.

    2

    Install Chisel

    Ubuntu/Debian .deb (preferred)
    cd /tmp
    ARCH=$(dpkg --print-architecture)
    VERSION=1.11.5
    wget "https://github.com/jpillora/chisel/releases/download/v${VERSION}/chisel_${VERSION}_linux_${ARCH}.deb"
    apt install -y "./chisel_${VERSION}_linux_${ARCH}.deb"
    chisel --version
    AlmaLinux/Rocky/RHEL .rpm
    cd /tmp
    VERSION=1.11.5
    wget "https://github.com/jpillora/chisel/releases/download/v${VERSION}/chisel_${VERSION}_linux_amd64.rpm"
    dnf install -y "./chisel_${VERSION}_linux_amd64.rpm"

    Avoid the convenience installer (curl https://i.jpillora.com/chisel! | bash) on production servers — pin to a specific release tag and verify checksums.

    3

    Generate a Persistent Server Key

    By default Chisel generates a fresh ECDSA key on each restart, breaking pinned-fingerprint clients. Generate once:

    --keygen
    sudo -u chisel chisel server --keygen /etc/chisel/server.key
    chmod 600 /etc/chisel/server.key
    chown chisel:chisel /etc/chisel/server.key

    Record the printed fingerprint — clients will pin it with --fingerprint to detect MITM.

    4

    Create the Auth File With Per-User ACLs

    /etc/chisel/users.json
    {
      "alice:6Pd9mK3vN8qR2xL5wTyZ": [
        "^0\\.0\\.0\\.0:1080quot;,
        "^R:0\\.0\\.0\\.0:[0-9]+quot;
      ],
      "bob:Hf4Xs7BcVj9QnE2RtMpY": [
        "^192\\.168\\.10\\.[0-9]{1,3}:(22|3306|5432|6379)quot;
      ],
      "ops:Lq8Wn3KvD5tBhJ7MzCxF": [
        ""
      ]
    }
    Lock it down
    chown chisel:chisel /etc/chisel/users.json
    chmod 600 /etc/chisel/users.json

    Patterns are full Go regexes. Forward addresses come through as host:port; reverse as R:iface:port. Empty string matches everything. Generate strong passwords with openssl rand -base64 18. The file is reloaded automatically when changed.

    5

    TLS — Built-in Let's Encrypt or nginx

    Option A — built-in: Chisel binds 443 directly and uses TLS-ALPN-01 (no port 80 required). Cleanest if Chisel is the only thing on the box.

    Option A prep
    dig +short chisel.example.com
    mkdir -p /var/lib/chisel/.cache
    chown -R chisel:chisel /var/lib/chisel

    Option B — nginx: required if you also host a website or need WAF/rate-limiting. nginx terminates TLS and proxies the WebSocket upgrade.

    Option B nginx server block
    server {
        listen 443 ssl http2;
        server_name chisel.example.com;
        ssl_certificate     /etc/letsencrypt/live/chisel.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/chisel.example.com/privkey.pem;
    
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_http_version 1.1;
            proxy_set_header Host              $host;
            proxy_set_header X-Real-IP         $remote_addr;
            proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            # Critical for Chisel
            proxy_set_header Upgrade           $http_upgrade;
            proxy_set_header Connection        "upgrade";
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
            proxy_buffering    off;
        }
    }
    server { listen 80; server_name chisel.example.com; return 301 https://$host$request_uri; }
    6

    systemd Service

    /etc/systemd/system/chisel.service (Option A)
    [Unit]
    Description=Chisel TCP/UDP tunnel server
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=chisel
    Group=chisel
    Environment=CHISEL_LE_CACHE=/var/lib/chisel/.cache
    Environment=CHISEL_LE_EMAIL=you@example.com
    
    ExecStart=/usr/local/bin/chisel server \
        --port 443 \
        --keyfile /etc/chisel/server.key \
        --authfile /etc/chisel/users.json \
        --tls-domain chisel.example.com \
        --reverse \
        --socks5 \
        --keepalive 25s \
        --backend https://www.google.com
    
    Restart=on-failure
    RestartSec=5s
    
    # Hardening
    AmbientCapabilities=CAP_NET_BIND_SERVICE
    CapabilityBoundingSet=CAP_NET_BIND_SERVICE
    NoNewPrivileges=true
    PrivateTmp=true
    PrivateDevices=true
    ProtectSystem=strict
    ProtectHome=true
    ProtectKernelTunables=true
    ProtectKernelModules=true
    ProtectControlGroups=true
    RestrictNamespaces=true
    RestrictRealtime=true
    LockPersonality=true
    ReadWritePaths=/var/lib/chisel
    SystemCallArchitectures=native
    SystemCallFilter=@system-service
    
    [Install]
    WantedBy=multi-user.target
    • --reverse required for any client using R: tunnels
    • --socks5 enables the server-side SOCKS5 proxy
    • --keepalive 25s keeps connections alive through aggressive intermediate proxies
    • --backend https://www.google.com proxies non-Chisel HTTP requests to Google so casual scanners see nothing interesting
    Start + tail
    systemctl daemon-reload
    systemctl enable --now chisel
    journalctl -u chisel -f

    For Option B, drop --tls-domain and the CHISEL_LE_* env vars and bind to --host 127.0.0.1 --port 8080.

    7

    Firewall

    UFW (Ubuntu/Debian)
    ufw default deny incoming
    ufw default allow outgoing
    ufw allow 22/tcp
    ufw allow 443/tcp
    ufw enable
    firewalld (Alma/Rocky)
    firewall-cmd --permanent --add-service=ssh
    firewall-cmd --permanent --add-service=https
    firewall-cmd --reload

    With built-in Let's Encrypt, port 80 is not required — TLS-ALPN-01 happens on 443.

    8

    Connect a Client

    The same binary runs as both server and client. Replace Vxk9...trailing= with the real fingerprint from Step 3.

    Example 1 — SOCKS5 for browsing
    chisel client \
      --fingerprint 'Vxk9...trailing=' \
      --auth 'alice:6Pd9mK3vN8qR2xL5wTyZ' \
      https://chisel.example.com \
      1080:socks
    Example 2 — reverse-tunnel a home service
    chisel client \
      --fingerprint 'Vxk9...trailing=' \
      --auth 'alice:6Pd9mK3vN8qR2xL5wTyZ' \
      https://chisel.example.com \
      R:8096:localhost:8096
    Example 3 — pull a remote port locally
    chisel client \
      --fingerprint 'Vxk9...trailing=' \
      --auth 'bob:Hf4Xs7BcVj9QnE2RtMpY' \
      https://chisel.example.com \
      5432:10.0.0.5:5432
    Client systemd unit (always-on)
    [Unit]
    Description=Chisel client tunnel
    After=network-online.target
    
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/chisel client \
        --fingerprint 'Vxk9...trailing=' \
        --auth 'alice:6Pd9mK3vN8qR2xL5wTyZ' \
        --keepalive 25s \
        --max-retry-interval 30s \
        https://chisel.example.com \
        R:8096:localhost:8096
    Restart=always
    RestartSec=10s
    
    [Install]
    WantedBy=multi-user.target
    9

    Hardening & Observability

    Fail2ban for failed auths — add -v to the server ExecStart, then:

    /etc/fail2ban/filter.d/chisel.conf
    [Definition]
    failregex = Failed authentication.*from <HOST>
    ignoreregex =
    /etc/fail2ban/jail.d/chisel.conf
    [chisel]
    enabled  = true
    port     = 443
    filter   = chisel
    backend  = systemd
    journalmatch = _SYSTEMD_UNIT=chisel.service
    maxretry = 5
    findtime = 600
    bantime  = 3600

    Monitoring: GET /health returns 200, GET /version returns the version string — both pre-auth, ideal for Uptime Kuma. systemctl kill -s USR2 chisel dumps tunnel stats to the journal.

    10

    Troubleshooting

    • "Fingerprint mismatch": wrong fingerprint, server regenerated its key (missing --keyfile), or genuine MITM. Re-run chisel server --keygen to confirm.
    • TLS-ALPN challenge fails on first start: verify A record propagation, port 443 reachability, and that nothing else is bound to 443 (ss -tlnp | grep :443).
    • Reverse tunnel works but port unreachable: add a corresponding ufw allow on the server side. Reverse remotes bind on the server.
    • Connection drops every 30–60s through a corporate proxy: lower --keepalive on both ends.

    What's Next

    • Front sensitive reverse-tunneled services with nginx + TLS before exposing them publicly
    • Subscribe to the Chisel GitHub release feed and update with apt install ./chisel_*.deb
    • Pair with WireGuard or Tailscale for cases where a full L3 mesh is a better fit