Skip to content

worker: PORT/HEALTH_PORT precedence is inverted relative to every sibling service #184

Open

Description

@jerelvelarde

Found during the review of #180. Not changed there — altering port precedence on a hotfix risked the live deploy.

apps/worker/src/index.ts resolves process.env.PORT ?? process.env.HEALTH_PORT ?? '3003', preferring PORT. Every other health-serving app reads HEALTH_PORT only (apps/discord-bot, apps/slack-bot, apps/teams-bot, apps/linear-sync), reserving PORT for a separate listener. The worker is the outlier, and docs/deployment.md already documents the asymmetry as known.

Three concrete problems:

  1. Whenever the platform injects PORT, the app binds it while ENV HEALTH_PORT=3005 and EXPOSE 3005 say otherwise. fix(worker): repair the missing SystemConfig table, guard against schema drift, and gate it in CI #180 made the Dockerfile HEALTHCHECK follow the same precedence as a stopgap, but the underlying disagreement remains.
  2. PORT="" (a cleared platform variable) yields parseInt('') → NaN, and listen(NaN) throws ERR_SOCKET_BAD_PORT synchronously at module scope — the process dies before binding, producing the same opaque "replicas never became healthy" signal as the original incident.
  3. The code's 3003 fallback disagrees with the Dockerfile's 3005 and collides with teams-bot's port.

Also worth fixing alongside: healthServer has no 'error' listener, so EADDRINUSE/EACCES is an uncaught exception with a bare stack trace.

Activity

  1. added
    roadmapTracked on the Outpost roadmap
    roadmap: nextRoadmap horizon: after launch path clears
    area: infrastructureWorker, queue, CI, deploy, containers, observability
    on Aug 17, 2026
  2. NathanTarbert commented on Sep 15, 2026

    @NathanTarbert
    Collaborator

    PR #262 addresses part of this, and deliberately not the headline. Recording the split so the remaining scope is clear once that merges.

    Addressed by #262: an empty or non-numeric PORT no longer parses to NaN and kills the process at module scope, and the health server gains an error listener so a bind failure is reported rather than thrown as an uncaught exception.

    Not addressed, deliberately: the precedence itself. apps/worker/src/index.ts:102 still reads PORT before HEALTH_PORT with a 3003 fallback, while every sibling reads HEALTH_PORT only — apps/discord-bot/src/index.ts:10 falls back to 3001, and the worker's own Dockerfile declares HEALTH_PORT=3005 and exposes 3005. So the image and the process still disagree about which variable and which default govern the port.

    That disagreement is what this issue is about, and it survives #262. Worth keeping open with the scope trimmed to the precedence and the fallback mismatch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: infrastructureWorker, queue, CI, deploy, containers, observabilityroadmapTracked on the Outpost roadmaproadmap: nextRoadmap horizon: after launch path clears

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions