Skip to content
Latchkey

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.

Actions log
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 localhost

Common 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

.github/workflows/ci.yml
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

  1. If the job runs in a container, connect to the service by its label (postgres:5432), not localhost.
  2. In that mode the ports mapping is unnecessary; containers share a network.
  3. 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.

Frequently asked questions

What causes "Service port unreachable"?
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.
How do I fix Service port unreachable?
Map the port for a runner-hosted job

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card