Bind staging Docker to 18088 so portproxy can own 8088.

This commit is contained in:
2026-08-24 12:06:13 +03:30
parent 2a0f9dbfee
commit 5ac3a4a708
5 changed files with 23 additions and 10 deletions

View File

@@ -9,7 +9,8 @@
# DEPLOY_SECRETS_DIR absolute path on runner, e.g. C:/dyolink/secrets # DEPLOY_SECRETS_DIR absolute path on runner, e.g. C:/dyolink/secrets
# #
# Optional: # Optional:
# STAGING_HTTP_PORT host port for nginx (default 8088) # STAGING_HTTP_PORT public port (default 8088) — Windows portproxy listens here
# STAGING_LOCAL_PORT Docker bind on 127.0.0.1 (default 18088) — must not equal 8088 if portproxy owns 8088
# CLONE_HOST git clone host when runner = Gitea host → 127.0.0.1:3000 # CLONE_HOST git clone host when runner = Gitea host → 127.0.0.1:3000
# #
# Same Windows PC runs Gitea + runner + deploy: # Same Windows PC runs Gitea + runner + deploy:
@@ -162,11 +163,14 @@ jobs:
} }
$stagingPort = '${{ vars.STAGING_HTTP_PORT }}'.Trim() $stagingPort = '${{ vars.STAGING_HTTP_PORT }}'.Trim()
if ([string]::IsNullOrWhiteSpace($stagingPort)) { $stagingPort = '8088' } if ([string]::IsNullOrWhiteSpace($stagingPort)) { $stagingPort = '8088' }
$localPort = '${{ vars.STAGING_LOCAL_PORT }}'.Trim()
if ([string]::IsNullOrWhiteSpace($localPort)) { $localPort = '18088' }
$imageTag = "${{ needs.build-and-push.outputs.image_tag }}" $imageTag = "${{ needs.build-and-push.outputs.image_tag }}"
$lines = @( $lines = @(
"REGISTRY_PREFIX=${{ vars.REGISTRY_HOST }}/${{ vars.REGISTRY_OWNER }}", "REGISTRY_PREFIX=${{ vars.REGISTRY_HOST }}/${{ vars.REGISTRY_OWNER }}",
"IMAGE_TAG=$imageTag", "IMAGE_TAG=$imageTag",
"STAGING_HTTP_PORT=$stagingPort", "STAGING_HTTP_PORT=$stagingPort",
"STAGING_LOCAL_PORT=$localPort",
"DEPLOY_SECRETS_DIR=$SD" "DEPLOY_SECRETS_DIR=$SD"
) )
Set-Location infrastructure Set-Location infrastructure

View File

@@ -144,7 +144,8 @@ Rules:
| `PUBLIC_BASE_URL` | `http://178.131.50.201:8088` | How **users** open staging in a browser (public IP OK) | | `PUBLIC_BASE_URL` | `http://178.131.50.201:8088` | How **users** open staging in a browser (public IP OK) |
| `DEPLOY_SECRETS_DIR` | `C:/dyolink/secrets` | Forward slashes OK on Windows | | `DEPLOY_SECRETS_DIR` | `C:/dyolink/secrets` | Forward slashes OK on Windows |
| `CLONE_HOST` | `127.0.0.1:3000` | Git clone (runs on Windows host, not inside Docker VM) | | `CLONE_HOST` | `127.0.0.1:3000` | Git clone (runs on Windows host, not inside Docker VM) |
| `STAGING_HTTP_PORT` | `8088` | Optional (8088 is default) | | `STAGING_HTTP_PORT` | `8088` | Public port (router + portproxy). Optional. |
| `STAGING_LOCAL_PORT` | `18088` | Docker bind on `127.0.0.1`. Must not be `8088` if portproxy already uses 8088. |
**Naming note:** Gitea rejects variable names starting with `GITEA_` or `GITHUB_`. Use `CLONE_HOST`, not `GITEA_CLONE_URL`. **Naming note:** Gitea rejects variable names starting with `GITEA_` or `GITHUB_`. Use `CLONE_HOST`, not `GITEA_CLONE_URL`.
@@ -167,18 +168,22 @@ New-NetFirewallRule -DisplayName "Dyolink Staging 8088" -Direction Inbound -Prot
### 7. External access on Windows + Docker Desktop (portproxy) ### 7. External access on Windows + Docker Desktop (portproxy)
Gitea on **:3000** runs natively on Windows and is reachable from your Mac. Staging **:8088** runs in **Docker Desktop**`127.0.0.1:8088` works on the PC, but `http://<public-ip>:8088` from another machine may get **Empty reply from server** unless you forward the port. Gitea on **:3000** runs natively on Windows. Staging nginx binds **127.0.0.1:18088** (not 8088). Windows **portproxy** then maps public **8088 → 18088**. Do **not** bind Docker on 8088 — portproxy already owns that port (`bind: access permissions` if you try).
Compose binds nginx to **127.0.0.1:8088** only. After deploy, run **once** in **PowerShell as Administrator**: Run **once** in **PowerShell as Administrator** (replace the old 8088→8088 rule if you had one):
```powershell ```powershell
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8088 connectaddress=127.0.0.1 connectport=8088 netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=8088
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8088 connectaddress=127.0.0.1 connectport=18088
netsh interface portproxy show all netsh interface portproxy show all
Start-Service iphlpsvc
Set-Service iphlpsvc -StartupType Automatic
``` ```
Verify on the server: Verify on the server:
```powershell ```powershell
curl http://127.0.0.1:18088/health
curl http://127.0.0.1:8088/health curl http://127.0.0.1:8088/health
``` ```
@@ -277,7 +282,8 @@ On the Windows host, from repo `infrastructure/`:
| `Missing database.staging.env` | Check `DEPLOY_SECRETS_DIR` path and file names | | `Missing database.staging.env` | Check `DEPLOY_SECRETS_DIR` path and file names |
| `docker login` denied | Token needs package permissions; check username/secret | | `docker login` denied | Token needs package permissions; check username/secret |
| Git clone fails in workflow | Set `CLONE_HOST=127.0.0.1:3000` | | Git clone fails in workflow | Set `CLONE_HOST=127.0.0.1:3000` |
| Port 8088 unreachable from Mac / empty reply | Docker Desktop: run **portproxy** (§7). `127.0.0.1:8088/health` must work on Windows first. | | Port 8088 bind forbidden / access permissions | Portproxy already owns 8088. Bind Docker to `18088` and point portproxy **8088 → 127.0.0.1:18088**. |
| Port 8088 unreachable from Mac / empty reply | Docker Desktop: run **portproxy** (§7). `127.0.0.1:18088/health` must work on Windows first. |
| Backend restart loop | JWT secrets still placeholder; fix `backend.staging.env` | | Backend restart loop | JWT secrets still placeholder; fix `backend.staging.env` |
| Backend DB auth error | `DATABASE_URL` password ≠ `POSTGRES_PASSWORD` | | Backend DB auth error | `DATABASE_URL` password ≠ `POSTGRES_PASSWORD` |
| `dumb-init docker-entrypoint.sh: No such file or directory` | Windows CRLF in shell scripts — fixed in Dockerfiles (rebuild images). | | `dumb-init docker-entrypoint.sh: No such file or directory` | Windows CRLF in shell scripts — fixed in Dockerfiles (rebuild images). |

View File

@@ -11,8 +11,10 @@ REGISTRY_PREFIX=178.131.50.201:3000/yourgiteauser
# Short git SHA from CI, or "latest" after a manual pull of :latest # Short git SHA from CI, or "latest" after a manual pull of :latest
IMAGE_TAG=latest IMAGE_TAG=latest
# Host port published for nginx (URL = http://<your-ip>:<this-port>) # Public URL port (router + Windows portproxy listen here)
STAGING_HTTP_PORT=8088 STAGING_HTTP_PORT=8088
# Docker nginx bind on localhost only (must differ from 8088 when portproxy owns 8088)
STAGING_LOCAL_PORT=18088
# Absolute path on the server where database.staging.env and backend.staging.env live. # Absolute path on the server where database.staging.env and backend.staging.env live.
# Use forward slashes on Windows. Same path as Gitea Actions variable DEPLOY_SECRETS_DIR. # Use forward slashes on Windows. Same path as Gitea Actions variable DEPLOY_SECRETS_DIR.

View File

@@ -91,8 +91,9 @@ services:
frontend: frontend:
condition: service_healthy condition: service_healthy
ports: ports:
# Localhost only — Docker Desktop on Windows often breaks 0.0.0.0; use netsh portproxy for LAN/public (see STAGING-DEPLOY.md). # Bind a private localhost port — Windows portproxy already owns public 8088.
- "127.0.0.1:${STAGING_HTTP_PORT:-8088}:80" # See STAGING-DEPLOY.md §7: portproxy 0.0.0.0:8088 → 127.0.0.1:18088
- "127.0.0.1:${STAGING_LOCAL_PORT:-18088}:80"
volumes: volumes:
- ./nginx/http-only.conf:/etc/nginx/conf.d/default.conf:ro - ./nginx/http-only.conf:/etc/nginx/conf.d/default.conf:ro
- ./logs/nginx-staging:/var/log/nginx - ./logs/nginx-staging:/var/log/nginx

View File

@@ -93,7 +93,7 @@ services:
frontend: frontend:
condition: service_healthy condition: service_healthy
ports: ports:
- "127.0.0.1:${STAGING_HTTP_PORT:-8088}:80" - "127.0.0.1:${STAGING_LOCAL_PORT:-18088}:80"
volumes: volumes:
- ./nginx/http-only.conf:/etc/nginx/conf.d/default.conf:ro - ./nginx/http-only.conf:/etc/nginx/conf.d/default.conf:ro
- ./logs/nginx-staging:/var/log/nginx - ./logs/nginx-staging:/var/log/nginx