From 803cc5470db82b603c1a189c208ecd3f653f5a0d Mon Sep 17 00:00:00 2001 From: tyler Date: Fri, 5 Jun 2026 19:12:06 -0600 Subject: [PATCH] fix: use fixed port 3010 for production postgrest, remove dynamic allocation Dynamic port leasing was the root cause of all the "address already in use" failures. Production now always uses 3010; dev still uses 3001. No more .postgrest-port file, no more port scanning loop, no more race conditions. Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/deploy.yml | 91 ++++++++++--------------------------- docker-compose.yml | 2 +- 2 files changed, 25 insertions(+), 68 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 752cc16..5118eac 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -29,77 +29,36 @@ jobs: APP_DIR=/home/tyler/route-commerce mkdir -p $APP_DIR - # Free the dev-stack port (3001) and the port the previous deploy used - # (so a new deploy can pick it back up if it's the lowest free port) - PREV_PORT=$(cat .postgrest-port 2>/dev/null || echo "") - for port in 3001 $PREV_PORT; do - if [ -n "$port" ] && ss -tln 2>/dev/null | grep -qE "[[:space:]]127\.0\.0\.1:${port}[[:space:]]"; then - echo "Port $port in use, freeing..." - fuser -k -9 $port/tcp 2>/dev/null || true - docker ps -aq --filter "publish=$port" 2>/dev/null | xargs -r docker rm -f 2>/dev/null || true - fi - done + # Production postgrest always owns port 3010. No dynamic allocation. + POSTGREST_HOST_PORT=3010 + export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT" - # Hard-stop the previous stack by name — works even if docker-compose.yml - # is missing or stale from a prior failed deploy. - # Note: multiple --filter name= flags are ANDed; use two separate calls. + # Tear down previous stack: kill containers by name (two separate calls + # because multiple --filter name= flags are ANDed, not ORed). { docker ps -aq --filter "name=route_commerce_" docker ps -aq --filter "name=route-commerce-" - } | sort -u | xargs -r docker rm -f - # Network can only be removed after all containers are detached. + } | sort -u | xargs -r docker rm -f || true + # Remove network after containers are gone so Docker allocates a fresh subnet. docker network rm route-commerce_default 2>/dev/null || true - sleep 2 - # Verify all route-commerce containers are gone before picking a port. - REMAINING=$({ docker ps -aq --filter "name=route_commerce_"; docker ps -aq --filter "name=route-commerce-"; } | sort -u | wc -l) - if [ "$REMAINING" -gt 0 ]; then - echo "ERROR: $REMAINING route-commerce container(s) still running after teardown" - docker ps --filter "name=route_commerce_" - docker ps --filter "name=route-commerce-" - exit 1 - fi + # Write fresh .env for docker compose + cp docker-compose.yml $APP_DIR/docker-compose.yml + cat > $APP_DIR/.env </dev/null | grep -qE "[[:space:]]127\.0\.0\.1:${POSTGREST_HOST_PORT}[[:space:]]"; then - break - fi - echo "Port $POSTGREST_HOST_PORT in use, trying next... (attempt $attempt)" - POSTGREST_HOST_PORT=$((POSTGREST_HOST_PORT + 1)) - if [ $POSTGREST_HOST_PORT -gt 30200 ]; then - echo "ERROR: no free port in 3011-30200 range" - exit 1 - fi - sleep 1 - done - echo "Using PostgREST host port: $POSTGREST_HOST_PORT" - echo "$POSTGREST_HOST_PORT" > .postgrest-port - export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT" - export POSTGREST_HOST_PORT - - # Seed config files into APP_DIR if missing (they live in the repo, not in APP_DIR) - [ -f $APP_DIR/.env.example ] || cp .env.example $APP_DIR/.env.example - [ -f $APP_DIR/docker-compose.yml ] || cp docker-compose.yml $APP_DIR/docker-compose.yml cd $APP_DIR - [ -f .env ] || cp .env.example .env - # Append production secrets to .env (overriding .env.example defaults) - { - echo "POSTGRES_USER=${POSTGRES_USER}" - echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" - echo "POSTGRES_DB=${POSTGRES_DB}" - echo "MINIO_ROOT_USER=${MINIO_ROOT_USER}" - echo "MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}" - echo "POSTGREST_JWT_SECRET=${POSTGREST_JWT_SECRET}" - echo "POSTGREST_HOST_PORT=$POSTGREST_HOST_PORT" - echo "NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL" - } >> .env - # Bring the stack up fresh — --force-recreate ensures no stale - # network/container references from prior failed attempts - docker compose up -d --force-recreate db postgrest minio minio_init - # Wait for Postgres healthcheck + docker compose up -d db postgrest minio + + # Wait for Postgres for i in $(seq 1 30); do if docker compose exec -T db pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" > /dev/null 2>&1; then echo "Postgres is ready" @@ -152,8 +111,7 @@ jobs: MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }} FROM_EMAIL: ${{ secrets.FROM_EMAIL }} run: | - POSTGREST_HOST_PORT=$(cat .postgrest-port) - export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT" + export NEXT_PUBLIC_API_URL="http://localhost:3010" npm run build - name: Deploy @@ -191,8 +149,7 @@ jobs: run: | APP_DIR=/home/tyler/route-commerce mkdir -p $APP_DIR - # Use the port chosen by Start Docker stack (persisted to .postgrest-port) - POSTGREST_HOST_PORT=$(cat .postgrest-port) + POSTGREST_HOST_PORT=3010 export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT" # Write env file from secrets (preserves existing .env for docker compose) diff --git a/docker-compose.yml b/docker-compose.yml index 69da5e5..36d4bad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,7 @@ services: PGRST_OPENAPI_MODE: disabled PGRST_DB_EXTRA_SEARCH_PATH: public,extensions ports: - - "127.0.0.1:${POSTGREST_HOST_PORT:-3011}:3001" + - "127.0.0.1:${POSTGREST_HOST_PORT:-3010}:3001" minio: image: minio/minio:latest