Add auto-update script and custom Docker Compose examples
rabbitprincess committed
Jun 2, 2026 at 22:20 UTC
9e8ede1ebe44c8680d4ebbde6ac2d4996586c701
13 files changed
+96
-884
.gitignore
-4
@@ -41,10 +41,6 @@ profile.cov
41
42
/target
43
44
-# Docker Compose Override
45
-docker-compose.override.yml
46
-docker-compose.override.yaml
47
-
44
# Added by goreleaser init:
45
dist/
46
docs/src/routes/configuration/+page.md
+1
-1
@@ -80,7 +80,7 @@ variables:
80
| `PORT` | `8081` | int | Frontend API HTTP listen port |
81
| `PORTAL_API_BASE_URL` | `https://portal:4017` | string | Relay API base URL used to compose frontend-owned state |
82
| `LANDING_PAGE_ENABLED` | `false` | bool | Default landing page flag when no frontend state has been saved yet |
83
-| `PORTAL_FRONTEND_STATE_PATH` | `""` | string | Optional JSON file path for persisted frontend-owned state; bundled Compose stores this under `./.portal-certs/frontend-state/state.json` |
83
+| `PORTAL_FRONTEND_STATE_PATH` | `""` | string | Optional JSON file path for persisted frontend-owned state; a Compose deployment can store this under `./.portal-certs/frontend-state/state.json` |
84
| `HEADLESS_SHELL_URL` | `""` | string | Headless Chrome CDP WebSocket URL; leave empty to disable generated thumbnails |
85
86
### Cloudflare
docs/src/routes/deployment/+page.md
+52
-34
@@ -104,7 +104,7 @@ Open only the public ports that match the topology:
104
105
| Port | Required | Purpose |
106
|---|---|---|
107
-| `80/tcp` | optional | HTTP to HTTPS redirect in the bundled nginx example |
107
+| `80/tcp` | optional | HTTP to HTTPS redirect in a front nginx |
108
| `443/tcp` | yes | Public nginx edge for dashboard, relay API path routing, and wildcard TCP passthrough |
109
| `WIREGUARD_PORT/udp` | when `DISCOVERY=true` | Relay discovery WireGuard transport |
110
| `SNI_PORT/udp` | when UDP transport is enabled | QUIC tunnel ingress |
@@ -121,33 +121,26 @@ Keep these ports private or loopback-only in the recommended topology:
121
122
Certificate files are also split by owner:
123
124
-| Certificate | Default path in the example | Used by |
124
+| Certificate | Location | Used by |
125
|---|---|---|
126
-| Browser-facing HTTPS certificate | `./certs/fullchain.pem`, `./certs/privkey.pem` | nginx public edge |
126
+| Browser-facing HTTPS certificate | Edge-specific certificate path | nginx instance that terminates `portal.example.com` |
127
| Relay API and SNI certificate | `./.portal-certs/fullchain.pem`, `./.portal-certs/privatekey.pem` | `portal` unless managed ACME is configured |
128
129
-Portal-managed ACME can manage the relay certificate and relay DNS records. The bundled nginx example still expects a browser-facing certificate in `./certs`; manage that with your normal edge certificate process.
129
+Portal-managed ACME can manage the relay certificate and relay DNS records. If a separate front nginx only TCP-passthroughs Portal hostnames, it does not need Portal certificate material; the Portal nginx or relay edge behind it still terminates the Portal root host.
130
131
## 3. Deploy the Recommended Stack
132
133
-Start from the single-domain nginx example:
133
+Deploy the Portal services with your own Compose, systemd, or orchestration
134
+manifest. If another nginx already owns public `443/tcp`, use the nginx
135
+passthrough example only for SNI routing to the Portal nginx:
136
137
```bash
136
-mkdir -p portal-deploy
137
-cd portal-deploy
138
-
139
-cp <repo>/docs/static/examples/nginx-proxy/docker-compose.yaml ./docker-compose.yaml
138
cp <repo>/docs/static/examples/nginx-proxy/nginx.conf ./nginx.conf
141
-cp <repo>/docs/static/examples/nginx-proxy/.env.example ./.env
142
-cp <repo>/docs/static/examples/nginx-proxy/deploy_portal.sh ./deploy_portal.sh
143
-cp <repo>/docs/static/examples/nginx-proxy/watch_and_deploy.sh ./watch_and_deploy.sh
144
-cp <repo>/docs/static/examples/nginx-proxy/nginx_deploy.sh ./nginx_deploy.sh
145
-chmod +x deploy_portal.sh watch_and_deploy.sh nginx_deploy.sh
139
```
140
148
-Replace every `portal.example.com` in `nginx.conf` and `.env`.
149
-
150
-For deployments with multiple additional services behind the same edge nginx, use `docs/static/examples/nginx-proxy-multi-service` instead. The same Portal routing rules apply.
141
+Replace `portal.example.com` and the `portal_nginx` upstream in `nginx.conf`.
142
+The example does not deploy Portal containers, write Portal `.env` files, or
143
+reload Portal services.
144
145
### Configure `.env`
146
@@ -176,7 +169,10 @@ TRUSTED_PROXY_CIDRS=
169
LANDING_PAGE_ENABLED=false
170
```
171
179
-`API_PORT` defaults to `4017`. If you change it, update the relay `proxy_pass` targets in the bundled `nginx.conf` to the same port. Keep `SNI_PORT=443` because this is the public SNI port advertised to tunnel clients. The single-domain Compose example maps the relay container's SNI listener to `127.0.0.1:4443` on the host so nginx can own public `443/tcp` and still pass wildcard TCP traffic to the relay. Do not open `4443/tcp` publicly; it is only a host-local upstream in that example.
172
+`API_PORT` defaults to `4017`. Keep `SNI_PORT=443` because this is the public
173
+SNI port advertised to tunnel clients. If a separate nginx sits in front of
174
+Portal's own nginx, it should TCP-passthrough `portal.example.com` and
175
+`*.portal.example.com` instead of proxying Portal API paths itself.
176
177
If the relay joins public discovery, set `BOOTSTRAPS` to at least one reachable relay URL and keep `WIREGUARD_PORT/udp` open.
178
@@ -361,10 +357,36 @@ It owns:
357
- `/ui/policy/*` composition, while relay-enforced policy changes are still forwarded to `portal`.
358
- `/ui/service/status`, derived from relay state for quick-start UI checks.
359
- `/ui/thumbnail/<hostname>`, when optional screenshot generation is enabled.
364
-- The landing-page flag persisted at `PORTAL_FRONTEND_STATE_PATH`; the bundled Compose files store it under `./.portal-certs/frontend-state/state.json`.
360
+- The landing-page flag persisted at `PORTAL_FRONTEND_STATE_PATH`; a Compose deployment can store it under `./.portal-certs/frontend-state/state.json`.
361
362
The Go relay remains the owner of authentication, policy enforcement, lease state, tunnel ingress, install scripts, discovery, and x402 facilitator paths.
363
364
+### Custom Frontend
365
+
366
+To attach your own dashboard frontend, replace only the `portal-frontend`
367
+service image. Keep the service name `portal-frontend` and serve plain HTTP on
368
+port `8080` so the existing nginx route for SPA paths can continue to point at
369
+`portal-frontend:8080`.
370
+
371
+The custom frontend should use same-origin browser requests and leave these
372
+paths owned by the Portal services:
373
+
374
+| Path | Owner |
375
+|---|---|
376
+| `/ui/*` | `portal-api` presentation API |
377
+| `/api/*` | `portal` relay API |
378
+| `/sdk/*`, `/discovery*`, `/v1/sign` | `portal` relay protocols |
379
+| `*.portal.example.com` | `portal` SNI listener |
380
+
381
+```bash
382
+cp <repo>/docs/static/examples/custom-frontend/docker-compose.override.yaml ./docker-compose.override.yaml
383
+docker compose up -d portal-frontend
384
+```
385
+
386
+If the automated release updater should also track your custom frontend image,
387
+set `IMAGES` to include that image in addition to the Portal release-track
388
+images.
389
+
390
### Thumbnail Screenshots
391
392
Generated thumbnails are optional and disabled by default. Without this feature, apps without a custom thumbnail simply use the default card background.
@@ -384,33 +406,30 @@ thumbnail captured hostname=myapp.portal.example.com size=36209
406
407
Disable the feature by removing `HEADLESS_SHELL_URL` and stopping the `headless-shell` container.
408
387
-## 7. Auto-Update
409
+## 7. Automated Release Updates
410
411
Auto-update should follow a published release tag, not `latest`. The `latest`
412
image tag tracks default-branch image builds, so using it can update production
413
on a `main` merge before the GitHub Release and tunnel binaries are published.
414
393
-The bundled Compose examples use the v2 release track directly. Auto-update must
394
-pull all production images from that same release track:
415
+Production deployments should follow the v2 release track directly.
416
+Auto-update must pull all production images from that same release track:
417
418
- `ghcr.io/gosuda/portal:2`
419
- `ghcr.io/gosuda/portal-frontend:2`
420
- `ghcr.io/gosuda/portal-api:2`
421
400
-The bundled `deploy_portal.sh` pulls all Portal images together and reloads nginx after the services are updated:
422
+The auto-update example watches those image digests, pulls the changed official
423
+images, recreates the Portal services, and reloads the Portal nginx:
424
425
```bash
403
-#!/bin/bash
404
-set -e
405
-
406
-docker compose pull portal portal-frontend portal-api
407
-docker compose up -d portal portal-frontend portal-api
408
-bash nginx_deploy.sh
426
+cp <repo>/docs/static/examples/auto-update/watch_and_deploy.sh ./watch_and_deploy.sh
427
+chmod +x watch_and_deploy.sh
428
```
429
411
-The bundled `watch_and_deploy.sh` reads the Portal images from Docker Compose,
412
-polls their remote digests, and runs the deploy script when any watched release
413
-tag changes.
430
+The example watcher reads Portal images from Docker Compose when available,
431
+falls back to the v2 image set, and updates `portal`, `portal-frontend`, and
432
+`portal-api` when any watched release tag changes.
433
434
Systemd example:
435
@@ -430,7 +449,6 @@ ExecStart=/bin/bash <path-to-project>/watch_and_deploy.sh
449
Restart=always
450
RestartSec=10
451
Environment=INTERVAL=60
433
-Environment=DEPLOY_SCRIPT=deploy_portal.sh
452
453
[Install]
454
WantedBy=multi-user.target
@@ -461,7 +479,7 @@ Logs like `"\x16\x03\x01..." 400` mean a client sent HTTPS to the plain HTTP `po
479
480
### Relay Logs Show `tls: unknown certificate`
481
464
-This usually means a browser or proxy hit the relay API certificate directly instead of the public nginx certificate, or an upstream proxy tried to verify the relay's internal certificate. In the bundled nginx example, public browsers verify nginx's certificate, while nginx proxies to the relay API over internal HTTPS.
482
+This usually means a browser or proxy hit the relay API certificate directly instead of the public nginx certificate, or an upstream proxy tried to verify the relay's internal certificate. In the recommended topology, public browsers verify the Portal HTTPS edge certificate, while the edge proxies to the relay API over internal HTTPS.
483
484
### Root Host Works but Wildcard Apps Fail
485
docs/static/examples/auto-update/watch_and_deploy.sh
renamed
+14
-4
@@ -12,7 +12,8 @@ IMAGES="${IMAGES:-$(default_images)}"
12
IMAGES="${IMAGES:-ghcr.io/gosuda/portal:2 ghcr.io/gosuda/portal-frontend:2 ghcr.io/gosuda/portal-api:2}"
13
DIGEST_FILE="${DIGEST_FILE:-.portal_image_digest}"
14
INTERVAL="${INTERVAL:-60}"
15
-DEPLOY_SCRIPT="${DEPLOY_SCRIPT:-deploy_portal.sh}"
15
+SERVICES="${SERVICES:-portal portal-frontend portal-api}"
16
+RELOAD_NGINX="${RELOAD_NGINX:-true}"
17
18
get_remote_digest() {
19
for image in $IMAGES; do
@@ -27,7 +28,16 @@ get_remote_digest() {
28
}
29
30
echo "Watching $IMAGES for digest changes (interval: ${INTERVAL}s)"
30
-echo "Deploy script: $DEPLOY_SCRIPT"
31
+echo "Compose services: $SERVICES"
32
+
33
+update_services() {
34
+ docker compose pull $SERVICES
35
+ docker compose up -d $SERVICES
36
+
37
+ if [[ "$RELOAD_NGINX" == "true" ]]; then
38
+ docker compose exec -T nginx nginx -s reload
39
+ fi
40
+}
41
42
while true; do
43
if ! NEW_DIGEST=$(get_remote_digest); then
@@ -47,9 +57,9 @@ while true; do
57
58
if [[ "$NEW_DIGEST" != "$OLD_DIGEST" ]]; then
59
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Digest changed: ${OLD_DIGEST:-<none>} -> $NEW_DIGEST"
60
+ update_services
61
echo "$NEW_DIGEST" > "$DIGEST_FILE"
51
- bash "$DEPLOY_SCRIPT"
52
- echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deploy completed"
62
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Update completed"
63
fi
64
65
sleep "$INTERVAL"
docs/static/examples/custom-frontend/docker-compose.override.yaml
new
+8
@@ -0,0 +1,8 @@
1
+# Replace only the frontend image. The service name stays portal-frontend so the
2
+# existing nginx route to portal-frontend:8080 does not change.
3
+
4
+services:
5
+ portal-frontend:
6
+ image: your-registry/portal-custom-frontend:latest
7
+ environment:
8
+ PORT: 8080
docs/static/examples/nginx-proxy-multi-service/.env.example
deleted
-70
@@ -1,70 +0,0 @@
1
-# Portal + nginx multi-service deployment configuration
2
-# Copy this file to .env and fill in the values.
3
-
4
-# Public routing, discovery, and relay identity persistence
5
-PORTAL_URL=https://portal.example.com
6
-BOOTSTRAPS=
7
-DISCOVERY=true
8
-IDENTITY_PATH=/portal-certs
9
-
10
-# Listener ports
11
-# If API_PORT changes, also update the portal_api upstream in nginx.conf.
12
-API_PORT=4017
13
-SNI_PORT=443
14
-WIREGUARD_PORT=51820
15
-# Set when enabling public UDP or raw TCP lease ports.
16
-MIN_PORT=0
17
-MAX_PORT=0
18
-UDP_ENABLED=false
19
-TCP_ENABLED=false
20
-
21
-# TLS/ACME materials live under IDENTITY_PATH as fullchain.pem/privatekey.pem.
22
-
23
-# Supported managed values: cloudflare, gcloud, hetzner, njalla, route53, vultr
24
-ACME_DNS_PROVIDER=
25
-
26
-# Cloudflare API token (required when ACME_DNS_PROVIDER=cloudflare)
27
-CLOUDFLARE_TOKEN=
28
-
29
-# Google Cloud DNS settings. (required when ACME_DNS_PROVIDER=gcloud)
30
-GCP_PROJECT_ID=
31
-GCP_MANAGED_ZONE=
32
-GOOGLE_APPLICATION_CREDENTIALS=
33
-
34
-# Hetzner DNS settings (required when ACME_DNS_PROVIDER=hetzner)
35
-HETZNER_API_TOKEN=
36
-
37
-# Route53 settings (required when ACME_DNS_PROVIDER=route53)
38
-AWS_ACCESS_KEY_ID=
39
-AWS_SECRET_ACCESS_KEY=
40
-AWS_SESSION_TOKEN=
41
-AWS_REGION=
42
-AWS_DEFAULT_REGION=
43
-AWS_HOSTED_ZONE_ID=
44
-# Required only when ACME_DNS_PROVIDER=route53 and ENS_GASLESS_ENABLED=true and no ACTIVE KSK already exists.
45
-AWS_DNSSEC_KMS_KEY_ARN=
46
-
47
-# Vultr DNS settings (required when ACME_DNS_PROVIDER=vultr)
48
-VULTR_API_KEY=
49
-
50
-# Njalla DNS settings (required when ACME_DNS_PROVIDER=njalla)
51
-NJALLA_TOKEN=
52
-
53
-# ENS gasless DNS import automation. When enabled, Portal uses ACME_DNS_PROVIDER
54
-# for DNSSEC and ENS TXT automation, even when certificate files are managed manually.
55
-ENS_GASLESS_ENABLED=false
56
-
57
-# Admin/auth configuration. The relay identity wallet is always allowed.
58
-ADMIN_WALLETS=
59
-# Enable when the relay is behind nginx/ingress/load balancers and should trust forwarded client IP headers.
60
-# Optionally restrict which proxy source ranges may supply those headers; leave empty for default private/loopback proxy ranges.
61
-TRUST_PROXY_HEADERS=true
62
-TRUSTED_PROXY_CIDRS=
63
-
64
-# Frontend-owned presentation state.
65
-LANDING_PAGE_ENABLED=false
66
-
67
-# Optional: auto-generated thumbnail screenshots for tunnel apps without a thumbnail.
68
-# Used by the portal-api service. Requires the headless-shell sidecar.
69
-# Leave empty to keep generated screenshots disabled. See docs/src/routes/deployment/+page.md.
70
-# HEADLESS_SHELL_URL=ws://headless-shell:9222
docs/static/examples/nginx-proxy-multi-service/docker-compose.yaml
deleted
-166
@@ -1,166 +0,0 @@
1
-# Portal relay + multiple services - docker compose deployment example.
2
-#
3
-# This example runs Portal alongside other web services behind one nginx.
4
-#
5
-# Architecture:
6
-# nginx:443 (L4 stream, ssl_preread)
7
-# - portal.example.com -> nginx:8443 (L7 path split)
8
-# - /sdk/*, /discovery*, /v1/sign -> portal:${API_PORT:-4017} (portal protocol, HTTPS, Docker network only)
9
-# - /api/* relay API -> portal:${API_PORT:-4017} (portal, HTTPS, Docker network only)
10
-# - /ui/* presentation API -> portal-api:8081 (HTTP)
11
-# - frontend/UI paths -> portal-frontend:8080 (HTTP)
12
-# - *.portal.example.com -> portal:443 (portal SNI passthrough, Docker network only)
13
-# - everything else -> nginx:8443 (L7 for other apps)
14
-#
15
-# Prerequisites:
16
-# 1. Copy .env.example to .env and set all required values.
17
-# 2. Place TLS certificates in ./certs/:
18
-# - portal_fullchain.pem, portal_privkey.pem for portal.example.com
19
-# - app_a_fullchain.pem, app_a_privkey.pem for app-a.example.com
20
-# - app_b_fullchain.pem, app_b_privkey.pem for app-b.example.com
21
-# Portal also needs API TLS material in ./.portal-certs unless ACME_DNS_PROVIDER is configured.
22
-# 3. Create the .portal-certs directory with correct ownership (UID 65532 = nonroot).
23
-# Frontend presentation state is stored under ./.portal-certs/frontend-state:
24
-# mkdir -p ./.portal-certs/frontend-state
25
-# sudo chown 65532:65532 ./.portal-certs
26
-# chmod 755 ./.portal-certs
27
-# 4. Start all services:
28
-# docker compose up -d
29
-
30
-services:
31
- nginx:
32
- image: nginx:stable-alpine
33
- container_name: nginx
34
- ports:
35
- - "80:80"
36
- - "443:443"
37
- volumes:
38
- - ./nginx.conf:/etc/nginx/nginx.conf:ro
39
- - ./certs:/etc/certs:ro
40
- depends_on:
41
- - portal
42
- - portal-frontend
43
- - portal-api
44
- - app-a-api
45
- - app-a-frontend
46
- restart: unless-stopped
47
- networks:
48
- - default
49
- - app-a-network
50
- - app-b-network
51
-
52
- # Optional: uncomment to enable auto-generated thumbnails for tunnel apps.
53
- # See docs/src/routes/deployment/+page.md for details.
54
- # headless-shell:
55
- # image: chromedp/headless-shell:stable
56
- # restart: unless-stopped
57
-
58
- portal:
59
- image: ghcr.io/gosuda/portal:2
60
- container_name: portal
61
- ports:
62
- - "${WIREGUARD_PORT:-51820}:${WIREGUARD_PORT:-51820}/udp"
63
- # Uncomment when enabling UDP transport:
64
- # - "${SNI_PORT:-443}:${SNI_PORT:-443}/udp"
65
- # - "${MIN_PORT:-40000}-${MAX_PORT:-40009}:${MIN_PORT:-40000}-${MAX_PORT:-40009}/udp"
66
- # Uncomment when enabling raw TCP port transport:
67
- # - "${MIN_PORT:-40000}-${MAX_PORT:-40009}:${MIN_PORT:-40000}-${MAX_PORT:-40009}"
68
- stop_grace_period: 30s
69
- environment:
70
- PORTAL_URL: ${PORTAL_URL:-https://portal.example.com}
71
- BOOTSTRAPS: ${BOOTSTRAPS:-}
72
- DISCOVERY: ${DISCOVERY:-true}
73
- WIREGUARD_PORT: ${WIREGUARD_PORT:-51820}
74
- API_PORT: ${API_PORT:-4017}
75
- SNI_PORT: ${SNI_PORT:-443}
76
- IDENTITY_PATH: ${IDENTITY_PATH:-/portal-certs}
77
- MIN_PORT: ${MIN_PORT:-0}
78
- MAX_PORT: ${MAX_PORT:-0}
79
- UDP_ENABLED: ${UDP_ENABLED:-false}
80
- TCP_ENABLED: ${TCP_ENABLED:-false}
81
- ADMIN_WALLETS: ${ADMIN_WALLETS:-}
82
- TRUST_PROXY_HEADERS: ${TRUST_PROXY_HEADERS:-true}
83
- TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:-}
84
- ACME_DNS_PROVIDER: ${ACME_DNS_PROVIDER:-}
85
- ENS_GASLESS_ENABLED: ${ENS_GASLESS_ENABLED:-false}
86
- CLOUDFLARE_TOKEN: ${CLOUDFLARE_TOKEN:-}
87
- GCP_PROJECT_ID: ${GCP_PROJECT_ID:-}
88
- GCP_MANAGED_ZONE: ${GCP_MANAGED_ZONE:-}
89
- GOOGLE_APPLICATION_CREDENTIALS: ${GOOGLE_APPLICATION_CREDENTIALS:-}
90
- HETZNER_API_TOKEN: ${HETZNER_API_TOKEN:-}
91
- AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
92
- AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
93
- AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN:-}
94
- AWS_REGION: ${AWS_REGION:-}
95
- AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION:-}
96
- AWS_HOSTED_ZONE_ID: ${AWS_HOSTED_ZONE_ID:-}
97
- AWS_DNSSEC_KMS_KEY_ARN: ${AWS_DNSSEC_KMS_KEY_ARN:-}
98
- VULTR_API_KEY: ${VULTR_API_KEY:-}
99
- NJALLA_TOKEN: ${NJALLA_TOKEN:-}
100
- volumes:
101
- - ./.portal-certs:${IDENTITY_PATH:-/portal-certs}
102
- # Uncomment when using a Google Cloud service account file for gcloud automation.
103
- # - ./gcp-dns.json:/run/secrets/gcp-dns.json:ro
104
- restart: unless-stopped
105
-
106
- portal-frontend:
107
- image: ghcr.io/gosuda/portal-frontend:2
108
- container_name: portal-frontend
109
- depends_on:
110
- - portal
111
- - portal-api
112
- restart: unless-stopped
113
-
114
- portal-api:
115
- image: ghcr.io/gosuda/portal-api:2
116
- container_name: portal-api
117
- depends_on:
118
- - portal
119
- # Uncomment with the headless-shell service above to enable generated screenshots.
120
- # - headless-shell
121
- environment:
122
- PORT: 8081
123
- PORTAL_API_BASE_URL: "https://portal:${API_PORT:-4017}"
124
- LANDING_PAGE_ENABLED: ${LANDING_PAGE_ENABLED:-false}
125
- PORTAL_FRONTEND_STATE_PATH: /frontend-state/state.json
126
- HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-}
127
- # HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-ws://headless-shell:9222}
128
- volumes:
129
- - ./.portal-certs/frontend-state:/frontend-state
130
- restart: unless-stopped
131
-
132
- app-a-api:
133
- image: your-registry/app-a-api:latest
134
- container_name: app-a-api
135
- restart: unless-stopped
136
- networks:
137
- - app-a-network
138
-
139
- app-a-frontend:
140
- image: your-registry/app-a-frontend:latest
141
- container_name: app-a-frontend
142
- restart: unless-stopped
143
- networks:
144
- - app-a-network
145
- depends_on:
146
- - app-a-api
147
-
148
- app-b-api:
149
- image: your-registry/app-b-api:latest
150
- container_name: app-b-api
151
- restart: unless-stopped
152
- networks:
153
- - app-b-network
154
-
155
- app-b-frontend:
156
- image: your-registry/app-b-frontend:latest
157
- container_name: app-b-frontend
158
- restart: unless-stopped
159
- networks:
160
- - app-b-network
161
-
162
-networks:
163
- app-a-network:
164
- driver: bridge
165
- app-b-network:
166
- driver: bridge
docs/static/examples/nginx-proxy-multi-service/nginx.conf
deleted
-262
@@ -1,262 +0,0 @@
1
-# Portal relay + multiple services - nginx reverse proxy configuration example.
2
-#
3
-# This example runs Portal alongside other web services behind one nginx.
4
-# Replace these domains with your own:
5
-# portal.example.com - Portal relay frontend + API
6
-# app-a.example.com - first web application
7
-# app-b.example.com - second web application
8
-#
9
-# Traffic flow:
10
-# :80 -> redirect to HTTPS
11
-# :443 -> L4 SNI inspection (ssl_preread)
12
-# portal.example.com -> nginx L7 (path split)
13
-# *.portal.example.com -> portal SNI listener (raw TCP passthrough)
14
-# everything else -> nginx L7 for other services
15
-
16
-user nginx;
17
-worker_processes auto;
18
-error_log /var/log/nginx/error.log;
19
-pid /run/nginx.pid;
20
-
21
-events {
22
- worker_connections 1024;
23
-}
24
-
25
-stream {
26
- map $ssl_preread_server_name $backend {
27
- portal.example.com local_https;
28
- ~\.portal\.example\.com$ portal_sni;
29
- default local_https;
30
- }
31
-
32
- upstream portal_sni {
33
- # Portal SNI listener. TLS is not terminated here.
34
- server portal:443;
35
- }
36
-
37
- upstream local_https {
38
- # nginx's own L7 HTTPS listener.
39
- server 127.0.0.1:8443;
40
- }
41
-
42
- server {
43
- listen 443;
44
- ssl_preread on;
45
- proxy_pass $backend;
46
- proxy_socket_keepalive on;
47
- proxy_connect_timeout 10s;
48
- proxy_buffer_size 16k;
49
- proxy_timeout 86400s;
50
- }
51
-}
52
-
53
-http {
54
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
55
- '$status $body_bytes_sent "$http_referer" '
56
- '"$http_user_agent" "$http_x_forwarded_for"';
57
-
58
- access_log /var/log/nginx/access.log main;
59
-
60
- sendfile on;
61
- tcp_nopush on;
62
- tcp_nodelay on;
63
- keepalive_timeout 65;
64
- types_hash_max_size 4096;
65
-
66
- include /etc/nginx/mime.types;
67
- default_type application/octet-stream;
68
-
69
- upstream app_a_backend {
70
- server app-a-api:8000;
71
- keepalive 32;
72
- }
73
-
74
- upstream app_a_frontend {
75
- server app-a-frontend:3000;
76
- keepalive 32;
77
- }
78
-
79
- upstream app_b_backend {
80
- server app-b-api:8001;
81
- keepalive 32;
82
- }
83
-
84
- upstream app_b_frontend {
85
- server app-b-frontend:3000;
86
- keepalive 32;
87
- }
88
-
89
- server {
90
- listen 80;
91
- listen [::]:80;
92
- server_name app-a.example.com app-b.example.com portal.example.com;
93
- return 301 https://$host$request_uri;
94
- }
95
-
96
- server {
97
- listen 8443 ssl;
98
- listen [::]:8443 ssl;
99
- server_name portal.example.com;
100
- server_tokens off;
101
-
102
- ssl_certificate /etc/certs/portal_fullchain.pem;
103
- ssl_certificate_key /etc/certs/portal_privkey.pem;
104
-
105
- gzip on;
106
- gzip_vary on;
107
- gzip_min_length 1024;
108
- gzip_types text/plain text/css application/json application/javascript
109
- text/xml application/xml;
110
-
111
- location = /sdk/connect {
112
- proxy_pass https://portal:4017;
113
- proxy_ssl_verify off;
114
- proxy_ssl_server_name on;
115
- proxy_ssl_name $host;
116
- proxy_http_version 1.1;
117
-
118
- proxy_set_header Host $host;
119
- proxy_set_header X-Real-IP $remote_addr;
120
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
121
- proxy_set_header X-Forwarded-Proto https;
122
- proxy_set_header Upgrade $http_upgrade;
123
- proxy_set_header Connection $http_connection;
124
-
125
- proxy_buffering off;
126
- proxy_request_buffering off;
127
- proxy_read_timeout 86400s;
128
- proxy_send_timeout 86400s;
129
- }
130
-
131
- location ~ ^/(sdk/|discovery(?:/|$)|v1/sign$) {
132
- proxy_pass https://portal:4017;
133
- proxy_ssl_verify off;
134
- proxy_ssl_server_name on;
135
- proxy_ssl_name $host;
136
-
137
- proxy_set_header Host $host;
138
- proxy_set_header X-Real-IP $remote_addr;
139
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
140
- proxy_set_header X-Forwarded-Proto https;
141
-
142
- proxy_read_timeout 60s;
143
- }
144
-
145
- # Presentation-owned API paths are isolated under /ui/.
146
- location /ui/ {
147
- proxy_pass http://portal-api:8081;
148
-
149
- proxy_set_header Host $host;
150
- proxy_set_header X-Real-IP $remote_addr;
151
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
152
- proxy_set_header X-Forwarded-Proto https;
153
- proxy_set_header Authorization $http_authorization;
154
-
155
- proxy_read_timeout 60s;
156
- }
157
-
158
- location /api/ {
159
- proxy_pass https://portal:4017;
160
- proxy_ssl_verify off;
161
- proxy_ssl_server_name on;
162
- proxy_ssl_name $host;
163
-
164
- proxy_set_header Host $host;
165
- proxy_set_header X-Real-IP $remote_addr;
166
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
167
- proxy_set_header X-Forwarded-Proto https;
168
- proxy_set_header Authorization $http_authorization;
169
-
170
- proxy_read_timeout 60s;
171
- }
172
-
173
- location / {
174
- proxy_pass http://portal-frontend:8080;
175
-
176
- proxy_set_header Host $host;
177
- proxy_set_header X-Real-IP $remote_addr;
178
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
179
- proxy_set_header X-Forwarded-Proto https;
180
- }
181
- }
182
-
183
- server {
184
- listen 8443 ssl;
185
- listen [::]:8443 ssl;
186
- server_name app-a.example.com;
187
- server_tokens off;
188
-
189
- ssl_certificate /etc/certs/app_a_fullchain.pem;
190
- ssl_certificate_key /etc/certs/app_a_privkey.pem;
191
-
192
- gzip on;
193
- gzip_vary on;
194
- gzip_min_length 1024;
195
- gzip_types text/plain text/css application/json application/javascript
196
- text/xml application/xml;
197
-
198
- location /api {
199
- proxy_pass http://app_a_backend/api;
200
- proxy_http_version 1.1;
201
- proxy_set_header Connection "";
202
- proxy_buffering off;
203
- proxy_cache off;
204
- chunked_transfer_encoding off;
205
- proxy_set_header Host $host;
206
- proxy_set_header X-Real-IP $remote_addr;
207
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
208
- proxy_set_header X-Forwarded-Proto $scheme;
209
- proxy_read_timeout 86400s;
210
- proxy_send_timeout 86400s;
211
- }
212
-
213
- location / {
214
- proxy_pass http://app_a_frontend;
215
- proxy_http_version 1.1;
216
- proxy_set_header Host $host;
217
- proxy_set_header X-Real-IP $remote_addr;
218
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
219
- proxy_set_header X-Forwarded-Proto $scheme;
220
- }
221
- }
222
-
223
- server {
224
- listen 8443 ssl;
225
- listen [::]:8443 ssl;
226
- server_name app-b.example.com;
227
- server_tokens off;
228
-
229
- ssl_certificate /etc/certs/app_b_fullchain.pem;
230
- ssl_certificate_key /etc/certs/app_b_privkey.pem;
231
-
232
- gzip on;
233
- gzip_vary on;
234
- gzip_min_length 1024;
235
- gzip_types text/plain text/css application/json application/javascript
236
- text/xml application/xml;
237
-
238
- location /api {
239
- proxy_pass http://app_b_backend/api;
240
- proxy_http_version 1.1;
241
- proxy_set_header Connection "";
242
- proxy_buffering off;
243
- proxy_cache off;
244
- chunked_transfer_encoding off;
245
- proxy_set_header Host $host;
246
- proxy_set_header X-Real-IP $remote_addr;
247
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
248
- proxy_set_header X-Forwarded-Proto $scheme;
249
- proxy_read_timeout 86400s;
250
- proxy_send_timeout 86400s;
251
- }
252
-
253
- location / {
254
- proxy_pass http://app_b_frontend;
255
- proxy_http_version 1.1;
256
- proxy_set_header Host $host;
257
- proxy_set_header X-Real-IP $remote_addr;
258
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
259
- proxy_set_header X-Forwarded-Proto $scheme;
260
- }
261
- }
262
-}
docs/static/examples/nginx-proxy/.env.example
deleted
-70
@@ -1,70 +0,0 @@
1
-# Portal + nginx reverse proxy configuration
2
-# Copy this file to .env and fill in the values.
3
-
4
-# Public routing, discovery, and relay identity persistence
5
-PORTAL_URL=https://portal.example.com
6
-BOOTSTRAPS=
7
-DISCOVERY=true
8
-IDENTITY_PATH=/portal-certs
9
-
10
-# Listener ports
11
-# If API_PORT changes, also update the portal_api upstream in nginx.conf.
12
-API_PORT=4017
13
-SNI_PORT=443
14
-WIREGUARD_PORT=51820
15
-# Set when enabling public UDP or raw TCP lease ports.
16
-MIN_PORT=0
17
-MAX_PORT=0
18
-UDP_ENABLED=false
19
-TCP_ENABLED=false
20
-
21
-# TLS/ACME materials live under IDENTITY_PATH as fullchain.pem/privatekey.pem.
22
-
23
-# Supported managed values: cloudflare, gcloud, hetzner, njalla, route53, vultr
24
-ACME_DNS_PROVIDER=
25
-
26
-# Cloudflare API token (required when ACME_DNS_PROVIDER=cloudflare)
27
-CLOUDFLARE_TOKEN=
28
-
29
-# Google Cloud DNS settings. (required when ACME_DNS_PROVIDER=gcloud)
30
-GCP_PROJECT_ID=
31
-GCP_MANAGED_ZONE=
32
-GOOGLE_APPLICATION_CREDENTIALS=
33
-
34
-# Hetzner DNS settings (required when ACME_DNS_PROVIDER=hetzner)
35
-HETZNER_API_TOKEN=
36
-
37
-# Route53 settings (required when ACME_DNS_PROVIDER=route53)
38
-AWS_ACCESS_KEY_ID=
39
-AWS_SECRET_ACCESS_KEY=
40
-AWS_SESSION_TOKEN=
41
-AWS_REGION=
42
-AWS_DEFAULT_REGION=
43
-AWS_HOSTED_ZONE_ID=
44
-# Required only when ACME_DNS_PROVIDER=route53 and ENS_GASLESS_ENABLED=true and no ACTIVE KSK already exists.
45
-AWS_DNSSEC_KMS_KEY_ARN=
46
-
47
-# Vultr DNS settings (required when ACME_DNS_PROVIDER=vultr)
48
-VULTR_API_KEY=
49
-
50
-# Njalla DNS settings (required when ACME_DNS_PROVIDER=njalla)
51
-NJALLA_TOKEN=
52
-
53
-# ENS gasless DNS import automation. When enabled, Portal uses ACME_DNS_PROVIDER
54
-# for DNSSEC and ENS TXT automation, even when certificate files are managed manually.
55
-ENS_GASLESS_ENABLED=false
56
-
57
-# Admin/auth configuration. The relay identity wallet is always allowed.
58
-ADMIN_WALLETS=
59
-# Enable when the relay is behind nginx/ingress/load balancers and should trust forwarded client IP headers.
60
-# Optionally restrict which proxy source ranges may supply those headers; leave empty for default private/loopback proxy ranges.
61
-TRUST_PROXY_HEADERS=true
62
-TRUSTED_PROXY_CIDRS=
63
-
64
-# Frontend-owned presentation state.
65
-LANDING_PAGE_ENABLED=false
66
-
67
-# Optional: auto-generated thumbnail screenshots for tunnel apps without a thumbnail.
68
-# Used by the portal-api service. Requires the headless-shell sidecar.
69
-# Leave empty to keep generated screenshots disabled. See docs/src/routes/deployment/+page.md.
70
-# HEADLESS_SHELL_URL=ws://headless-shell:9222
docs/static/examples/nginx-proxy/deploy_portal.sh
deleted
-6
@@ -1,6 +0,0 @@
1
-#!/bin/bash
2
-set -e
3
-
4
-docker compose pull portal portal-frontend portal-api
5
-docker compose up -d portal portal-frontend portal-api
6
-bash nginx_deploy.sh
docs/static/examples/nginx-proxy/docker-compose.yaml
deleted
-128
@@ -1,128 +0,0 @@
1
-# Portal relay + frontend - nginx reverse proxy deployment example.
2
-# Replace "portal.example.com" with your actual domain throughout.
3
-#
4
-# Architecture:
5
-# nginx:443/tcp (L4 stream, ssl_preread)
6
-# - portal.example.com -> 127.0.0.1:8443 (nginx L7, TLS termination)
7
-# - /sdk/*, /discovery*, /v1/sign -> 127.0.0.1:${API_PORT:-4017} (portal protocol, HTTPS, loopback only)
8
-# - /api/* relay API -> 127.0.0.1:${API_PORT:-4017} (portal, HTTPS, loopback only)
9
-# - /ui/* presentation API -> 127.0.0.1:8081 (portal-api, HTTP, loopback only)
10
-# - frontend/UI paths -> 127.0.0.1:8080 (portal-frontend, HTTP, loopback only)
11
-# - *.portal.example.com -> 127.0.0.1:4443 (portal SNI, raw TCP passthrough)
12
-# portal:443/udp (QUIC tunnel listener, only if UDP_ENABLED=true)
13
-# portal:MIN-MAX/udp (per-lease UDP relay ports, only if UDP_ENABLED=true)
14
-# portal:MIN-MAX/tcp (per-lease raw TCP ports, only if TCP_ENABLED=true)
15
-#
16
-# Prerequisites:
17
-# 1. Copy .env.example to .env and set all required values.
18
-# 2. Place the root-domain TLS certificate files for nginx in ./certs/:
19
-# ./certs/fullchain.pem
20
-# ./certs/privkey.pem
21
-# 3. Create the .portal-certs directory with correct ownership (UID 65532 = nonroot).
22
-# Frontend presentation state is stored under ./.portal-certs/frontend-state:
23
-# mkdir -p ./.portal-certs/frontend-state
24
-# sudo chown 65532:65532 ./.portal-certs
25
-# chmod 755 ./.portal-certs
26
-# Portal also needs API TLS material in ./.portal-certs unless ACME_DNS_PROVIDER is configured.
27
-# 4. Start all services:
28
-# docker compose up -d
29
-
30
-services:
31
- nginx:
32
- image: nginx:stable-alpine
33
- container_name: nginx
34
- network_mode: host
35
- volumes:
36
- - ./nginx.conf:/etc/nginx/nginx.conf:ro
37
- - ./certs:/etc/nginx/certs:ro
38
- depends_on:
39
- - portal
40
- - portal-frontend
41
- - portal-api
42
- restart: unless-stopped
43
-
44
- # Optional: uncomment to enable auto-generated thumbnails for tunnel apps.
45
- # See docs/src/routes/deployment/+page.md for details.
46
- # headless-shell:
47
- # image: chromedp/headless-shell:stable
48
- # restart: unless-stopped
49
-
50
- portal:
51
- image: ghcr.io/gosuda/portal:2
52
- container_name: portal
53
- ports:
54
- - "127.0.0.1:${API_PORT:-4017}:${API_PORT:-4017}/tcp"
55
- - "127.0.0.1:4443:${SNI_PORT:-443}/tcp"
56
- - "${WIREGUARD_PORT:-51820}:${WIREGUARD_PORT:-51820}/udp"
57
- # Uncomment when enabling UDP transport:
58
- # - "${SNI_PORT:-443}:${SNI_PORT:-443}/udp"
59
- # - "${MIN_PORT:-40000}-${MAX_PORT:-40009}:${MIN_PORT:-40000}-${MAX_PORT:-40009}/udp"
60
- # Uncomment when enabling raw TCP port transport:
61
- # - "${MIN_PORT:-40000}-${MAX_PORT:-40009}:${MIN_PORT:-40000}-${MAX_PORT:-40009}"
62
- stop_grace_period: 30s
63
- environment:
64
- PORTAL_URL: ${PORTAL_URL:-https://portal.example.com}
65
- BOOTSTRAPS: ${BOOTSTRAPS:-}
66
- DISCOVERY: ${DISCOVERY:-true}
67
- WIREGUARD_PORT: ${WIREGUARD_PORT:-51820}
68
- API_PORT: ${API_PORT:-4017}
69
- SNI_PORT: ${SNI_PORT:-443}
70
- IDENTITY_PATH: ${IDENTITY_PATH:-/portal-certs}
71
- MIN_PORT: ${MIN_PORT:-0}
72
- MAX_PORT: ${MAX_PORT:-0}
73
- UDP_ENABLED: ${UDP_ENABLED:-false}
74
- TCP_ENABLED: ${TCP_ENABLED:-false}
75
- ADMIN_WALLETS: ${ADMIN_WALLETS:-}
76
- TRUST_PROXY_HEADERS: ${TRUST_PROXY_HEADERS:-true}
77
- TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:-}
78
- ACME_DNS_PROVIDER: ${ACME_DNS_PROVIDER:-}
79
- ENS_GASLESS_ENABLED: ${ENS_GASLESS_ENABLED:-false}
80
- CLOUDFLARE_TOKEN: ${CLOUDFLARE_TOKEN:-}
81
- GCP_PROJECT_ID: ${GCP_PROJECT_ID:-}
82
- GCP_MANAGED_ZONE: ${GCP_MANAGED_ZONE:-}
83
- GOOGLE_APPLICATION_CREDENTIALS: ${GOOGLE_APPLICATION_CREDENTIALS:-}
84
- HETZNER_API_TOKEN: ${HETZNER_API_TOKEN:-}
85
- AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
86
- AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
87
- AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN:-}
88
- AWS_REGION: ${AWS_REGION:-}
89
- AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION:-}
90
- AWS_HOSTED_ZONE_ID: ${AWS_HOSTED_ZONE_ID:-}
91
- AWS_DNSSEC_KMS_KEY_ARN: ${AWS_DNSSEC_KMS_KEY_ARN:-}
92
- VULTR_API_KEY: ${VULTR_API_KEY:-}
93
- NJALLA_TOKEN: ${NJALLA_TOKEN:-}
94
- volumes:
95
- - ./.portal-certs:${IDENTITY_PATH:-/portal-certs}
96
- # Uncomment when using a Google Cloud service account file for gcloud automation.
97
- # - ./gcp-dns.json:/run/secrets/gcp-dns.json:ro
98
- restart: unless-stopped
99
-
100
- portal-frontend:
101
- image: ghcr.io/gosuda/portal-frontend:2
102
- container_name: portal-frontend
103
- depends_on:
104
- - portal
105
- - portal-api
106
- ports:
107
- - "127.0.0.1:${FRONTEND_PORT:-8080}:8080"
108
- restart: unless-stopped
109
-
110
- portal-api:
111
- image: ghcr.io/gosuda/portal-api:2
112
- container_name: portal-api
113
- depends_on:
114
- - portal
115
- # Uncomment with the headless-shell service above to enable generated screenshots.
116
- # - headless-shell
117
- ports:
118
- - "127.0.0.1:8081:8081"
119
- environment:
120
- PORT: 8081
121
- PORTAL_API_BASE_URL: "https://portal:${API_PORT:-4017}"
122
- LANDING_PAGE_ENABLED: ${LANDING_PAGE_ENABLED:-false}
123
- PORTAL_FRONTEND_STATE_PATH: /frontend-state/state.json
124
- HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-}
125
- # HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-ws://headless-shell:9222}
126
- volumes:
127
- - ./.portal-certs/frontend-state:/frontend-state
128
- restart: unless-stopped
docs/static/examples/nginx-proxy/nginx.conf
+21
-130
@@ -1,157 +1,48 @@
1
-# Portal relay + frontend - nginx reverse proxy configuration example.
2
-# Replace "portal.example.com" with your actual domain throughout.
1
+# Front nginx SNI passthrough example for a separately deployed Portal nginx.
2
+# This nginx does not deploy Portal and does not terminate TLS for Portal hosts.
3
#
4
-# Traffic flow:
5
-# :80 -> redirect to HTTPS
6
-# :443 -> L4 SNI inspection (ssl_preread, no TLS termination)
7
-# portal.example.com -> :8443 (nginx L7, terminates TLS)
8
-# *.portal.example.com -> 127.0.0.1:4443 (portal SNI, raw TCP passthrough)
9
-#
10
-# L7 path routing on portal.example.com:
11
-# /sdk/*, /discovery*, /v1/sign -> https://127.0.0.1:4017 (portal protocols)
12
-# /api/* relay API -> https://127.0.0.1:4017 (portal)
13
-# /ui/* presentation API -> http://127.0.0.1:8081 (portal-api)
14
-# Frontend/UI paths -> http://127.0.0.1:8080 (portal-frontend)
4
+# Replace:
5
+# portal.example.com - Portal's public root host
6
+# 10.0.0.20:443 - Portal nginx HTTPS/SNI listener
7
+# 127.0.0.1:8443 - your existing HTTPS listener for non-Portal hosts
8
9
events {
10
worker_connections 4096;
11
}
12
13
stream {
21
- map $ssl_preread_server_name $backend {
22
- portal.example.com portal_web;
23
- ~\.portal\.example\.com$ portal_sni;
24
- default portal_web;
14
+ map $ssl_preread_server_name $tls_upstream {
15
+ portal.example.com portal_nginx;
16
+ ~^.+\.portal\.example\.com$ portal_nginx;
17
+ default existing_https;
18
}
19
27
- upstream portal_web {
28
- server 127.0.0.1:8443;
20
+ upstream portal_nginx {
21
+ # Portal's own nginx. TLS is passed through unchanged.
22
+ server 10.0.0.20:443;
23
}
24
31
- upstream portal_sni {
32
- # Portal SNI listener. TLS is not terminated here.
33
- server 127.0.0.1:4443;
25
+ upstream existing_https {
26
+ # Replace with your normal HTTPS upstream, or point default to
27
+ # portal_nginx if this front nginx is dedicated to Portal.
28
+ server 127.0.0.1:8443;
29
}
30
31
server {
32
listen 443;
33
+ listen [::]:443;
34
ssl_preread on;
39
- proxy_pass $backend;
35
+ proxy_pass $tls_upstream;
36
proxy_connect_timeout 5s;
37
proxy_timeout 86400s;
38
}
39
}
40
41
http {
46
- sendfile on;
47
- tcp_nopush on;
48
- tcp_nodelay on;
49
- keepalive_timeout 65;
50
-
51
- include /etc/nginx/mime.types;
52
- default_type application/octet-stream;
53
-
54
- gzip on;
55
- gzip_vary on;
56
- gzip_min_length 1024;
57
- gzip_types text/plain text/css application/json application/javascript
58
- text/xml application/xml application/xml+rss text/javascript;
59
-
60
- server {
61
- listen 8443 ssl;
62
- server_name portal.example.com;
63
- server_tokens off;
64
-
65
- ssl_certificate /etc/nginx/certs/fullchain.pem;
66
- ssl_certificate_key /etc/nginx/certs/privkey.pem;
67
-
68
- ssl_protocols TLSv1.2 TLSv1.3;
69
- ssl_ciphers HIGH:!aNULL:!MD5;
70
- ssl_prefer_server_ciphers on;
71
- ssl_session_cache shared:SSL:10m;
72
- ssl_session_timeout 10m;
73
-
74
- # /sdk/connect is hijacked by the relay into a long-lived raw TCP
75
- # reverse session. Keep HTTP/1.1, disable buffering, and use long timeouts.
76
- location = /sdk/connect {
77
- proxy_pass https://127.0.0.1:4017;
78
- proxy_ssl_verify off;
79
- proxy_ssl_server_name on;
80
- proxy_ssl_name $host;
81
- proxy_http_version 1.1;
82
-
83
- proxy_set_header Host $host;
84
- proxy_set_header X-Real-IP $remote_addr;
85
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
86
- proxy_set_header X-Forwarded-Proto https;
87
- proxy_set_header Upgrade $http_upgrade;
88
- proxy_set_header Connection $http_connection;
89
-
90
- proxy_buffering off;
91
- proxy_request_buffering off;
92
- proxy_read_timeout 86400s;
93
- proxy_send_timeout 86400s;
94
- }
95
-
96
- location ~ ^/(sdk/|discovery(?:/|$)|v1/sign$) {
97
- proxy_pass https://127.0.0.1:4017;
98
- proxy_ssl_verify off;
99
- proxy_ssl_server_name on;
100
- proxy_ssl_name $host;
101
-
102
- proxy_set_header Host $host;
103
- proxy_set_header X-Real-IP $remote_addr;
104
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
105
- proxy_set_header X-Forwarded-Proto https;
106
-
107
- proxy_read_timeout 60s;
108
- }
109
-
110
- # Presentation-owned API paths are isolated under /ui/.
111
- location /ui/ {
112
- proxy_pass http://127.0.0.1:8081;
113
-
114
- proxy_set_header Host $host;
115
- proxy_set_header X-Real-IP $remote_addr;
116
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
117
- proxy_set_header X-Forwarded-Proto https;
118
- proxy_set_header Authorization $http_authorization;
119
-
120
- proxy_read_timeout 60s;
121
- }
122
-
123
- # Relay API/control endpoints belong to portal. The frontend /admin
124
- # route falls through to portal-frontend.
125
- location /api/ {
126
- proxy_pass https://127.0.0.1:4017;
127
- proxy_ssl_verify off;
128
- proxy_ssl_server_name on;
129
- proxy_ssl_name $host;
130
-
131
- proxy_set_header Host $host;
132
- proxy_set_header X-Real-IP $remote_addr;
133
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
134
- proxy_set_header X-Forwarded-Proto https;
135
- proxy_set_header Authorization $http_authorization;
136
-
137
- proxy_read_timeout 60s;
138
- }
139
-
140
- location / {
141
- proxy_pass http://127.0.0.1:8080;
142
-
143
- proxy_set_header Host $host;
144
- proxy_set_header X-Real-IP $remote_addr;
145
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
146
- proxy_set_header X-Forwarded-Proto https;
147
-
148
- proxy_read_timeout 60s;
149
- }
150
- }
151
-
42
server {
43
listen 80;
154
- server_name _;
44
+ listen [::]:80;
45
+ server_name portal.example.com *.portal.example.com;
46
return 301 https://$host$request_uri;
47
}
48
}
docs/static/examples/nginx-proxy/nginx_deploy.sh
deleted
-9
@@ -1,9 +0,0 @@
1
-#!/bin/bash
2
-set -e
3
-
4
-docker cp nginx:/run/nginx.pid ./nginx.pid
5
-docker cp ./nginx.conf nginx:/etc/nginx/nginx.conf
6
-docker exec nginx nginx -s reload
7
-
8
-docker cp ./nginx.pid nginx:/run/nginx.pid
9
-rm ./nginx.pid