Short version: GitHub added entrypoint and command to service containers in GitHub Actions. If you have ever built a custom Docker image just to pass a single flag to a database in CI — this change is for you. Some of those old workarounds can now go.

What changed

In April 2026, GitHub updated the service container model in GitHub Actions: two new keys appeared in the jobs.<job_id>.services section — entrypoint and command. They work the way anyone who has written docker compose expects them: entrypoint replaces the container’s main startup command, and command passes arguments to it.

This means that for most common scenarios — configuring a database, changing ports, passing special flags — you no longer need a wrapper image or a bootstrap step in your workflow.

What pain this removes

Picture a typical situation: you need Postgres in CI with specific settings — maybe change shared_buffers, enable an extension, or set a particular encoding. Before this update, you had a few options, and none of them were great.

Option one — build a custom Docker image with the flags baked into ENTRYPOINT or CMD. This works, but it adds maintenance: you need to build the image, push it to a registry, and keep the base Postgres image up to date.

Option two — write a shell script inside the workflow that manually starts the service with docker run. This gives you control, but makes the workflow messier and less portable.

Option three — rely on env and options, hoping the service understands what you are passing through environment variables. Often this works, but not always, especially when you need to change the actual startup command, not just environment variables.

Now all of this gets covered with two lines in YAML. No wrapper images. No shell hacks. No magic.

Examples: what it looks like in practice

Postgres with custom flags

Before — custom image or options:

services:
  postgres:
    image: my-registry/postgres-custom:16-with-extensions
    env:
      POSTGRES_PASSWORD: test
    ports:
      - 5432:5432

After — native command:

services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_PASSWORD: test
    ports:
      - 5432:5432
    command: >-
      postgres
      -c shared_buffers=256MB
      -c max_connections=200

You no longer need a separate Docker image with a Dockerfile whose only purpose is to add -c to CMD.

MySQL with a non-default entrypoint

Say you need MySQL to start with a specific init script or override the default entrypoint:

services:
  mysql:
    image: mysql:8
    env:
      MYSQL_ROOT_PASSWORD: test
    ports:
      - 3306:3306
    entrypoint: ["docker-entrypoint.sh"]
    command: ["--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]

When the entrypoint differs from the default, command gets passed to it as arguments — the same way Docker Compose handles this.

etcd for distributed tests

etcd often needs specific flags at startup, especially when tests verify distributed logic:

services:
  etcd:
    image: bitnami/etcd:latest
    env:
      ALLOW_NONE_AUTHENTICATION: "yes"
    ports:
      - 2379:2379
    command: ["etcd", "--advertise-client-urls", "http://localhost:2379"]

Without this, you would previously need to find a special image or hack together a shell script.

Redis with custom config

services:
  redis:
    image: redis:7
    ports:
      - 6379:6379
    command: ["redis-server", "--maxmemory", "256mb", "--maxmemory-policy", "noeviction"]

When command is enough, and when you need to change entrypoint

The rule is simple.

command is sufficient when:

This covers 80–90% of database and cache use cases in CI.

You need to change entrypoint when:

A custom image is still justified when:

Honest limitations

This is not a revolution and does not solve every CI problem. A few things to keep in mind:

What to check in your workflows tomorrow

Walk through your .github/workflows/ and ask yourself a few simple questions:

  1. Do I have service containers that use custom images just to pass flags?
  2. Do I have docker run calls inside workflows that could be replaced with a native service container using command?
  3. Does my wrapper image only change CMD or ENTRYPOINT? If so — time to retire it.
  4. Am I running my full test suite after making the change? The service might start differently, and you want to catch that in CI, not in production.

If you find even one case where a custom image can be replaced with two lines of YAML — that is already a win. Fewer images means less maintenance, means fewer places for things to quietly break.

What not to do

Practical takeaway

GitHub removed one of the most annoying CI workarounds. If your team runs integration tests in GitHub Actions and spins up databases or caches there — spend 15 minutes and check which wrapper images or shell hacks you can now remove without risk.

The best part is that this change does not require learning anything new. You keep writing YAML, just without the extra abstraction layers around services that already know how to talk through the command line.