Build a Tagged Docker Image Without Sending the Wrong Context

Run docker build from the wrong directory and you can ship your whole home folder into an image without noticing. This guide walks through a small, deliberate build context, a tagged image, and a check that the result is what you meant to ship. Allow about fifteen minutes for a first build, plus time to download the base image. The examples use the installed Docker CLI, version 29.8.1, and the local docker-build(1) manual page from the docker-ce-cli package.

This guide assumes that Docker is installed and that your account can talk to the Docker daemon. Most commands are ordinary user commands. If the daemon socket is restricted on your machine, use your normal Docker group or administrator-approved setup. Do not add broad privileges just to make a build convenient.

1. Check the client and create a controlled context

A build context is the directory, archive or URL supplied after the options. With a local directory, the CLI sends the directory's contents to the daemon, and the Dockerfile can use files from that context. Start with a dedicated directory so a build cannot accidentally include home directories, credentials, editor files or a large checkout.

$ docker version --format '{{.Client.Version}}'
29.8.1
$ mkdir -p demo-image
$ cd demo-image

For a real project, add a suitable .dockerignore before building. It should exclude files that are not inputs to the image, especially .git, local environment files, private keys, dependency caches and build output. The old local manpage describes the context transfer but does not document every current BuildKit feature, so check the installed command's help when a flag is version-sensitive.

2. Write a minimal Dockerfile

Create a file named Dockerfile in the context. This example produces a small static response using an image already intended to serve files:

$ printf '%s\n' \
    'FROM nginx:alpine' \
    'COPY index.html /usr/share/nginx/html/index.html' \
    > Dockerfile
$ printf '%s\n' 'Built by the controlled Docker context' > index.html
$ sed -n '1,10p' Dockerfile

Do not put passwords, tokens or private keys in a Dockerfile or pass them with --build-arg. The installed manual explicitly says build arguments are not for secret values. Values used during a build can be exposed through build metadata, logs or image layers. Use the secret-handling mechanism supported by your installed builder, and review the result before distributing it.

Checkpoint: the context should contain only files that the Dockerfile needs:

$ find . -maxdepth 1 -type f -printf '%f\n' | sort
Dockerfile
index.html

3. Build and tag the image

Run docker build with the tag first and the context directory last:

$ docker build --tag demo-web:1.0 .
[+] Building ...
...
 => naming to docker.io/library/demo-web:1.0

The final . matters. It means the current directory is the context. The default Dockerfile name is Dockerfile. The --tag option applies the repository and tag to the image after a successful build; without an explicit tag, Docker uses latest for the tag in the usual image-name form. A tag is a label, not an immutable version guarantee, so use a meaningful version or digest in deployment records.

Output varies with the builder, cache state and Docker version. A successful command returns status 0 and ends with an image naming or export message. If it fails, do not treat a partly printed step as a usable image. Capture the error and fix the Dockerfile or context before retrying.

4. Verify the image and its contents

Check that the tag resolves locally and inspect the image metadata:

$ docker image inspect demo-web:1.0 \
    --format '{{.Id}} {{.RepoTags}}'
sha256:... [demo-web:1.0]
$ docker image ls demo-web:1.0
REPOSITORY   TAG   IMAGE ID   CREATED   SIZE
demo-web     1.0   ...        ...       ...

The exact image ID, creation time and size will differ. The important checks are that inspection succeeds and the repository and tag are present. A tag can be moved later, so record the image ID or digest when you need to identify the exact build.

For a functional check, start a temporary container and remove it after the request. This changes local Docker state by creating a container and may bind a local port, so first check that port 8080 is unused:

$ docker run --name demo-web-check --detach --publish 127.0.0.1:8080:80 demo-web:1.0
$ curl --fail http://127.0.0.1:8080/
Built by the controlled Docker context
$ docker rm --force demo-web-check
demo-web-check

The --force removal stops and removes only the named temporary container. If the run fails, inspect it with docker logs demo-web-check, then remove it when finished. If port 8080 is already in use, choose another local port, such as 127.0.0.1:18080:80, and use that port in the curl command. The bind is to loopback, so it is not intended to expose the test service on every network interface.

5. Choose the Dockerfile and cache deliberately

For a different filename, use --file. The path must stay inside the build context:

$ docker build --file Dockerfile.release --tag demo-web:1.0 .

With a local context, a relative Dockerfile path is relative to that context. A common trap is running from one directory while assuming the path is relative to the shell's original directory. Check both paths before building:

$ pwd
/path/to/demo-image
$ test -f Dockerfile.release && echo 'Dockerfile is present'
Dockerfile is present

Docker normally reuses cache where it can. Use --no-cache when you need to test every build step afresh, but expect a slower build and more downloads:

$ docker build --no-cache --tag demo-web:1.0-clean .

Use --pull when the build must attempt to obtain newer referenced base images. Neither option makes an image reproducible by itself. Pin base-image digests and dependencies when supply-chain repeatability is a requirement, and retain the build logs and resulting digest.

6. Use remote contexts only when you mean to

The installed manual also accepts a URL or - in place of the local path. A tar archive URL is fetched and used as the context; a Git URL is cloned before the context is sent to the daemon. That changes where source code is obtained and can send more material than expected. Review the remote source, transport and Dockerfile before running a remote build.

For a local archive or standard input, inspect its contents first rather than piping an unknown archive straight into a build. Never include a URL containing credentials. If a remote build is unavoidable, use a pinned revision where the source system supports it, and verify the resulting image as in step 4.

7. Recover from common failures

If Docker cannot connect to the daemon, check the client and daemon details with docker version. The command may need an administrator to start or repair the service, but do not restart a production daemon as a casual troubleshooting step because that can disrupt running containers.

If a file cannot be copied, confirm that it is inside the context and not excluded by .dockerignore. If the context transfer is unexpectedly large, stop and inspect the directory before retrying. The context is sent to the daemon before the build steps run, so a misplaced archive or checkout can waste time and expose data.

If a build fails after changing a source file, retry normally first. Use --no-cache only when stale cached steps are a plausible cause. If the temporary verification container remains after an interrupted shell session, remove exactly that container with docker rm --force demo-web-check. Do not run broad prune commands merely to reclaim space; they can remove useful images, stopped containers and cache for unrelated work.

Done means