Treat an image as a packaged filesystem and configuration contract, then treat each container as a process with explicit storage, identity, and shutdown behavior. A small image is useful only when it remains reproducible and operable.
For: Developers packaging applications and operating container images
Guide scope and versions
Container lifecycle examples built and executed with synthetic local images on a Linux arm64 Docker engine. Dockerfile fragments use the surrounding application files described in each example. These checks do not establish production readiness, backup recovery or workload sizing.
Question 1 of 10
What survives when a container is replaced?
An image is an immutable template made from filesystem layers and metadata. A container adds a writable layer for that instance; data written only there is tied to that container's lifetime and should not be treated as durable storage.
A service writes uploaded files into /app/uploads inside the container and then gets recreated. Those writes can disappear with the old writable layer. Put durable data in a volume or external storage and make backup/restore responsibility explicit.
Images are content-addressed layer stacks that can be shared by multiple containers. The container writable layer is useful for ephemeral state, but it can add copy-on-write costs and is not a substitute for an application data design.
Shell · Worked example
# Set VOLUME to a new disposable volume name.
docker run --rm -v "$VOLUME":/var/lib/app alpine:3.21 sh -c 'printf saved > /var/lib/app/value'
The write was read back from a second container after the first was removed. The named volume persists; backups and restores are separate.
Built and executed locally with synthetic images.
Follow-up: When is the writable layer appropriate?
For disposable caches, temporary files, and runtime state that can be reconstructed. Put user data, database files, and other durable state in an explicitly managed storage system.
Why does the build context need a `.dockerignore`?
The build context is the set of files sent to the builder. Exclude local artifacts, secrets, and irrelevant files so they do not slow builds or become available to Dockerfile instructions by accident.
A repository contains .env, test output, and a large dependency directory. If the context includes them, a later broad COPY . . can place sensitive or unnecessary files in an image layer. .dockerignore reduces both accidental inclusion and invalidated cache inputs.
Do not copy a secret into a layer and then delete it in a later instruction. Image history and cached layers can retain the original bytes. For a build-time credential, use a BuildKit secret mount for the one instruction that needs it.
Text · Worked example
.env
.git
node_modules
coverage
*.log
The real build excluded all five paths while retaining a control file. Patterns still need to match your project; they do not erase secrets passed through other mechanisms.
Built and executed locally with synthetic images.
Follow-up: How should a build fetch a private package?
Use the builder's secret or SSH mount for the fetch step, avoid printing the credential, and ensure the resulting layer contains only the fetched artifact. Build credentials and runtime credentials have separate lifetimes and handling.
What belongs in a multi-stage build's final image?
Keep compilers and build-only tools in an earlier stage, then copy the runtime artifact and required runtime files into the final stage. The final image should contain what the application needs to run, not the whole build environment by default.
A Go service can compile in a toolchain stage and copy its binary into a small runtime base. A Node application may still need runtime packages, certificates, locale data, or native libraries. A smaller base can reduce transfer and attack surface, but missing runtime dependencies cause startup failure.
Pinning a base image digest improves repeatability, while regular updates are still required for security fixes. Record the supported architecture and verify the actual final image with a build and runtime smoke test before claiming its size or behavior.
Dockerfile · Worked example
FROM golang:1.24 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/server ./cmd/server
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]
The Go stage built a static synthetic server, and the distroless stage ran it under the nonroot identity. Supply your own compatible module, source files and runtime dependency checks.
Built and executed locally with synthetic images.
Follow-up: Should every service use the smallest possible base?
No. Choose a supported base that the team can patch and debug. Reduce unneeded packages while keeping required runtime components and a practical update path.
Why should a Docker build avoid secret `ARG` and `ENV` values?
Build arguments and environment variables can remain visible in image metadata, history, or build provenance. Use a BuildKit secret or SSH mount for the build instruction that needs credentials, then ensure the output artifact does not contain them.
A build needs to download a private dependency. Passing a token with --build-arg may expose it through build records even if the Dockerfile never writes it into the final filesystem. A secret mount makes it temporarily available to the specific RUN instruction.
A build secret prevents automatic persistence by the builder, but code in that instruction can still print or copy the secret. Treat the build step and logs as trusted code, and rotate any credential that was baked into an image or published artifact.
Dockerfile · Worked example
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=token \
./fetch-private-dependency --token-file /run/secrets/token
A synthetic downloader consumed a temporary secret mount. The secret file was absent after the instruction and its value was absent from the saved image layers. This does not exercise a real private dependency provider.
Built and executed locally with synthetic images.
Follow-up: Does a runtime environment variable have the same risk as a build secret?
They have different exposure paths. Runtime secrets should be supplied by the runtime's secret mechanism and protected from logs, inspection, and unauthorized processes; a build mount only addresses the build phase.
What breaks when an image switches to a non-root user?
The process may lose permission to bind privileged ports or write paths owned by root. Create only the directories it needs, set explicit ownership or IDs when the runtime expects them, and test the image under its configured user.
An app starts as root, writes cache files under /var/cache/app, then a Dockerfile adds USER app. At runtime it fails because the directory is not writable. Assign the required directory to the runtime user during the build and keep other paths read-only where practical.
A non-root process reduces the impact of some compromises but does not replace application authorization, filesystem restrictions, or a patched base image. Orchestrators may assign arbitrary user IDs, so images intended for those environments should account for that contract.
The Alpine-specific user commands built successfully, and the final process ran with a nonzero UID. Other distributions use different commands; verify writable paths under the intended identity.
Built and executed locally with synthetic images.
Follow-up: Should the app switch back to root for startup tasks?
Avoid it when possible. If privileged initialization is required, separate that responsibility from the long-running application process and drop privileges before serving requests.
A volume is managed by Docker and is a good default for container data; a bind mount maps a host path into the container and depends on that host layout. Both provide storage outside the container writable layer, but neither automatically provides backup, replication, or data migration.
A local developer may bind-mount source code for fast iteration. A production database should use an explicitly managed volume or external storage with documented backup and recovery. A bind mount can expose host files to the container, so limit its path and access mode.
Read-only mounts reduce accidental writes. Named volume lifecycle is separate from container lifecycle, but deleting a volume can still destroy data. Do not use a temporary container or --rm as a backup plan.
Shell · Worked example
# Set VOLUME to a new disposable volume name.
docker run --rm \
--mount type=volume,src="$VOLUME",dst=/var/lib/app \
alpine:3.21 sh -c 'printf saved > /var/lib/app/value'
A second container read the file after removal of the writer container. Persistence is not proof of backup, encryption or portability.
Built and executed locally with synthetic images.
Follow-up: What must a production storage plan add?
A tested backup and restore path, retention and access policy, capacity monitoring, and a recovery target. Container volume syntax only connects storage; it does not provide those operating guarantees.
Why does exec-form `ENTRYPOINT` matter for shutdown?
Exec form starts the application as the container's main process, so it receives termination signals directly. A shell-form wrapper can become PID 1 and may fail to forward signals unless it uses exec or a suitable init process.
A service must flush a bounded queue after SIGTERM. If a shell wrapper intercepts or fails to forward the signal, the application may not begin graceful shutdown and the runtime can eventually kill it. Use exec-form ENTRYPOINT/CMD or have a wrapper replace itself with the application process.
Graceful shutdown also needs an application-level deadline and idempotent cleanup. Signal delivery is only the first step; Docker does not know whether a database transaction or external side effect is safe to interrupt.
The synthetic server received SIGTERM as PID 1, awaited its HTTP shutdown and exited zero. This does not verify production request drainage under load.
Built and executed locally with synthetic images.
Follow-up: When is an init process needed?
When the container launches child processes that need reaping or reliable signal forwarding. Choose a small init or runtime option intentionally; a single-process app may not need one.
Inside a container, localhost refers to that container's own network namespace. Containers that need to communicate should join a shared user-defined network and address the peer by its network name or service name.
An API container tries to connect to localhost:5432 while PostgreSQL runs in a separate container. The API is connecting to itself, not the database. Attach both containers to a user-defined bridge network and use the database container's name as the host.
Publishing a port with -p exposes a container port through the host; it is not required for two containers on the same network to communicate. Keep internal service ports private when the host does not need to reach them, and apply authentication and network policy appropriate to the environment.
Shell · Worked example
# Set unique NETWORK and PEER names for this disposable demo.
docker network create --internal "$NETWORK"
docker run -d --name "$PEER" --network "$NETWORK" busybox:1.37.0 \
sh -c 'mkdir -p /www; printf ok >/www/index.html; exec httpd -f -p 8080 -h /www'
docker run --rm --network "$NETWORK" -e PEER="$PEER" busybox:1.37.0 sh -c '
for attempt in 1 2 3 4 5; do
wget -T 2 -qO- "http://$PEER:8080" && exit 0
sleep 1
done
exit 1'
The pinned BusyBox image includes the HTTP server and wget client used here. Both containers share a private user-defined network, where Docker DNS resolves the peer name. The bounded client loop tolerates startup delay. This checks connectivity, not database initialization, authentication or application authorization.
Built and executed locally with synthetic images.
Follow-up: Does a Compose service name work as a host name?
Services on a shared Compose network can discover each other by service name. Keep the service's internal port separate from any host-published port, and avoid depending on a container's changing IP address.
Build the image from a clean context, inspect the final image's user and contents, then run a smoke test that exercises startup and shutdown. A successful build alone does not prove the runtime stage contains every required file or library.
A multi-stage image builds successfully but fails to start because the final stage omits a certificate bundle. Add a runtime smoke test in the same base image and verify the service can load configuration, reach its expected local health state, and exit cleanly on SIGTERM.
Measure image size and pull time if they affect startup or delivery, but do not optimize for bytes at the cost of a brittle unsupported base. Scan and update the actual final image, and keep build provenance tied to the exact source and base image used.
Shell · Worked example
# BUILD_CONTEXT must contain the application Dockerfile and source.
# IMAGE_TAG is a new local test tag.
docker build -t "$IMAGE_TAG" "$BUILD_CONTEXT"
docker image inspect "$IMAGE_TAG" --format '{{.Config.User}}'
docker run --rm "$IMAGE_TAG" --version
The exact build, user-inspection and version commands completed against a synthetic application context. Your image must implement the version flag; use an application smoke test appropriate to your service.
Built and executed locally with synthetic images.
Follow-up: How would you compare two image variants?
Use the same source revision, target architecture, builder, cache policy, and representative runtime workload. Compare pull/start time, memory, behavior, and patchability alongside image bytes.
A health check records whether the configured command reports the container as healthy or unhealthy. Docker Engine does not restart a container merely because it becomes unhealthy; orchestration and restart policy behavior must be considered separately.
A process is running but cannot accept requests because it has not loaded configuration. A health check can report that state, but a simple Docker restart policy reacts to process exit, not an unhealthy status. Compose can use health status for dependency ordering; a production orchestrator has its own probe contract.
Keep the check cheap, local, and tied to a meaningful ability to serve. A check that depends on every transient external service can mark all replicas unhealthy together and create a cascade.
The image accepted this check. A declared one-second test interval override produced healthy and unhealthy states after changing a synthetic marker. Docker health status alone does not restart the container or remove traffic.
Built and executed locally with synthetic images.
Follow-up: How should a health check treat a database outage?
Separate process liveness from serving readiness. If the app can recover when the database returns, do not make a liveness check restart every process for the same temporary dependency outage.
These are Hacker News job posts that mention these technologies, not employee earnings or a measure of what a skill adds to pay. The amounts summarize annualized advertised minimums in approximate USD. Groups can overlap and differ in roles, locations and dates.
Approximate USD using ECB daily reference rates as of 2026-10-09. Original currencies and offer ranges remain on the source listings. These figures cover posts tagged Docker, rather than every job in the broader technology family.