chore(dev): standardize ports where they don't conflict

Most container ports go back to their natural defaults — the offsets
that existed weren't justified, they just made URLs harder to remember.
Now standard:

  backend         8080   (was always 8080)
  tracking        3000   (was 13000)
  realtime        4000   (was 14000)
  web             5173   (was 15173 — already changed)
  kafka           9092   (was 19092)
  schema-registry 8081   (was 18081)
  localstack      4566   (was 14566)
  cloud-tasks     8123   (was 18123)
  stripe-mock     12111  (always was)

Kept offset (the defaults conflict too often on real dev machines):

  postgres        15432  (system postgres / sibling project)
  redis           16379  (sibling docker projects with redis)
  mailpit ui      18025  (sibling docker projects with mailpit)
  mailpit smtp    11025  (same)
  kafka-ui        18090  (8080 already used by backend)

Touched: docker-compose.yml, Makefile (test-seed SEED_TEST_DB), READMEs
(root + deploy), local-development.md + deployment-guide.md. Internal
docker-network refs (kafka:29092, mailpit:1025, etc.) unchanged — only
host-port mappings moved. Compose validated, all default-profile
services come up healthy on the new ports.
This commit is contained in:
Matthew Meszaros
2026-05-22 14:51:16 +00:00
parent 33ea340660
commit e5c0c8a448
4 changed files with 57 additions and 41 deletions
+7 -7
View File
@@ -33,8 +33,8 @@ The frontend (React) talks to three control-plane services: the **Backend API**
| Service | Port (local) | Plane | Description |
|---------|------|-------|-------------|
| Backend | 8080 | control | REST API, auth, business logic, worker orchestration |
| Tracking | 13000 | control | Open/click pixel + redirect service |
| Realtime | 14000 | control | WebSocket gateway |
| Tracking | 3000 | control | Open/click pixel + redirect service |
| Realtime | 4000 | control | WebSocket gateway |
| Consumer | | control | Kafka event processor |
| Worker | | execution | SSH-managed sender process, one per VPS |
| Web | 5173 | | Vite dev server (frontend) |
@@ -60,15 +60,15 @@ make tools
Service URLs (all ports offset to avoid clashes with locally-installed daemons):
- Backend API: http://localhost:8080
- Tracking: http://localhost:13000
- Realtime: http://localhost:14000
- Tracking: http://localhost:3000
- Realtime: http://localhost:4000
- Web: http://localhost:5173
- Mailpit: http://localhost:18025
- Kafka: localhost:19092
- Schema Registry: http://localhost:18081
- Kafka: localhost:9092
- Schema Registry: http://localhost:8081
- Postgres: localhost:15432
- Redis: localhost:16379
- LocalStack: http://localhost:14566
- LocalStack: http://localhost:4566
- stripe-mock: http://localhost:12111
See [resources/local-development.md](resources/local-development.md) for the full setup.
+2 -2
View File
@@ -108,8 +108,8 @@ Each worker profile picks a release channel (`pinned` / `stable` / `dev`) and an
```bash
curl http://localhost:8080/health # backend
curl http://localhost:13000/health # tracking
curl http://localhost:14000/health # realtime
curl http://localhost:3000/health # tracking
curl http://localhost:4000/health # realtime
```
## Documentation
+35 -19
View File
@@ -15,11 +15,17 @@
# docker compose --profile seed run --rm seed
# docker compose --profile tools up -d kafka-ui
#
# All ports are offset so they don't clash with locally-run services:
# postgres 15432, redis 16379, kafka 19092, schema-registry 18081,
# backend 8080, tracking 13000, realtime 14000, web 5173,
# mailpit ui 18025 / smtp 11025, kafka-ui 18090,
# localstack 14566, stripe-mock 12111, cloud-tasks 18123.
# Standard ports throughout, with a few exceptions kept offset:
# - postgres 15432 (5432 commonly bound by system Postgres)
# - redis 16379 (6379 commonly bound by sibling projects)
# - mailpit ui 18025 / smtp 11025 (8025/1025 commonly bound by
# sibling projects)
# - kafka-ui 18090 (8080 is taken by backend)
#
# Standard ports for everything else:
# backend 8080, tracking 3000, realtime 4000, web 5173,
# kafka 9092, schema-registry 8081, localstack 4566,
# stripe-mock 12111, cloud-tasks 8123.
x-aws-localstack: &aws-localstack
AWS_ENDPOINT_URL: http://localstack:4566
@@ -59,6 +65,9 @@ services:
POSTGRES_USER: warmbly
POSTGRES_PASSWORD: warmbly
POSTGRES_DB: warmbly_dev
# 5432 is more often already taken on dev machines (system Postgres,
# another docker-compose project, etc.) than any other port in this
# stack — keep the offset for it specifically.
ports: ["15432:5432"]
volumes:
- postgres_data:/var/lib/postgresql/data
@@ -70,6 +79,7 @@ services:
redis:
image: redis:7-alpine
# 6379 commonly conflicts with another docker-compose project's redis.
ports: ["16379:6379"]
volumes:
- redis_data:/data
@@ -88,12 +98,12 @@ services:
kafka:
image: confluentinc/cp-kafka:7.5.0
depends_on: [zookeeper]
ports: ["19092:19092"]
ports: ["9092:9092"]
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:29092,PLAINTEXT_HOST://0.0.0.0:19092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:19092
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:29092,PLAINTEXT_HOST://0.0.0.0:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
@@ -110,7 +120,7 @@ services:
depends_on:
kafka:
condition: service_healthy
ports: ["18081:8081"]
ports: ["8081:8081"]
environment:
SCHEMA_REGISTRY_HOST_NAME: schema-registry
SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: kafka:29092
@@ -124,10 +134,12 @@ services:
- "8123"
- "-queue"
- "projects/local/locations/local/queues/default"
ports: ["18123:8123"]
ports: ["8123:8123"]
mailpit:
image: axllent/mailpit:latest
# 8025 / 1025 are common defaults in other docker-compose stacks
# (any project with mailpit). Offsetting to avoid conflict.
ports:
- "18025:8025" # web UI
- "11025:1025" # SMTP
@@ -140,7 +152,7 @@ services:
localstack:
image: localstack/localstack:3.7
ports:
- "14566:4566"
- "4566:4566"
environment:
SERVICES: kms,dynamodb,s3
DEBUG: "0"
@@ -149,7 +161,11 @@ services:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:4566/_localstack/health | grep -q '\"kms\": \"available\"'"]
# LocalStack reports each service as 'available' (initialised, never
# called) or 'running' (initialised + handled at least one request).
# Either state means KMS is up; the previous check only matched
# 'available' and flipped to unhealthy the moment anything used KMS.
test: ["CMD-SHELL", "curl -fsS http://localhost:4566/_localstack/health | grep -qE '\"kms\": \"(available|running)\"'"]
interval: 5s
timeout: 5s
retries: 30
@@ -189,10 +205,10 @@ services:
# Web origin for CORS. Backend's allow-list resolution checks
# CORS_ALLOW_ORIGINS, then APP_URL, then a fallback derived from
# WEBSOCKET_URL. Without this the origin would default to the
# realtime port (14000), not the Vite dev server (5173), and
# realtime port (4000), not the Vite dev server (5173), and
# browser preflights from the dashboard get 403'd.
APP_URL: "http://localhost:5173"
WEBSOCKET_URL: ws://localhost:14000/socket/websocket
WEBSOCKET_URL: ws://localhost:4000/socket/websocket
PRIMARY_DB: postgres://warmbly:warmbly@postgres:5432/warmbly_dev?sslmode=disable
REDIS: redis://redis:6379
@@ -206,7 +222,7 @@ services:
AUTH_SECRET: local-dev-auth-secret-minimum-32-characters-long
GOOGLE_CLIENT_ID: local-google-client-id
GOOGLE_CLIENT_SECRET: local-google-client-secret
GOOGLE_REDIRECT_URI: http://localhost:13000/auth/google/callback
GOOGLE_REDIRECT_URI: http://localhost:3000/auth/google/callback
APPLE_APP_ID: local-apple-app-id
APPLE_TEAM_ID: local-apple-team-id
APPLE_KEY_ID: local-apple-key-id
@@ -221,7 +237,7 @@ services:
EMAIL_NAME: Warmbly Dev
EMAIL_ADDRESS: dev@warmbly.local
TRACKING_DOMAIN: localhost:13000
TRACKING_DOMAIN: localhost:3000
SMTP_HOST: mailpit
SMTP_PORT: "1025"
@@ -320,7 +336,7 @@ services:
build:
context: ./tracking
dockerfile: Dockerfile
ports: ["13000:3000"]
ports: ["3000:3000"]
environment:
APP_ENV: dev
AWS_CONFIG_ENABLED: "false"
@@ -339,7 +355,7 @@ services:
build:
context: .
dockerfile: deploy/docker/realtime.Dockerfile
ports: ["14000:4000"]
ports: ["4000:4000"]
environment:
PHX_HOST: localhost
PORT: 4000
@@ -384,7 +400,7 @@ services:
environment:
VITE_APP_URL: "http://localhost:5173"
VITE_API_URL: "http://localhost:8080"
VITE_TRACKING_DOMAIN: "localhost:13000"
VITE_TRACKING_DOMAIN: "localhost:3000"
VITE_TURNSTILE_KEY: "1x00000000000000000000AA"
VITE_TURNSTILE_BYPASS_TOKEN: "warmbly-local-turnstile-bypass"
depends_on:
+13 -13
View File
@@ -75,22 +75,22 @@ The `sim` profile adds two more workers (`worker-premium-1`, `worker-dedicated-1
## Service URLs
All ports are offset to avoid colliding with locally-installed daemons:
Mostly standard ports. A few are offset because their defaults conflict too often: Postgres `15432`, Redis `16379`, Mailpit UI `18025` and SMTP `11025`, kafka-ui `18090` (because 8080 is backend). Everything else uses its natural port. Override locally in a `docker-compose.override.yml` if you still hit a conflict.
| Service | URL |
|---------|-----|
| Backend API | http://localhost:8080 |
| Tracking | http://localhost:13000 |
| Realtime | http://localhost:14000 |
| Tracking | http://localhost:3000 |
| Realtime | http://localhost:4000 |
| Web (Vite dev) | http://localhost:5173 |
| Mailpit | http://localhost:18025 |
| Kafka | localhost:19092 |
| Schema Registry | http://localhost:18081 |
| Kafka | localhost:9092 |
| Schema Registry | http://localhost:8081 |
| Postgres | localhost:15432 |
| Redis | localhost:16379 |
| LocalStack | http://localhost:14566 |
| LocalStack | http://localhost:4566 |
| stripe-mock | http://localhost:12111 |
| Cloud Tasks emulator | http://localhost:18123 |
| Cloud Tasks emulator | http://localhost:8123 |
| kafka-ui (with `make tools`) | http://localhost:18090 |
## Database setup
@@ -153,9 +153,9 @@ go install github.com/cosmtrek/air@latest
# Point at containerized infra
export PRIMARY_DB="postgres://warmbly:warmbly@localhost:15432/warmbly_dev?sslmode=disable"
export REDIS="redis://localhost:16379"
export KAFKA_BOOTSTRAP_SERVERS="localhost:19092"
export SCHEMA_REGISTRY_URL="http://localhost:18081"
export AWS_ENDPOINT_URL="http://localhost:14566"
export KAFKA_BOOTSTRAP_SERVERS="localhost:9092"
export SCHEMA_REGISTRY_URL="http://localhost:8081"
export AWS_ENDPOINT_URL="http://localhost:4566"
export AWS_REGION="us-east-1"
export AWS_ACCESS_KEY_ID="test"
export AWS_SECRET_ACCESS_KEY="test"
@@ -253,13 +253,13 @@ Or use kafka-ui at http://localhost:18090 (`make tools`).
### Inspect schema registry
```bash
curl http://localhost:18081/subjects | jq
curl http://localhost:18081/subjects/tracking-events-value/versions/latest | jq
curl http://localhost:8081/subjects | jq
curl http://localhost:8081/subjects/tracking-events-value/versions/latest | jq
```
## Troubleshooting
**Port already in use**`lsof -i :15432` (or whichever) to find what's holding it. The compose ports are offset on purpose; if you have a local Postgres on 5432 it shouldn't clash.
**Port already in use**`lsof -i :5432` (or whichever) to find what's holding it. The compose ports are offset on purpose; if you have a local Postgres on 5432 it shouldn't clash.
**Backend can't reach Kafka** — Kafka needs ~30s to fully start. Healthchecks gate the dependent services, so `docker compose up` should handle this. If you brought services up in a weird order, `docker compose restart backend consumer`.