GitHub Actions Service Container Port Not Reachable
A step cannot reach a service container because it connects to the wrong host or port - localhost works from a job running directly on the runner, but a container job must use the service name, and unmapped ports are unreachable from the host.
What this error means
A test or migration step fails with "connection refused" against a service like Postgres or Redis, even though the service container shows as healthy.
psql: error: connection to server at "localhost", port 5432 failed:
Connection refused
# the job runs in a container; the service is reachable as host "postgres", not localhostCommon causes
Wrong host for the network context
When the job itself runs in a container, services are reachable by their label as the hostname (for example postgres), not localhost. When the job runs directly on the runner, you reach a mapped port on localhost.
Port not mapped to the host
For a job on the runner, the service port must be mapped with ports: so it is exposed on localhost. Without the mapping, the host cannot connect.
How to fix it
Map the port for a runner-hosted job
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
ports:
- 5432:5432
steps:
- run: psql -h localhost -p 5432 ...Use the service name from a container job
- If the job runs in a container, connect to the service by its label (postgres:5432), not localhost.
- In that mode the ports mapping is unnecessary; containers share a network.
- Wait for the service to be healthy with a health check before connecting.
How to prevent it
- Map ports for runner-hosted jobs; use the service hostname for container jobs.
- Add a health check (or options --health-cmd) so steps wait until the service is ready.
- Keep the connection host and port consistent with where the job actually runs.