GitHub Actions with Docker Containers: Services & Jobs
GitHub Actions supports running jobs inside Docker containers and defining service containers for dependencies like databases, making it easy to run integration tests in isolated environments.
What You'll Learn
In this tutorial, you'll learn how to run a job inside a container, define service containers for databases and caches, build and push Docker images, and use Docker layer Caching in workflows.
Why It Matters
Many applications depend on databases, Message Queues, or caches during testing. Service containers let you spin up these dependencies directly in the workflow without external infrastructure. Running jobs in containers ensures a consistent environment regardless of the runner operating system.
Real-World Use
Durga Antivirus Pro's integration tests spin up PostgreSQL, Redis, and RabbitMQ as service containers, then run the scan engine inside a container that connects to all three. The entire test environment is self-contained within the workflow and is torn down automatically when the job completes.
Running Jobs in Containers
Specify a container for the entire job:
jobs:
test:
runs-on: ubuntu-latest
container:
image: node:20-alpine
env:
NODE_ENV: test
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
Service Containers
Define service containers that the job can connect to:
jobs:
integration:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:alpine
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- run: npm test
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
REDIS_URL: redis://localhost:6379
Building and Pushing Images
Build a Docker image and push it to a registry:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/myorg/my-app:latest
Docker Layer Caching
Use GitHub Actions cache for Docker layers:
- uses: docker/setup-buildx-action@v3
- uses: docker/cache-action@v1
with:
path: /tmp/.buildx-cache
- uses: docker/build-push-action@v6
with:
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
Practice Questions
1. How do you define a service container in a workflow?
Use the services key at the job level with a map of service names to container images.
2. How does a job connect to a service container?
Service containers are accessible at localhost on the ports they expose.
3. What is the benefit of running a job inside a container? It provides a consistent, reproducible environment regardless of the runner's operating system.
4. How do you authenticate to GitHub Container Registry?
Use docker/login-action with ${{ secrets.GITHUB_TOKEN }} as the password.
5. Challenge: Create a workflow with a PostgreSQL service container. Run a Python script that creates a table, inserts data, and verifies the data was stored.
Mini Project: Full Integration Test Stack
Create a workflow with three service containers: PostgreSQL, Redis, and RabbitMQ. The job runs a Go application that connects to all three services, performs CRUD operations, and publishes messages. Verify all connections succeed and data flows end-to-end between services.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro