Tune Docker CLI Defaults with config.json

Edit config.json and you can make docker ps and docker images print columns you actually want. Every change here is reversible: back the file up first, and you can always put it back. Allow about 15 minutes for a first configuration and verification.

Before you start

This guide is for the Docker command-line client on Linux. It was checked with Docker CLI 29.8.1 from docker-ce-cli 5:29.8.1-1~ubuntu.24.04~noble. You need a normal shell account and a working Docker client. The examples that list containers and images need access to a Docker daemon, but editing the client file itself does not need sudo.

Docker normally reads $HOME/.docker/config.json. The file is JSON, not a shell script. Docker manages other files in the .docker directory, so limit manual edits to config.json. If the file already exists, preserve its unrelated properties, especially credential-store settings.

1. Inspect the active configuration location

Check the client version and the environment that can redirect the configuration directory:

$ docker --version
Docker version 29.8.1, build 4a63305
$ printf '%s\n' "${DOCKER_CONFIG:-$HOME/.docker}"
/home/USER/.docker

The second output is an example. Replace USER with your account name if you use the path in a later command. A set DOCKER_CONFIG changes the directory used by the client. The global --config option can select a different directory for one invocation, and takes precedence over DOCKER_CONFIG.

Checkpoint: Write down the directory printed by the command. If it is not the directory you expected, inspect your shell startup files before editing anything.

2. Back up config.json before editing

Make a dated copy only when the file exists:

$ config_dir="${DOCKER_CONFIG:-$HOME/.docker}"
$ config_file="$config_dir/config.json"
$ test -f "$config_file" && cp --preserve=mode,timestamps "$config_file" "$config_file.bak"
$ if test -f "$config_file.bak"; then printf 'backup: %s\n' "$config_file.bak"; else printf 'no existing config.json\n'; fi
backup: /home/USER/.docker/config.json.bak

If the backup command reports a permission error, stop and inspect ownership and permissions. Do not solve a client configuration problem by making the whole .docker directory world-readable. A Docker configuration can contain registry credentials, proxy URLs or references to a credential helper. Docker's documentation warns against sharing or committing it.

There is no need to restart Docker after changing this client file. The next Docker CLI invocation reads the selected configuration.

3. Add a readable default for docker ps

Use a JSON-aware editor if the file already contains settings. The smallest valid new file for this example is:

{
  "psFormat": "table {{.Names}}\\t{{.Status}}"
}

psFormat is used when docker ps has no --format option. The table directive keeps column headings, and the JSON string needs two backslashes so that Docker receives the template's \t tab escape. Do not replace an existing configuration wholesale unless you have copied every setting you need.

Validate the result with an ordinary, read-only command:

$ docker ps --no-trunc | head -5
NAMES                              STATUS
parish                             Up 5 hours (healthy)
app                                Up 17 hours
web.example.com                    Up 19 hours (healthy)

Your rows will differ. The useful check is that the headings and columns match the template. An explicit format overrides the file:

$ docker ps --format 'table {{.ID}}\t{{.Image}}'
CONTAINER ID   IMAGE

Checkpoint: If the explicit command works but the default does not, the client is probably reading another configuration directory. Re-run step 1 and check for a command alias or wrapper.

4. Set a matching default for docker images

The separate imagesFormat property controls docker images and docker image ls when neither command supplies --format. Add it beside psFormat:

{
  "psFormat": "table {{.Names}}\\t{{.Status}}",
  "imagesFormat": "table {{.Repository}}\\t{{.Tag}}"
}

Verify both the legacy and object-style command:

$ docker images | head -5
REPOSITORY                              TAG
parish                                  latest
app                                     latest
app                                     v0.3.0
$ docker image ls --format 'table {{.Repository}}\t{{.Tag}}' | head -3
REPOSITORY                              TAG
parish                                  latest

Do not assume that a format property changes every Docker listing. Current Docker releases expose additional properties for other commands, but each command's documentation defines its own template fields. If a template is rejected or renders blank values, remove the new property, restore the backup, and test a simpler field.

5. Change detach keys only when there is a real conflict

The default sequence for an attached container is ctrl-p,ctrl-q. It leaves the container running while detaching your terminal. A configuration property such as "detachKeys": "ctrl-e,e" changes the client default for docker attach, docker exec, docker run and docker start when those commands do not receive their own --detach-keys option.

Add it only if the normal sequence conflicts with your terminal or application:

{
  "psFormat": "table {{.Names}}\\t{{.Status}}",
  "imagesFormat": "table {{.Repository}}\\t{{.Tag}}",
  "detachKeys": "ctrl-e,e"
}

Test a per-command override before relying on the global setting. The command below starts a short-lived container and chooses a sequence for that invocation:

$ docker run --rm --detach-keys='ctrl-x,x' alpine:latest true

This example may pull an image if it is not already available. It does not keep a container running because true exits immediately. Treat detach sequences as a usability setting, not a security control. If you lose track of an attached terminal, use a second shell to inspect docker ps before sending signals or removing anything.

6. Treat headers and credentials as security-sensitive

HttpHeaders adds named headers to messages sent by the Docker client to the daemon. Docker passes these headers through; it does not interpret them or let them replace headers it sets itself. Use this only for a documented integration that requires a harmless, non-secret value:

{
  "HttpHeaders": {
    "X-Client-Environment": "staging"
  }
}

Do not put passwords, access tokens or private keys in this object. Headers can reach a remote daemon or an intermediary, and the configuration may be copied into backups. Current Docker releases also support the experimental DOCKER_CUSTOM_HEADERS environment variable, but the same exposure risk applies. Environment variables override properties in config.json, and command-line options override environment variables, so inspect all three layers when a value appears to be ignored.

7. Test an isolated configuration directory

For a new format, test without touching your real credentials or defaults. Put a minimal config.json in a temporary directory, then point one command at it:

$ mkdir -p /tmp/docker-config-test
$ editor /tmp/docker-config-test/config.json
$ docker --config /tmp/docker-config-test ps | head -3
NAMES                              STATUS
parish                             Up 5 hours (healthy)

The --config option applies to that command only. Do not copy your real configuration into /tmp, because temporary files may be readable by other local users depending on the directory permissions. Remove the test directory after checking it:

$ rm -rf /tmp/docker-config-test

This deletion is safe only for the disposable directory created above. Never substitute your real $HOME/.docker path.

8. Recover from a bad edit

If Docker reports invalid JSON or a command starts using unwanted defaults, stop using the broken file and restore the backup:

$ cp --preserve=mode,timestamps "$config_file.bak" "$config_file"
$ docker ps --format 'table {{.Names}}\t{{.Status}}' | head -3
NAMES                              STATUS

If the backup is unavailable, move the current file aside instead of deleting it, then create a minimal configuration with only settings you have verified. Removing a credential-store entry can change where future logins are saved, so check the file contents before any replacement. You do not need elevated privileges for these steps unless the file genuinely belongs to another account, in which case the correct fix is to use that account's Docker configuration.

Done means