skip to content

How do you configure a third-party service image such as `postgres` without building your own image?

level: middleimportance: must knowfreq 68%

answer

  1. The image already has a configuration surface
  2. Read the image's documented variables first
  3. An entrypoint script applies them at start
  4. POSTGRES_PASSWORD, MYSQL_ROOT_PASSWORD and friends
  5. A _FILE suffix keeps values out of inspect

basics

~20 s

Through the environment variables the image documents. A service image's entrypoint script reads them at container start and writes the real configuration, so docker run -e POSTGRES_PASSWORD=... -e POSTGRES_DB=... configures it without a Dockerfile of your own.

solid answer

~50 s

Well-behaved service images publish a configuration contract made of environment variables, and the image's `docker-entrypoint.sh` turns those into real configuration before it starts the server. So you configure them at `docker run` (or Compose) time: `-e POSTGRES_PASSWORD`, `-e POSTGRES_USER`, `-e POSTGRES_DB` for the Official Postgres image, `-e MYSQL_ROOT_PASSWORD` and friends for MySQL. Two things are worth knowing beyond the list. First, some variables are read on **every** start and some only during **first-time initialization** of an empty data directory — changing the latter later has no effect. Second, several images support a `_FILE` suffix (`POSTGRES_PASSWORD_FILE`) so the value is read from a mounted file instead of living in the process environment, where `docker inspect` and anyone with daemon access can read it. When no variable exists for what you need, the escape hatches are mounting a config file at the path the image documents, or passing server flags after the image name.

code

bash · 7 lines
bash
docker run -d --name billing-db \
  -e POSTGRES_USER=billing \
  -e POSTGRES_DB=subscriptions \
  -e POSTGRES_PASSWORD_FILE=/run/secrets/pgpass \
  -v /etc/billing/pgpass:/run/secrets/pgpass:ro \
  -v billing-pgdata:/var/lib/postgresql/data \
  postgres:16

go deeper

for a junior

Know that you configure somebody else's service image by passing environment variables at run time, and that the image's Docker Hub page lists which ones it accepts and which are mandatory.

for a middle

Explain the mechanism: the image's entrypoint script reads the environment before starting the server, some variables apply only during first-time initialization, and the _FILE suffix reads a value from a mounted file instead.

for a senior

Demonstrate the operational discipline: environment is fixed at container creation, credentials belong in files rather than Config.Env, and configuration lives in a checked-in Compose or env file so a container can be recreated identically.

for a principal

Own the standard. Decide how configuration and credentials reach containers across the estate, so teams are not inventing per-service conventions, and so a rotated credential is a redeploy rather than an archaeology exercise.

### The contract The single most common thing anyone does with Docker is run an image somebody else built — a database, a cache, a broker, a web server — and the interesting question is how you make it behave the way you need without forking it. The answer is that a service image is not just a filesystem: it ships an **entrypoint script** that runs before the server does, and that script defines a configuration contract expressed as environment variables. On the Official Images this is `docker-entrypoint.sh`, and reading it is the fastest way to learn exactly what an image will accept. The flow at start is: the daemon creates the container with the environment you passed, the entrypoint script runs as PID 1's first program, it validates and applies the variables (creating a database, writing a config file, generating credentials), and finally it executes the real server process. Nothing about this is a Docker Engine feature — the engine only delivers the variables. Everything else is a convention the image implements, which is why the contract differs between images and why the documentation on the repository page is the authority. ### Required, optional, and first-run-only The Official Postgres image refuses to start with no password configured: you must supply `POSTGRES_PASSWORD`, or explicitly opt out with `POSTGRES_HOST_AUTH_METHOD=trust`, and the error message it prints says so. `POSTGRES_USER` and `POSTGRES_DB` name the superuser and the database created **during initialization** — set them on a container whose data directory already exists and nothing happens, because that branch of the script never runs again. `PGDATA` moves the data directory the server uses. MySQL exposes an analogous set: `MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`. Knowing which of an image's variables are first-run-only is the difference between a five-minute fix and an afternoon of confusion. A team that changes `POSTGRES_DB` from `billing` to `subscriptions` and recreates the container against the same data volume will find the old database still there and the new one absent, and nothing in the logs shouts about it. ### Passing values that should not be in the environment An environment variable is visible to more people than engineers expect: `docker inspect` shows `Config.Env` to anyone who can reach the daemon, it is inherited by every child process, and it lands in shell history and CI job definitions on the way in. Several Official Images therefore implement a `_FILE` convention — for any supported variable `X`, setting `X_FILE=/path/inside/the/container` makes the entrypoint read the value from that file instead. `POSTGRES_PASSWORD_FILE=/run/secrets/pgpass` keeps the password out of the container's environment entirely, and it is what makes these images work naturally with orchestrator-provided secret files. Distinguish the three things people call 'a secret' here: a **build** secret used while building an image, a runtime **environment variable**, and a **file** mounted into the running container. This convention is about the last two. ### When there is no variable for what you need Two escape hatches, both of which avoid a custom image: 1. **Mount a configuration file at the path the image documents.** Most service images read a config file or a drop-in directory, and the documentation names it. You supply the file from the host or from a config object; the image is unmodified. 2. **Pass arguments after the image name.** Arguments you place after the image reference replace the image's default command, and service-image entrypoints are written to cope: the Postgres image's entrypoint prepends the server command when the first argument starts with a dash, so `docker run postgres -c max_connections=200` starts the server with that setting. Reading the entrypoint tells you whether a given image supports this. Only when neither works — you need an extension installed, or an additional binary — does building your own image on top become the right answer, and even then it is a thin layer, not a fork. ### Operational habits Keep the variables in a Compose file or an `--env-file` checked into the repository, with the sensitive values supplied separately as files. Recreate rather than mutate: environment is fixed at container creation, so changing a value means a new container, not a restart of the old one — which is exactly what makes a container reproducible. And write down which of your variables are first-run-only, because the person who inherits the stack will otherwise learn it the hard way at 02:14 when a subscription-billing cron cannot find its database.

  • What does a variable like `POSTGRES_PASSWORD_FILE` do, and why prefer it over `POSTGRES_PASSWORD`?
    The entrypoint reads the value from the file path you give instead of from the environment. The password then never appears in `Config.Env`, is not inherited by child processes, and does not have to pass through a shell command line or a CI job definition. It is the convention that lets these images consume a mounted credential file directly.
  • Who can read an environment variable you passed to `docker run`?
    Anyone who can talk to the Docker daemon: `docker inspect` prints `Config.Env` for the container, and `docker exec` can print the environment of the running process. On the host, a root user can read it from the process's `/proc` entry. Effectively, the environment is visible to everyone with daemon or host access, so treat it as non-secret.
  • What happens if you change an environment variable on an existing container?
    Nothing — the environment is fixed when the container is created, so `docker restart` reuses the old values. You recreate the container with the new value. That is a feature: it keeps the container's configuration a property of the command or Compose file that created it rather than of accumulated in-place edits.
  • The image documents no variable for the setting you need. What do you do before building a custom image?
    Two options first. Mount a configuration file at the path the image's documentation names, so the unmodified image reads your settings. Or pass server arguments after the image name — many service entrypoints forward arguments starting with a dash to the server, as `docker run postgres -c max_connections=200` does. Build your own layer only when you genuinely need extra software inside.

saying these in an interview costs you the question

  • Baking a password into a custom image with ENV
  • Believing environment variables are hidden inside the container
  • Expecting a restart to pick up a changed environment variable
  • Thinking every setting requires forking the image
  • Assuming all documented variables apply on every start
  • Editing config inside a running container instead of recreating

context