feat: update Docker images to version 2 and adjust deployment documentation
Kim committed
Jun 1, 2026 at 12:19 UTC
0d449a03a81598238b202ff169f3c8d94b753230
8 files changed
+56
-21
docker-compose.yml
+3
-3
@@ -44,7 +44,7 @@ services:
44
# restart: unless-stopped
45
46
portal-api:
47
- image: ghcr.io/gosuda/portal-api:latest
47
+ image: ghcr.io/gosuda/portal-api:2
48
build:
49
context: ./frontend
50
dockerfile: Dockerfile
@@ -66,7 +66,7 @@ services:
66
restart: unless-stopped
67
68
portal-frontend:
69
- image: ghcr.io/gosuda/portal-frontend:latest
69
+ image: ghcr.io/gosuda/portal-frontend:2
70
build:
71
context: ./frontend
72
dockerfile: Dockerfile
@@ -76,7 +76,7 @@ services:
76
restart: unless-stopped
77
78
portal:
79
- image: ghcr.io/gosuda/portal:latest
79
+ image: ghcr.io/gosuda/portal:2
80
build:
81
context: .
82
dockerfile: Dockerfile
docs/src/routes/cli-reference/+page.md
+3
-1
@@ -327,7 +327,9 @@ Prints the installed version string and exits.
327
328
## Behavior Notes
329
330
-- `portal expose` and `portal list` check for new releases in the background.
330
+- `portal expose` and `portal list` check the latest published GitHub Release in
331
+ the background. A `main` merge or branch artifact is not offered to installed
332
+ clients until the release is created with matching binary and checksum assets.
333
- `portal expose` loads or creates a signing identity at `identity.json` or
334
`--identity-path`.
335
- Multiple relay URLs are registered independently. A failed relay does not stop
docs/src/routes/deployment/+page.md
+23
-6
@@ -10,7 +10,17 @@ priority: P1
10
11
# Portal Relay Deployment Guide
12
13
-This guide starts from the production topology. Read this as the source of truth for how the split relay, frontend, and presentation API are expected to be deployed.
13
+Portal supports two deployment profiles:
14
+
15
+- API-only relay: one `portal` image exposes relay API paths and tunnel ingress
16
+ directly. This is documented in [Self-Hosting](/self-hosting).
17
+- Full Portal edge: `nginx`, `portal`, `portal-frontend`, and `portal-api`
18
+ provide one browser-facing HTTPS origin with dashboard, presentation API, and
19
+ wildcard tunnel routing.
20
+
21
+This guide covers the full Portal edge profile and is the source of truth for
22
+how the split relay, frontend, and presentation API are expected to be deployed
23
+together.
24
25
## 1. Production Topology
26
@@ -376,11 +386,16 @@ Disable the feature by removing `HEADLESS_SHELL_URL` and stopping the `headless-
386
387
## 7. Auto-Update
388
379
-Auto-update must pull all production images together:
389
+Auto-update should follow a published release tag, not `latest`. The `latest`
390
+image tag tracks default-branch image builds, so using it can update production
391
+on a `main` merge before the GitHub Release and tunnel binaries are published.
392
+
393
+The bundled Compose examples use the v2 release track directly. Auto-update must
394
+pull all production images from that same release track:
395
381
-- `ghcr.io/gosuda/portal:latest`
382
-- `ghcr.io/gosuda/portal-frontend:latest`
383
-- `ghcr.io/gosuda/portal-api:latest`
396
+- `ghcr.io/gosuda/portal:2`
397
+- `ghcr.io/gosuda/portal-frontend:2`
398
+- `ghcr.io/gosuda/portal-api:2`
399
400
The bundled `deploy_portal.sh` pulls all Portal images together and reloads nginx after the services are updated:
401
@@ -393,7 +408,9 @@ docker compose up -d portal portal-frontend portal-api
408
bash nginx_deploy.sh
409
```
410
396
-The bundled `watch_and_deploy.sh` polls remote image digests and runs the deploy script when any watched image changes.
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.
414
415
Systemd example:
416
docs/src/routes/self-hosting/+page.md
+11
-4
@@ -5,7 +5,13 @@ description: Run your own Portal relay for private tunneling.
5
6
# Self-Hosting Guide
7
8
-This guide is for developers who want their own relay for a single project or team — not a platform operator managing relay infrastructure for many users. If you need production-grade deployment with managed TLS, ACME automation, or multi-tenant relay infrastructure, see the [Deployment Guide](/deployment) instead.
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
@@ -33,10 +39,11 @@ docker run -d \
39
-e PORTAL_URL=https://relay.example.com:4017 \
40
-e IDENTITY_PATH=/portal-certs \
41
-v $(pwd)/relay-data:/portal-certs \
36
- ghcr.io/gosuda/portal:latest
42
+ ghcr.io/gosuda/portal:2
43
```
44
39
-Replace `relay.example.com` with your domain. The relay identity address is allowed to sign in to the admin UI by default.
45
+Replace `relay.example.com` with your domain. The relay identity address is
46
+allowed to use relay admin auth by default.
47
48
## Docker Compose Setup
49
@@ -46,7 +53,7 @@ For a more maintainable setup, use Docker Compose:
53
# compose.yml
54
services:
55
relay:
49
- image: ghcr.io/gosuda/portal:latest
56
+ image: ghcr.io/gosuda/portal:2
57
restart: unless-stopped
58
ports:
59
- "443:443"
docs/static/examples/nginx-proxy-multi-service/docker-compose.yaml
+3
-3
@@ -56,7 +56,7 @@ services:
56
# restart: unless-stopped
57
58
portal:
59
- image: ghcr.io/gosuda/portal:latest
59
+ image: ghcr.io/gosuda/portal:2
60
container_name: portal
61
ports:
62
- "${WIREGUARD_PORT:-51820}:${WIREGUARD_PORT:-51820}/udp"
@@ -104,7 +104,7 @@ services:
104
restart: unless-stopped
105
106
portal-frontend:
107
- image: ghcr.io/gosuda/portal-frontend:latest
107
+ image: ghcr.io/gosuda/portal-frontend:2
108
container_name: portal-frontend
109
depends_on:
110
- portal
@@ -112,7 +112,7 @@ services:
112
restart: unless-stopped
113
114
portal-api:
115
- image: ghcr.io/gosuda/portal-api:latest
115
+ image: ghcr.io/gosuda/portal-api:2
116
container_name: portal-api
117
depends_on:
118
- portal
docs/static/examples/nginx-proxy/docker-compose.yaml
+3
-3
@@ -48,7 +48,7 @@ services:
48
# restart: unless-stopped
49
50
portal:
51
- image: ghcr.io/gosuda/portal:latest
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"
@@ -98,7 +98,7 @@ services:
98
restart: unless-stopped
99
100
portal-frontend:
101
- image: ghcr.io/gosuda/portal-frontend:latest
101
+ image: ghcr.io/gosuda/portal-frontend:2
102
container_name: portal-frontend
103
depends_on:
104
- portal
@@ -108,7 +108,7 @@ services:
108
restart: unless-stopped
109
110
portal-api:
111
- image: ghcr.io/gosuda/portal-api:latest
111
+ image: ghcr.io/gosuda/portal-api:2
112
container_name: portal-api
113
depends_on:
114
- portal
docs/static/examples/nginx-proxy/watch_and_deploy.sh
+9
-1
@@ -1,7 +1,15 @@
1
#!/usr/bin/env bash
2
set -euo pipefail
3
4
-IMAGES="${IMAGES:-ghcr.io/gosuda/portal:latest ghcr.io/gosuda/portal-frontend:latest ghcr.io/gosuda/portal-api:latest}"
4
+default_images() {
5
+ docker compose config --images 2>/dev/null \
6
+ | grep -E '^ghcr\.io/gosuda/portal(:|-frontend:|-api:)' \
7
+ | sort -u \
8
+ | tr '\n' ' ' || true
9
+}
10
+
11
+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}"
registry.json
+1
@@ -1,5 +1,6 @@
1
{
2
"relays": [
3
+ "https://gosunuts.xyz/",
4
"https://portal.thumbgo.kr/",
5
"https://portal.rabbitson87.dev/",
6
"https://s-h.day/",