2026-06-26 14:09:31 +02:00
2026-06-25 15:00:08 +02:00
2026-06-25 15:57:06 +02:00
2026-06-26 11:42:14 +02:00
2026-06-26 14:09:31 +02:00
2026-06-26 14:09:31 +02:00
2026-06-25 16:11:46 +02:00
2026-06-26 14:01:58 +02:00

Telebit Relay

Self-hosted Telebit relay and management API for running behind an external nginx wildcard HTTPS setup.

Server Setup

Create .env from .env.example:

cp .env.example .env

Set at least:

DOMAIN=example.com
TUNNEL_DOMAIN=example.com
SECRET=change-this-to-a-long-random-secret
POSTGRES_PASSWORD=change-this-postgres-password
TELEBIT_PORT=8085
MGMT_PORT=6468

Start the relay:

docker compose up -d --build

The relay listens on 127.0.0.1:8085 through Docker's published port, and management is bound to 127.0.0.1:6468.

Nginx

Nginx should terminate HTTPS. The relay container is patched to run as a plain HTTP/WebSocket backend and should not handle Let's Encrypt itself.

Use plain http:// upstreams. A standalone template is available in example-nginx.conf:

server {
    listen 80;
    listen [::]:80;
    server_name t.example.com *.t.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name t.example.com *.t.example.com;

    ssl_certificate /etc/letsencrypt/live/t.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/t.example.com/privkey.pem;

    location = /ws {
        proxy_pass http://127.0.0.1:8085/ws;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header Authorization $http_authorization;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:6468/api/;
        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;
    }

    location / {
        proxy_pass http://127.0.0.1:8085;
        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;
    }
}

Reload:

sudo nginx -t
sudo systemctl reload nginx

Client Wrapper

telebitctl manages local client keys and runs the Telebit client.

Build local binaries with Docker:

./telebitctl build

This writes:

bin/telebit
bin/signjwt

Create a new tunnel key for a subdomain:

./telebitctl bind test

This creates test.$DOMAIN and writes .telebit-clients/test.env.

Run the tunnel to a local service:

./telebitctl up test 80

That forwards:

https://test.$DOMAIN -> localhost:80

up uses a per-slug client config. If .telebit-clients/test.env already exists, it reuses that key and only updates the local port. If it does not exist, it binds test before starting the tunnel.

Because each slug has its own env file, multiple tunnels can run at the same time:

telebitctl up app 3000
telebitctl up api 8080

up restarts the client if the WebSocket connection drops. By default it waits 5 seconds and retries forever. You can tune that with:

TELEBIT_RETRY_DELAY=10 telebitctl up app 3000
TELEBIT_RETRY_MAX=3 telebitctl up app 3000

Rotate the client key:

./telebitctl auth refresh test

Release a subdomain:

./telebitctl release test

Notes

  • .env, .telebit-clients/, telebit-client.env, and bin/ are ignored by git.
  • SECRET in .env is the management/relay master secret. Do not use it directly as a long-lived client key.
  • Client keys are one-time registration secrets. Use bind or auth refresh to create device-specific keys.
  • If /ws returns nginx 502, nginx is probably still proxying to https://127.0.0.1:8085; it must use http://127.0.0.1:8085.
Description
No description provided
Readme 60 KiB
Languages
Shell 90.8%
Dockerfile 9.2%