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 <noreply@anthropic.com>
This commit is contained in:
+24
-67
@@ -29,77 +29,36 @@ jobs:
|
|||||||
APP_DIR=/home/tyler/route-commerce
|
APP_DIR=/home/tyler/route-commerce
|
||||||
mkdir -p $APP_DIR
|
mkdir -p $APP_DIR
|
||||||
|
|
||||||
# Free the dev-stack port (3001) and the port the previous deploy used
|
# Production postgrest always owns port 3010. No dynamic allocation.
|
||||||
# (so a new deploy can pick it back up if it's the lowest free port)
|
POSTGREST_HOST_PORT=3010
|
||||||
PREV_PORT=$(cat .postgrest-port 2>/dev/null || echo "")
|
export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT"
|
||||||
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
|
|
||||||
|
|
||||||
# Hard-stop the previous stack by name — works even if docker-compose.yml
|
# Tear down previous stack: kill containers by name (two separate calls
|
||||||
# is missing or stale from a prior failed deploy.
|
# because multiple --filter name= flags are ANDed, not ORed).
|
||||||
# Note: multiple --filter name= flags are ANDed; use two separate calls.
|
|
||||||
{
|
{
|
||||||
docker ps -aq --filter "name=route_commerce_"
|
docker ps -aq --filter "name=route_commerce_"
|
||||||
docker ps -aq --filter "name=route-commerce-"
|
docker ps -aq --filter "name=route-commerce-"
|
||||||
} | sort -u | xargs -r docker rm -f
|
} | sort -u | xargs -r docker rm -f || true
|
||||||
# Network can only be removed after all containers are detached.
|
# Remove network after containers are gone so Docker allocates a fresh subnet.
|
||||||
docker network rm route-commerce_default 2>/dev/null || true
|
docker network rm route-commerce_default 2>/dev/null || true
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Verify all route-commerce containers are gone before picking a port.
|
# Write fresh .env for docker compose
|
||||||
REMAINING=$({ docker ps -aq --filter "name=route_commerce_"; docker ps -aq --filter "name=route-commerce-"; } | sort -u | wc -l)
|
cp docker-compose.yml $APP_DIR/docker-compose.yml
|
||||||
if [ "$REMAINING" -gt 0 ]; then
|
cat > $APP_DIR/.env <<EOF
|
||||||
echo "ERROR: $REMAINING route-commerce container(s) still running after teardown"
|
POSTGRES_USER=${POSTGRES_USER}
|
||||||
docker ps --filter "name=route_commerce_"
|
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
docker ps --filter "name=route-commerce-"
|
POSTGRES_DB=${POSTGRES_DB}
|
||||||
exit 1
|
MINIO_ROOT_USER=${MINIO_ROOT_USER}
|
||||||
fi
|
MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
|
||||||
|
POSTGREST_JWT_SECRET=${POSTGREST_JWT_SECRET}
|
||||||
|
POSTGREST_HOST_PORT=${POSTGREST_HOST_PORT}
|
||||||
|
NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||||
|
EOF
|
||||||
|
|
||||||
# Find the first free host port starting from 3011. Persist the choice
|
|
||||||
# so the Build and Deploy steps below can use the same URL.
|
|
||||||
POSTGREST_HOST_PORT=3011
|
|
||||||
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
|
||||||
if ! ss -tln 2>/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
|
cd $APP_DIR
|
||||||
[ -f .env ] || cp .env.example .env
|
docker compose up -d db postgrest minio
|
||||||
# Append production secrets to .env (overriding .env.example defaults)
|
|
||||||
{
|
# Wait for Postgres
|
||||||
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
|
|
||||||
for i in $(seq 1 30); do
|
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
|
if docker compose exec -T db pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" > /dev/null 2>&1; then
|
||||||
echo "Postgres is ready"
|
echo "Postgres is ready"
|
||||||
@@ -152,8 +111,7 @@ jobs:
|
|||||||
MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }}
|
MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }}
|
||||||
FROM_EMAIL: ${{ secrets.FROM_EMAIL }}
|
FROM_EMAIL: ${{ secrets.FROM_EMAIL }}
|
||||||
run: |
|
run: |
|
||||||
POSTGREST_HOST_PORT=$(cat .postgrest-port)
|
export NEXT_PUBLIC_API_URL="http://localhost:3010"
|
||||||
export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT"
|
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
@@ -191,8 +149,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
APP_DIR=/home/tyler/route-commerce
|
APP_DIR=/home/tyler/route-commerce
|
||||||
mkdir -p $APP_DIR
|
mkdir -p $APP_DIR
|
||||||
# Use the port chosen by Start Docker stack (persisted to .postgrest-port)
|
POSTGREST_HOST_PORT=3010
|
||||||
POSTGREST_HOST_PORT=$(cat .postgrest-port)
|
|
||||||
export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT"
|
export NEXT_PUBLIC_API_URL="http://localhost:$POSTGREST_HOST_PORT"
|
||||||
|
|
||||||
# Write env file from secrets (preserves existing .env for docker compose)
|
# Write env file from secrets (preserves existing .env for docker compose)
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ services:
|
|||||||
PGRST_OPENAPI_MODE: disabled
|
PGRST_OPENAPI_MODE: disabled
|
||||||
PGRST_DB_EXTRA_SEARCH_PATH: public,extensions
|
PGRST_DB_EXTRA_SEARCH_PATH: public,extensions
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${POSTGREST_HOST_PORT:-3011}:3001"
|
- "127.0.0.1:${POSTGREST_HOST_PORT:-3010}:3001"
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio:latest
|
image: minio/minio:latest
|
||||||
|
|||||||
Reference in New Issue
Block a user