{"meta":{"title":"Communicating with Docker service containers","intro":"Learn how to use Docker service containers to connect databases, web services, memory caches, and other tools to your workflow.","product":"GitHub Actions","breadcrumbs":[{"href":"/en/actions","title":"GitHub Actions"},{"href":"/en/actions/tutorials","title":"Tutorials"},{"href":"/en/actions/tutorials/use-containerized-services","title":"Use containerized services"},{"href":"/en/actions/tutorials/use-containerized-services/use-docker-service-containers","title":"Use Docker service containers"}],"documentType":"article"},"body":"# Communicating with Docker service containers\n\nLearn how to use Docker service containers to connect databases, web services, memory caches, and other tools to your workflow.\n\n## Communicating with Docker service containers\n\nService containers are Docker containers that provide a simple and portable way for you to host services that you might need to test or operate your application in a workflow. For example, your workflow might need to run integration tests that require access to a database and memory cache.\n\nYou can configure service containers for each job in a workflow. GitHub creates a fresh Docker container for each service configured in the workflow, and destroys the service container when the job completes. Steps in a job can communicate with all service containers that are part of the same job. However, you cannot create and use service containers inside a composite action.\n\n> \\[!NOTE]\n> If your workflows use Docker container actions, job containers, or service containers, then you must use a Linux runner:\n>\n> * If you are using GitHub-hosted runners, you must use an Ubuntu runner.\n> * If you are using self-hosted runners, you must use a Linux machine as your runner and Docker must be installed.\n\nYou can configure jobs in a workflow to run directly on a runner machine or in a Docker container. Communication between a job and its service containers is different depending on whether a job runs directly on the runner machine or in a container.\n\n### Running jobs in a container\n\nWhen you run jobs in a container, GitHub connects service containers to the job using Docker's user-defined bridge networks. For more information, see [Bridge network driver](https://docs.docker.com/engine/network/drivers/bridge/) in the Docker documentation.\n\nRunning the job and services in a container simplifies network access. You can access a service container using the label you configure in the workflow. The hostname of the service container is automatically mapped to the label name. For example, if you create a service container with the label `redis`, the hostname of the service container is `redis`.\n\nYou don't need to configure any ports for service containers. By default, all containers that are part of the same Docker network expose all ports to each other, and no ports are exposed outside of the Docker network.\n\n### Running jobs on the runner machine\n\nWhen running jobs directly on the runner machine, you can access service containers using `localhost:<port>` or `127.0.0.1:<port>`. GitHub configures the container network to enable communication from the service container to the Docker host.\n\nWhen a job runs directly on a runner machine, the service running in the Docker container does not expose its ports to the job on the runner by default. You need to map ports on the service container to the Docker host. For more information, see [Communicating with Docker service containers](/en/actions/using-containerized-services/about-service-containers#mapping-docker-host-and-service-container-ports).\n\n## Creating service containers\n\nYou can use the `services` keyword to create service containers that are part of a job in your workflow. For more information, see [`jobs.<job_id>.services`](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservices).\n\nThis example creates a service called `redis` in a job called `container-job`. The Docker host in this example is the `node:16-bullseye` container.\n\n```yaml copy\nname: Redis container example\non: push\n\njobs:\n  # Label of the container job\n  container-job:\n    # Containers must run in Linux based operating systems\n    runs-on: ubuntu-latest\n    # Docker Hub image that `container-job` executes in\n    container: node:16-bullseye\n\n    # Service containers to run with `container-job`\n    services:\n      # Label used to access the service container\n      redis:\n        # Docker Hub image\n        image: redis\n```\n\n## Mapping Docker host and service container ports\n\nIf your job runs in a Docker container, you do not need to map ports on the host or the service container. If your job runs directly on the runner machine, you'll need to map any required service container ports to ports on the host runner machine.\n\nYou can map service containers ports to the Docker host using the `ports` keyword. For more information, see [`jobs.<job_id>.services`](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservices).\n\n| Value of `ports` | Description                                                                       |\n| ---------------- | --------------------------------------------------------------------------------- |\n| `8080:80`        | Maps TCP port 80 in the container to port 8080 on the Docker host.                |\n| `8080:80/udp`    | Maps UDP port 80 in the container to port 8080 on the Docker host.                |\n| `8080/udp`       | Maps a randomly chosen port on the Docker host to UDP port 8080 in the container. |\n\nWhen you map ports using the `ports` keyword, GitHub uses the `--publish` command to publish the container’s ports to the Docker host. For more information, see [Docker container networking](https://docs.docker.com/config/containers/container-networking/) in the Docker documentation.\n\nWhen you specify the container port but not the Docker host port, the container port is randomly assigned to a free port. GitHub sets the assigned container port in the service container context. For example, for a `redis` service container, if you configured the Docker host port 5432, you can access the corresponding container port using the `job.services.redis.ports[5432]` context. For more information, see [Contexts reference](/en/actions/learn-github-actions/contexts#job-context).\n\n### Example mapping Redis ports\n\nThis example maps the service container `redis` port 6379 to the Docker host port 6379.\n\n```yaml copy\nname: Redis Service Example\non: push\n\njobs:\n  # Label of the container job\n  runner-job:\n    # You must use a Linux environment when using service containers or container jobs\n    runs-on: ubuntu-latest\n\n    # Service containers to run with `runner-job`\n    services:\n      # Label used to access the service container\n      redis:\n        # Docker Hub image\n        image: redis\n        #\n        ports:\n          # Opens tcp port 6379 on the host and service container\n          - 6379:6379\n```\n\n## Authenticating with image registries\n\nYou can specify credentials for your service containers in case you need to authenticate with an image registry. This allows you to use images from private registries or to [increase your DockerHub rate limit](https://www.docker.com/increase-rate-limits/).\n\nHere’s an example of authenticating with Docker Hub and the GitHub Container registry:\n\n```yaml copy\njobs:\n  build:\n    services:\n      redis:\n        # Docker Hub image\n        image: redis\n        ports:\n          - 6379:6379\n        credentials:\n          username: ${{ secrets.dockerhub_username }}\n          password: ${{ secrets.dockerhub_password }}\n      db:\n        # Private registry image\n        image: ghcr.io/octocat/testdb:latest\n        credentials:\n          username: ${{ github.repository_owner }}\n          password: ${{ secrets.ghcr_password }}\n```\n\n## Customizing service container entrypoints and commands\n\nBy default, service containers run with the entrypoint and command defined in the Docker image. You can override these using the `entrypoint` and `command` keys. This is useful when you need to pass flags to a service (such as a database) or swap the image entrypoint entirely, without building a custom wrapper image.\n\nThe `command` key overrides the image's default command (`CMD`). Most scenarios only need `command`—the image already has the right entrypoint, you just need to pass flags:\n\n```yaml copy\nservices:\n  mysql:\n    image: mysql:8\n    command: --sql_mode=STRICT_TRANS_TABLES --max_allowed_packet=512M\n    env:\n      MYSQL_ROOT_PASSWORD: test\n    ports:\n      - 3306:3306\n```\n\nThe `entrypoint` key overrides the image's `ENTRYPOINT`. You can combine it with `command` to pass arguments to the custom entrypoint:\n\n```yaml copy\nservices:\n  etcd:\n    image: quay.io/coreos/etcd:v3.5.17\n    entrypoint: etcd\n    command: >-\n      --listen-client-urls http://0.0.0.0:2379\n      --advertise-client-urls http://0.0.0.0:2379\n    ports:\n      - 2379:2379\n```\n\nThe naming and behavior match Docker Compose. For more information, see [`jobs.<job_id>.services.<service_id>.command`](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcommand) and [`jobs.<job_id>.services.<service_id>.entrypoint`](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_identrypoint).\n\n## Further reading\n\n* [Creating Redis service containers](/en/actions/using-containerized-services/creating-redis-service-containers)\n* [Creating PostgreSQL service containers](/en/actions/using-containerized-services/creating-postgresql-service-containers)"}