Beginner
    15–20 min
    512 MB+ RAM

    Self-Host a Bore Tunnel Server on a RamNode VPS

    Lightweight, open-source TCP tunneling written in Rust. Expose local ports to the internet through a server you control — no usage limits, no accounts, no third-party involvement.

    What Is Bore?

    Bore has two components: bore server runs on your VPS and listens for incoming tunnel connections, and bore local runs on your local machine and forwards a local port through the server. When a client connects, bore assigns a remote port on the server side. Anyone connecting to your-vps:remote-port gets routed transparently to localhost:local-port on the client machine.

    Expose a development web server for demos or testing
    Share a local API for webhook debugging
    Make a home lab service reachable without a static IP
    Single static binary — no runtime dependencies

    Requirements

    • A RamNode VPS running Ubuntu 22.04 LTS or Debian 12
    • Root or sudo access
    • A domain name pointed at your VPS (optional but recommended)
    • Port 7835 open for control connections (configurable)
    • Port range open for tunneled connections (recommended: 10000–20000)
    1

    Update the System

    Update packages
    apt update && apt upgrade -y
    2

    Install Bore

    Bore ships as a single static binary. Download the latest release from GitHub:

    Download and install bore
    BORE_VERSION=$(curl -s https://api.github.com/repos/ekzhang/bore/releases/latest \
      | grep '"tag_name"' | cut -d '"' -f 4)
    
    curl -Lo /tmp/bore.tar.gz \
      "https://github.com/ekzhang/bore/releases/download/${BORE_VERSION}/bore-${BORE_VERSION}-x86_64-unknown-linux-musl.tar.gz"
    
    tar -xzf /tmp/bore.tar.gz -C /tmp
    install -m 755 /tmp/bore /usr/local/bin/bore
    Verify
    bore --version
    3

    Create a Dedicated Service User

    Running bore under a dedicated unprivileged user limits blast radius if anything goes wrong:

    Create bore user
    useradd --system --no-create-home --shell /usr/sbin/nologin bore
    4

    Configure the Firewall

    Allow the bore control port and a restricted range for tunneled connections:

    Configure UFW
    ufw allow 7835/tcp comment "bore control"
    ufw allow 10000:20000/tcp comment "bore tunnels"
    ufw enable

    Adjust the port range to match your bore server configuration.

    5

    Create the systemd Service

    For production, always set a --secret. Without one, anyone who knows your server address can create tunnels through it.

    Generate a secret
    openssl rand -hex 32
    /etc/systemd/system/bore.service
    [Unit]
    Description=Bore Tunnel Server
    After=network.target
    
    [Service]
    Type=simple
    User=bore
    Group=bore
    ExecStart=/usr/local/bin/bore server \
      --control-port 7835 \
      --min-port 10000 \
      --max-port 20000 \
      --secret YOUR_SECRET_HERE
    Restart=on-failure
    RestartSec=5
    NoNewPrivileges=true
    ProtectSystem=strict
    ProtectHome=true
    PrivateTmp=true
    
    [Install]
    WantedBy=multi-user.target

    Replace YOUR_SECRET_HERE with the generated value.

    FlagDefaultDescription
    --control-port7835Port bore clients connect to
    --min-port1024Minimum port for tunnel assignments
    --max-port65535Maximum port for tunnel assignments
    --secret(none)Shared secret to restrict access
    6

    Start and Enable the Service

    Enable and start bore
    systemctl daemon-reload
    systemctl enable bore
    systemctl start bore
    systemctl status bore
    Expected output
    ● bore.service - Bore Tunnel Server
         Loaded: loaded (/etc/systemd/system/bore.service; enabled)
         Active: active (running) since ...
    7

    Test the Connection from a Local Machine

    Install bore on your local machine, then open a tunnel:

    macOS (Homebrew)
    brew install bore-cli
    Open a tunnel
    bore local 3000 --to your-vps-ip --secret YOUR_SECRET_HERE
    Expected output
    2024-01-15T12:00:00Z  INFO bore_cli::client: listening at your-vps-ip:10432

    Your local port 3000 is now reachable at http://your-vps-ip:10432 from anywhere.

    8

    Pin a Specific Remote Port (Optional)

    Use the --port flag for a consistent port — useful for webhooks or shared dev environments:

    Pin remote port
    bore local 3000 --to your-vps-ip --port 10080 --secret YOUR_SECRET_HERE

    Security Hardening

    Restrict tunnel port access by IP

    Allow only specific clients
    ufw allow from 203.0.113.42 to any port 10000:20000 proto tcp
    ufw delete allow 10000:20000/tcp

    Rotate the secret

    Edit /etc/systemd/system/bore.service, update the secret value, then:

    Apply new secret
    systemctl daemon-reload
    systemctl restart bore

    Putting a Domain in Front (Optional)

    Use nginx as a reverse proxy to route a subdomain to a specific tunnel port (e.g., dev.yourdomain.com instead of your-vps-ip:10080).

    Install nginx
    apt install nginx -y
    /etc/nginx/sites-available/bore-tunnel
    server {
        listen 80;
        server_name dev.yourdomain.com;
    
        location / {
            proxy_pass http://127.0.0.1:10080;
            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;
        }
    }
    Enable and add HTTPS
    ln -s /etc/nginx/sites-available/bore-tunnel /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx
    
    apt install certbot python3-certbot-nginx -y
    certbot --nginx -d dev.yourdomain.com

    Updating Bore

    Update to latest version
    systemctl stop bore
    
    BORE_VERSION=$(curl -s https://api.github.com/repos/ekzhang/bore/releases/latest \
      | grep '"tag_name"' | cut -d '"' -f 4)
    
    curl -Lo /tmp/bore.tar.gz \
      "https://github.com/ekzhang/bore/releases/download/${BORE_VERSION}/bore-${BORE_VERSION}-x86_64-unknown-linux-musl.tar.gz"
    
    tar -xzf /tmp/bore.tar.gz -C /tmp
    install -m 755 /tmp/bore /usr/local/bin/bore
    
    systemctl start bore
    bore --version

    Quick Reference

    TaskCommand
    Start serversystemctl start bore
    Stop serversystemctl stop bore
    View logsjournalctl -u bore -f
    Open tunnelbore local PORT --to VPS_IP --secret SECRET
    Fixed port tunnelbore local PORT --to VPS_IP --port REMOTE --secret SECRET

    Troubleshooting

    Client cannot connect to the server

    Check that port 7835 is open in UFW and that the bore service is running. Verify with systemctl status bore and journalctl -u bore -f.

    Tunnel connects but service is unreachable

    Ensure the tunnel port range (10000–20000) is open in UFW. Check that the assigned port falls within your configured range.

    Authentication failed

    The secret on the client must exactly match the server. Check for trailing whitespace or mismatched values.