CI: Postgres on 127.0.0.1, because the runner uses host networking
CI / verify (push) Successful in 1m59s

The runner is configured with `container.network: host`. That one setting
explains all three earlier failures, and the workflow now records them so
nobody repeats the sequence:

  services:                     not resolvable by name from a host-networked
                                job — "getaddrinfo EAI_AGAIN postgres"
  --network container:$HOSTNAME /etc/hostname is the HOST's name, not a
                                container id, so the join finds nothing
  default-gateway addressing    wrong idea outright: with host networking the
                                default route is the real router, not a bridge

Sharing the host's network namespace means a published port is just on
127.0.0.1. Readiness is now checked over TCP from the job itself rather than
with pg_isready inside the container — the latter proves the server started,
not that this job can reach it, which is the thing that actually failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 20:32:06 -07:00
parent e6b4c1618e
commit 2a30645c8d
+31 -23
View File
@@ -30,21 +30,25 @@ jobs:
verify:
runs-on: ubuntu-latest
# Postgres is started as a step rather than through `services:`.
# Postgres is started as a step rather than through `services:`, because
# this runner is configured with `container.network: host`.
#
# Two approaches were tried and rejected before this one:
# That single setting explains three failed attempts, and is worth writing
# down so nobody repeats them:
#
# `services:` — this runner does not attach service
# containers to the job network, giving
# "getaddrinfo EAI_AGAIN postgres".
# `--network container:$HOSTNAME` /etc/hostname inside the job reports
# the HOST's name, not the container id,
# so the namespace join finds no such
# container.
# `services:` Service containers are not resolvable by
# name from a host-networked job, giving
# "getaddrinfo EAI_AGAIN postgres".
# `--network container:$HOSTNAME` /etc/hostname reports the HOST's name,
# not a container id, so the namespace
# join finds no such container.
# default-gateway addressing The wrong idea entirely: with host
# networking the default route is the
# real router, not a docker bridge.
#
# What works without needing to know our own container id: publish the port
# on the host and reach it through the job container's default gateway. The
# port is derived from the run id so concurrent runs cannot collide.
# Because the job shares the host's network namespace, a published port is
# simply on 127.0.0.1. The port is derived from the run id so concurrent
# runs cannot collide.
env:
PG_CONTAINER: pig-ci-pg-${{ github.run_id }}
@@ -57,29 +61,33 @@ jobs:
- name: Start Postgres
run: |
# A per-run port in the ephemeral range, so two runs never collide.
PG_PORT=$(( 45000 + (${{ github.run_id }} % 15000) ))
GATEWAY=$(ip route | awk '/^default/ {print $3; exit}')
if [ -z "$GATEWAY" ]; then echo "Could not determine the gateway"; exit 1; fi
echo "Postgres will be published on ${GATEWAY}:${PG_PORT}"
echo "Publishing Postgres on 127.0.0.1:${PG_PORT}"
docker rm -f "$PG_CONTAINER" 2>/dev/null || true
docker run -d --name "$PG_CONTAINER" \
-p "${PG_PORT}:5432" \
-p "127.0.0.1:${PG_PORT}:5432" \
-e POSTGRES_USER=pig -e POSTGRES_PASSWORD=pig -e POSTGRES_DB=pig \
postgres:16-alpine
# pg_isready inside the container only proves the server started.
# What matters is that THIS job can reach it through the published
# port, so the readiness check is made from here, over TCP.
for i in $(seq 1 60); do
if docker exec "$PG_CONTAINER" pg_isready -U pig -q; then
echo "Postgres ready after ${i}s"
# Export for every later step.
echo "DATABASE_URL=postgres://pig:pig@${GATEWAY}:${PG_PORT}/pig" >> "$GITHUB_ENV"
if node -e "
const net=require('net');
const s=net.connect(${PG_PORT},'127.0.0.1');
s.on('connect',()=>{s.end();process.exit(0)});
s.on('error',()=>process.exit(1));
" 2>/dev/null; then
echo "Reachable after ${i}s"
echo "DATABASE_URL=postgres://pig:pig@127.0.0.1:${PG_PORT}/pig" >> "$GITHUB_ENV"
exit 0
fi
sleep 1
done
echo "Postgres did not become ready"
docker logs "$PG_CONTAINER" | tail -30
echo "Postgres never became reachable on 127.0.0.1:${PG_PORT}"
docker logs "$PG_CONTAINER" 2>&1 | tail -30
exit 1
- name: Install