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:
- the service starts fine with the default entrypoint;
- you only need to pass arguments or flags;
- you are tweaking configuration that the service accepts via its command line.
This covers 80–90% of database and cache use cases in CI.
You need to change entrypoint when:
- the service ignores
commandbecause of how its Dockerfile is written (not all, but it happens); - you need to run a wrapper script before the main process;
- you are changing the startup command itself, not just its arguments.
A custom image is still justified when:
- you need pre-installed extensions or libraries;
- you need to modify the container’s filesystem before startup;
- there is a complex initialization procedure that cannot be expressed through the command line.
Honest limitations
This is not a revolution and does not solve every CI problem. A few things to keep in mind:
- Service containers in GitHub Actions only work on Linux runners. If your workflow runs on a Windows or macOS runner, this will not help.
entrypointandcommanddo not replaceoptions. For volume mounts, network configuration, and other Docker flags, you still needoptions.- Not every service behaves nicely when you change its entrypoint or command in CI. Some official images have very specific logic in their
docker-entrypoint.sh, and changing it can break initialization. - This does not apply to containerized jobs (where the entire job runs inside a container) — only to service containers.
What to check in your workflows tomorrow
Walk through your .github/workflows/ and ask yourself a few simple questions:
- Do I have service containers that use custom images just to pass flags?
- Do I have
docker runcalls inside workflows that could be replaced with a native service container usingcommand? - Does my wrapper image only change CMD or ENTRYPOINT? If so — time to retire it.
- 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
- Do not delete custom images immediately — verify first that the new approach produces identical test results.
- Do not change
entrypointwithout a real need. Ifcommandis enough — usecommand. - Do not forget that
optionsis still needed for volume mounts, networking, and other Docker-level settings. - Do not assume all official images react the same way to entrypoint changes — always test.
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.