What Is a Service Container in CI? Databases for Your Tests
A service container is a supporting container - a database, cache, or queue - that your CI provider starts next to your job so integration tests have something real to talk to.
Many tests need a live dependency: a Postgres database, a Redis cache, a message broker. Rather than installing and managing these on the runner, CI systems let you declare service containers. The provider starts them alongside your job, wires up networking, and tears them down afterward.
The problem they solve
Integration tests are far more valuable when they run against the real database engine, not a mock. Service containers make that cheap and repeatable - a clean Postgres for every run, with no host pollution.
How jobs reach them
The service is reachable over the network by a hostname (often the service’s label). Your test config points at that host and the mapped port, and connects as it would to any database.
A GitHub Actions example
In a workflow you add a services: block with a postgres image, env for the password, and a port mapping. The runner starts it before your steps; tests connect to it via the service hostname.
Readiness matters
- A database may accept connections only after it finishes initializing.
- Health checks or a wait loop prevent "connection refused" flakes.
- Seed data in an init step so tests start from a known state.
Service containers and reliability
Pulling and starting service images adds time and an occasional transient failure. Managed runners cache common service images and auto-retry registry blips, so a slow or flaky pull does not fail an otherwise-green test run.
Key takeaways
- A service container is a CI-managed dependency (DB, cache, queue) for tests.
- Jobs reach it over the network by hostname and port.
- Wait for readiness to avoid connection-refused flakiness.