Prepare a Docker Container Without Starting It
You will create a named Docker container, inspect the settings Docker stored, start it only when you are ready, and remove it cleanly afterwards. Allow about fifteen minutes if the image is already local, or longer if Docker must download it. The examples use Docker CLI 29.8.1 from the installed docker-ce-cli package version 5:29.8.1-1~ubuntu.24.04~noble.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need a working Docker daemon and permission to use its socket. These commands normally run as your logged-in user. Use sudo only if your Docker installation specifically requires it; adding a user to the Docker group gives that user broad control over the host, so treat that as a security decision rather than a routine fix.
1. Check Docker and the image
Confirm the client can reach the daemon, then check that the image name resolves locally:
$ docker version --format '{{.Client.Version}}'
29.8.1
$ docker image inspect alpine:latest > /dev/null
$ printf 'image is available\n'
image is available
The inspect command is read-only. If it reports that the image is missing, either pull a specific image you trust with docker pull alpine:latest, or choose an image already approved for your system. Do not paste an image name from an untrusted source into an administrative script.
2. Create a stopped container
docker create prepares a writable container layer and records the command without starting a process. The initial status is created. Give the container a name so later inspection and cleanup do not depend on a long ID:
$ docker create --name example-shell --pull=never alpine:latest sh -c 'printf "ready\n"; sleep 30'
<container-id>
The ID is printed to standard output. In this example --pull=never prevents an unexpected registry download; it also means the command fails if the local image check was skipped or the tag changed. The command after the image, sh -c ..., is saved for later. It does not run during creation.
Checkpoint
Verify the state before doing anything that starts workloads:
$ docker inspect --format '{{.Name}} {{.State.Status}} {{.Config.Image}}' example-shell
/example-shell created alpine:latest
3. Add only the settings you need
Creation is where you define container settings such as environment variables, resource limits, ports and mounts. Keep the example small:
$ docker create \
--name example-web \
--pull=never \
--env APP_MODE=staging \
--memory=256m \
--read-only \
alpine:latest \
sh -c 'printf "%s\n" "$APP_MODE"; sleep 30'
<container-id>
Check the values Docker recorded rather than relying on memory:
$ docker inspect --format 'status={{.State.Status}} memory={{.HostConfig.Memory}} readonly={{.HostConfig.ReadonlyRootfs}}' example-web
status=created memory=268435456 readonly=true
A memory value of 268435456 is 256 MiB in bytes. A read-only root filesystem can expose application assumptions about writable temporary or cache paths. Add a dedicated --tmpfs mount or a narrowly scoped volume only when the application needs one. Avoid --privileged unless you have a documented host-level reason: it expands the container's access to devices and kernel capabilities.
4. Understand mounts and ports before starting
Use --mount or --volume when the container needs data. A host path is a bind mount; a name without a leading slash is a Docker named volume. The distinction matters:
$ docker create \
--name example-data \
--pull=never \
--mount type=volume,source=example-data,target=/data \
alpine:latest \
sh -c 'sleep 30'
<container-id>
$ docker inspect --format '{{range .Mounts}}{{.Type}} {{.Name}} {{.Destination}} {{.RW}}{{end}}' example-data
volume example-data /data true
Volumes default to read-write. Use a read-only suffix or mount option for data the process must not alter. Be careful with bind mounts: the container can then read or change the selected host path. Likewise, -p 127.0.0.1:8080:80 publishes a container port only on the host loopback address, while a publication without a host address can expose it on host interfaces.
5. Start it deliberately and read its output
Creation does not run the command. Start the first example and attach to its output:
$ docker start --attach example-shell
ready
The process exits after its sleep. Check the resulting status and exit code:
$ docker inspect --format 'status={{.State.Status}} exit={{.State.ExitCode}}' example-shell
status=exited exit=0
If you need a long-running service, use docker start example-name without --attach, then inspect logs with docker logs example-name. A restart policy such as --restart=unless-stopped changes how the daemon manages later exits; do not add one to a short-lived test container by accident.
6. Remove the test containers
Removing a container is destructive to its writable container layer. Confirm the name first, then remove the containers created above:
$ docker ps -a --filter name='^/example-' --format 'table {{.Names}}\t{{.Status}}'
NAMES STATUS
example-shell Exited (0) ...
example-web Created
example-data Created
$ docker rm example-shell example-web example-data
example-shell
example-web
example-data
The named volume is separate from the container and remains after docker rm. That is useful when it contains data you still need, but it also leaves state behind:
$ docker volume rm example-data
example-data
Run the volume removal only after checking its contents and confirming that no other container needs it. If you removed a container too early, the image can be used to create another one, but changes stored only in the removed container layer are not recovered by that action.
Common traps
- Seeing a container ID does not mean the application is running. Check
.State.Status;createdis the expected state immediately after creation. - The default pull policy is
missing. Use--pull=neverfor an offline or tightly controlled workflow, or verify the exact image digest before allowing a pull. --rmremoves the container after it exits. It is convenient for disposable runs, but it removes the object you might otherwise inspect after a failure.--envvalues can appear in container metadata and inspection output. Do not put passwords or long-lived tokens there; use a secrets mechanism suited to your deployment.
Done means
- The image was checked and the container was created with a deliberate name.
docker inspectconfirmed the container was initiallycreated.- Mounts, ports, privileges and environment values were reviewed before starting.
- The container was started explicitly and its exit status or logs were checked.
- Test containers and any disposable volumes were removed only after their data was reviewed.