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/",