fix(pii): listen on 5001 to avoid app :3000 collision (awsvpc)#5182
Conversation
The pii image hardcoded uvicorn --port 3000 and ignored env. In the app ECS
task (awsvpc) all containers share one network namespace, and the app owns
3000 — so the sidecar must listen elsewhere (the stock presidio images honored
PORT and ran on 5002/5001). Bind ${PORT} (shell-form CMD), default 5001, and
update EXPOSE/HEALTHCHECK accordingly so the taskdef can set PORT=5001.
Verified: default binds 5001; PORT=5002 override binds 5002; /analyze works on
the overridden port.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryLow Risk Overview
Reviewed by Cursor Bugbot for commit 45a1694. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
@greptile review |
Greptile SummaryThis PR fixes a port collision in the
Confidence Score: 4/5Safe to merge — the change is isolated to one Dockerfile and correctly resolves the port collision with a well-understood pattern. The core mechanics are sound: docker/pii.Dockerfile — verify the stale "port (3000)" comment at the top is updated before the image is published. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant TD as ECS TaskDef
participant App as App Container (:3000)
participant PII as PII Sidecar
Note over TD: awsvpc — shared network namespace
TD->>PII: "start with PORT=5001 env var"
PII->>PII: "sh -c "exec uvicorn ... --port ${PORT}""
PII-->>TD: listening on :5001
TD->>App: start (owns :3000)
App-->>TD: listening on :3000
Note over App,PII: No port collision
TD->>PII: "HEALTHCHECK curl http://localhost:${PORT}/health"
PII-->>TD: 200 OK
TD->>App: "route PII_URL=http://localhost:5001"
App->>PII: POST /analyze
PII-->>App: PII spans response
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant TD as ECS TaskDef
participant App as App Container (:3000)
participant PII as PII Sidecar
Note over TD: awsvpc — shared network namespace
TD->>PII: "start with PORT=5001 env var"
PII->>PII: "sh -c "exec uvicorn ... --port ${PORT}""
PII-->>TD: listening on :5001
TD->>App: start (owns :3000)
App-->>TD: listening on :3000
Note over App,PII: No port collision
TD->>PII: "HEALTHCHECK curl http://localhost:${PORT}/health"
PII-->>TD: 200 OK
TD->>App: "route PII_URL=http://localhost:5001"
App->>PII: POST /analyze
PII-->>App: PII spans response
|
EXPOSE can't be parameterized, so the configurable-PORT approach left EXPOSE
showing 5001 regardless (Greptile P2). We own both the image and the taskdef
and only ever need 5001, so hardcode it: exec-form CMD on 5001, EXPOSE 5001,
healthcheck on 5001. Runtime cmdline is identical to the verified ${PORT}
default (uvicorn ... --port 5001).
|
@greptile review |
| CMD curl -fsS http://localhost:5001/health || exit 1 | ||
|
|
||
| CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "3000"] | ||
| CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "5001"] |
There was a problem hiding this comment.
$PORT is ignored — exec-form CMD bypasses shell variable expansion
The PR description says "bind ${PORT} via a shell-form CMD, default 5001" and the testing section claims PORT=5002 binds 5002. But the CMD uses JSON/exec-form (["uvicorn", …, "--port", "5001"]), which is passed directly to the process without invoking a shell — $PORT is never expanded. Setting PORT=5002 in the ECS taskdef will have no effect; the container will still bind 5001. The HEALTHCHECK on line 48 also hardcodes 5001, so any env-var override would cause the health check to probe the wrong port. To match the stated intent, both lines need to use shell-form and read ${PORT:-5001}.
| CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "5001"] | |
| CMD uvicorn server:app --host 0.0.0.0 --port ${PORT:-5001} |
Summary
sim/piiimage hardcodeduvicorn --port 3000, so it always bound 3000.sim/piisidecar on 3000 collides with it and breaks the task. (The stockmcrpresidio images ran on 5002/5001, which is why they coexisted with the app.)CMDon 5001,EXPOSE 5001, healthcheck on 5001.Why hardcode 5001 (not a configurable
$PORT)An earlier revision made the port configurable via
$PORT, butEXPOSEcan't be parameterized in a Dockerfile, so it always showed 5001 regardless of the runtime port (Greptile P2). We own both the image and the taskdef and only ever need one port, so hardcoding 5001 keepsEXPOSE/healthcheck/CMD consistent and avoids the mismatch. (Implication: the taskdef just needscontainerPort: 5001; aPORTenv on the sidecar is a no-op.)Deploy ordering
Must be in ECR before the infra taskdef points the sidecar at
sim/pii— otherwise the taskdef would run the old 3000-binding image and collide with the app. Independent of the app-rewire PR (which deploys after the taskdef). Sequence: this → infra taskdef (containerPort 5001,PII_URL=http://localhost:5001) → app-rewire.Testing
Built locally and verified the service binds 5001 and
/analyzereturns spans on that port. The hardcoded build produces an identical runtime process (uvicorn server:app --host 0.0.0.0 --port 5001) to the verified revision; CI rebuilds the image on merge.Type of Change
Checklist