main
md 208 lines 6.23 KB
Rendered Raw
1 ---
2 title: Self-Hosting
3 description: Run your own Portal relay for private tunneling.
4 ---
5
6 # Self-Hosting Guide
7
8 This guide is for developers who want their own API-only relay for a single
9 project or team. It runs the `portal` relay image directly and exposes relay API
10 paths plus tunnel ingress without the hosted dashboard, `/ui/*` presentation API,
11 generated thumbnails, or frontend-owned landing page state.
12
13 If you need the browser dashboard and presentation API behind one public HTTPS
14 origin, use the [Deployment Guide](/deployment) instead.
15
16 You should have a relay running and accepting tunnel connections in about 10 minutes.
17
18 ## Prerequisites
19
20 - Docker installed on your server
21 - A Linux server with a static public IP
22 - A domain name you control (e.g. `relay.example.com`)
23 - Inbound ports open on your server:
24 - `443/tcp` — SNI router (tunnel traffic)
25 - `4017/tcp` — Admin/API port (tunnel registration)
26
27 ## Quick Start
28
29 Run the relay with a single Docker command:
30
31 ```bash
32 mkdir -p ./relay-data
33 # Put fullchain.pem and privatekey.pem in ./relay-data first, or configure ACME below.
34 docker run -d \
35 --name portal-relay \
36 --restart unless-stopped \
37 -p 443:443 \
38 -p 4017:4017 \
39 -e PORTAL_URL=https://relay.example.com:4017 \
40 -e IDENTITY_PATH=/portal-certs \
41 -e ADMIN_TOKEN="$(openssl rand -hex 32)" \
42 -v $(pwd)/relay-data:/portal-certs \
43 ghcr.io/gosuda/portal:2
44 ```
45
46 Replace `relay.example.com` with your domain. Keep the generated
47 `ADMIN_TOKEN`; it is required for relay admin and policy access.
48
49 ## Docker Compose Setup
50
51 For a more maintainable setup, use Docker Compose:
52
53 ```yaml
54 # compose.yml
55 services:
56 relay:
57 image: ghcr.io/gosuda/portal:2
58 restart: unless-stopped
59 ports:
60 - "443:443"
61 - "4017:4017"
62 environment:
63 PORTAL_URL: https://relay.example.com:4017
64 API_PORT: "4017"
65 SNI_PORT: "443"
66 IDENTITY_PATH: /portal-certs
67 ADMIN_TOKEN: ${ADMIN_TOKEN}
68 volumes:
69 - ./relay-data:/portal-certs
70 ```
71
72 Start it:
73
74 ```bash
75 docker compose up -d
76 ```
77
78 ### Key Environment Variables
79
80 | Variable | Default | Description |
81 |---|---|---|
82 | `PORTAL_URL` | `https://localhost:4017` | Public base URL of your relay. Tunnels use this to register. |
83 | `API_PORT` | `4017` | Admin/API server port. |
84 | `SNI_PORT` | `443` | TCP SNI router port for tunnel traffic. |
85 | `IDENTITY_PATH` | `./.portal-certs` | Relay state directory containing `identity.json`, `policy.json`, and TLS materials. |
86 | `ADMIN_TOKEN` | | Bearer token source for relay admin and policy APIs. |
87
88 ## Optional: Enable Relay-Owned Sui x402 Facilitator
89
90 To reserve relay-side x402 support for future control-plane resources, enable
91 the relay-owned facilitator. This is intended for relay-owned charges such as
92 tunnel registration, lease renewal, raw TCP/UDP port allocation, or premium
93 capacity if an operator decides to require them. Payments use Sui mainnet by
94 default; set `X402_TESTNET=true` for Sui testnet.
95
96 ```yaml
97 environment:
98 X402_ENABLED: "true"
99 X402_TESTNET: "false"
100 X402_PAY_TO: "0x..."
101 ```
102
103 This serves `/api/x402/supported`, `/api/x402/verify`, and `/api/x402/settle`.
104 Portal payments intentionally support only Sui mainnet/testnet USDC through the
105 gasless stablecoin address-balance flow.
106
107 Tunnel paid routes do not use these relay settings. Route-level payment
108 enforcement is configured separately by the tunnel with
109 `portal expose --x402-pay-to` and optional `--x402-testnet`; relay
110 `X402_PAY_TO` and `X402_TESTNET` are reserved for relay-owned control-plane
111 resources.
112
113 ## Connecting Your Tunnel
114
115 Point `portal-tunnel` at your relay with the `--relays` flag:
116
117 ```bash
118 portal expose --relays https://relay.example.com:4017 --discovery=false localhost:3000
119 ```
120
121 The `--relays` flag accepts a comma-separated list of relay API URLs. If you omit the scheme, `https` is assumed.
122
123 To avoid typing `--relays` every time, use a shell alias:
124
125 ```bash
126 alias portal-relay='portal expose --relays https://relay.example.com:4017 --discovery=false'
127 portal-relay localhost:3000
128 ```
129
130 ## DNS Configuration
131
132 Tunnels are assigned subdomains under your relay domain (e.g. `abc123.relay.example.com`). You need a wildcard DNS record pointing to your server:
133
134 | Type | Name | Value |
135 |---|---|---|
136 | `A` | `*.relay.example.com` | `<your server IP>` |
137 | `A` | `relay.example.com` | `<your server IP>` |
138
139 DNS propagation typically takes a few minutes but can take up to 48 hours depending on your provider.
140
141 ## Optional: TLS with ACME
142
143 By default the relay expects you to place `fullchain.pem` and `privatekey.pem` in the `IDENTITY_PATH` directory (`.portal-certs` by default). For automatic certificate management via DNS-01 challenges, set `ACME_DNS_PROVIDER`:
144
145 ```yaml
146 environment:
147 ACME_DNS_PROVIDER: cloudflare # or: gcloud, hetzner, njalla, route53, vultr
148 CLOUDFLARE_TOKEN: <your-token>
149 ```
150
151 See the [Deployment Guide](/deployment) for full ACME configuration options, credential setup per provider, and managed DNS automation.
152
153 ## Optional: Enable TCP/UDP Tunneling
154
155 To relay raw TCP or UDP traffic (game servers, databases, etc.), enable the transports and set a port range:
156
157 ```yaml
158 environment:
159 TCP_ENABLED: "true"
160 UDP_ENABLED: "true"
161 MIN_PORT: "10000"
162 MAX_PORT: "10100"
163 ports:
164 - "10000-10100:10000-10100/tcp"
165 - "10000-10100:10000-10100/udp"
166 ```
167
168 See [TCP/UDP Tunneling](/tcp-udp-tunneling) for usage details.
169
170 ## Troubleshooting
171
172 **Port already in use**
173
174 Port `443` is commonly taken by another process. Check what's listening:
175
176 ```bash
177 sudo ss -tlnp | grep ':443'
178 ```
179
180 Stop the conflicting service or change `SNI_PORT` and update your firewall rules accordingly.
181
182 **DNS not resolving**
183
184 Verify your wildcard record is live before connecting a tunnel:
185
186 ```bash
187 dig +short test.relay.example.com
188 ```
189
190 If nothing returns, check your DNS provider dashboard and allow more time for propagation.
191
192 **Firewall blocking connections**
193
194 Ensure both ports are open in your cloud provider's security group or firewall:
195
196 ```bash
197 # UFW example
198 sudo ufw allow 443/tcp
199 sudo ufw allow 4017/tcp
200 ```
201
202 **Certificate errors**
203
204 If you see TLS errors on the client side, confirm your certificate files are present in `IDENTITY_PATH` and that `fullchain.pem` includes the full chain (leaf + intermediates). If using ACME, check the relay logs for DNS provider authentication errors:
205
206 ```bash
207 docker compose logs relay --tail 50
208 ```