
docker compose build builds or rebuilds the images for services that have a build definition. It does not replace a running container. After rebuilding, use docker compose up to apply the new image, then check both the container’s image ID and the application response.
This guide follows that sequence with one small local service. The configuration and generated build plan passed checks with Docker Compose v5.1.4. Docker’s Linux engine was unavailable during preparation, so the image build, cache behavior, container replacement, and HTTP response have not been tested here.
| Command | Use it for | What to check afterward |
|---|---|---|
| docker compose build web | Build the web service image | The new local image ID |
| docker compose up -d --build web | Build, then create or update the service | The running container and response |
| docker compose up -d --no-build web | Apply an image already built | Whether the container uses that image |
| docker compose restart web | Restart an existing container | Whether restarting resolved the process issue |
Docker’s up reference explains recreation when an image or service configuration changes. A restart reuses the existing container; it does not apply a newly built image or updated Compose environment settings. For those settings, see our guide to Compose environment variables.
Use a new directory, an available port 8080, Docker Compose, and a working Docker engine running Linux containers. Check docker context show and docker info before building. The shell example for image IDs below uses Bash. Run every command from this example directory and stop if a command fails.
Create a site directory and save this as site/index.html:
<!doctype html>
<html lang="en"><meta charset="utf-8"><title>Build demo</title>
<h1>Build version 1</h1></html>
Save this as Dockerfile beside the site directory:
FROM python:3.12-slim
WORKDIR /app
COPY site/ /app/site/
USER 10001:10001
EXPOSE 8000
CMD ["python", "-m", "http.server", "8000", "--bind", "0.0.0.0", "--directory", "/app/site"]
The image copies the page into its filesystem and runs as numeric UID/GID 10001. Keep the source directory and page readable by that user. Python’s http.server is suitable for this disposable demonstration, not production serving. The base tag can change; a reproducible build needs a reviewed digest and controlled inputs.
Save this as .dockerignore. The allowlist keeps unrelated project files out of the build context; put only public example content under site:
*
!Dockerfile
!site/
!site/**
Finally, save compose.yaml:
name: compose-build-demo
services:
web:
image: compose-build-demo-web:local
pull_policy: never
build:
context: .
dockerfile: Dockerfile
ports:
- "127.0.0.1:8080:8000"
build.context identifies the files available to the builder. Here it is the directory containing compose.yaml; dockerfile identifies the recipe within that context. image names the result. Docker documents these relationships in the Compose Build Specification.
The service’s pull_policy: never keeps its application image local. This is separate from pulling the Dockerfile’s base image during an explicit build. Only the host’s loopback address exposes port 8080. There are no bind mounts, so changing the source page cannot directly change the running container’s copy.
docker compose -f compose.yaml config --quiet
docker compose -f compose.yaml config --format json
The config command should resolve one service named web, the expected context directory, image compose-build-demo-web:local, and a port binding from 127.0.0.1:8080 to container port 8000. This checks the Compose model, not whether the Dockerfile can build or the container can start.
Our checks also rendered docker compose -f compose.yaml build --print web and verified its target, Dockerfile path, and image tag. That command prints a build plan without executing it. Its output is useful for inspection, not evidence of a successful image build.
docker compose -f compose.yaml build --pull web
docker compose -f compose.yaml up -d --no-build --pull never web
docker compose -f compose.yaml ps
curl --fail http://127.0.0.1:8080/
Execute each line only after the previous one succeeds. The first command requests a fresh base-image check. The second uses the local application image without building again or pulling a substitute. The expected page contains Build version 1; confirm it on your machine. If port 8080 is occupied, change the published port in Compose and use the same port in your request.
The shorter docker compose up -d --build web combines building and startup. Keeping the steps separate makes it easier to locate a failure. A successful build does not establish service health.
In Bash, run:
docker image inspect compose-build-demo-web:local --format '{{.Id}}'
cid=$(docker compose -f compose.yaml ps -q web)
if [ -z "$cid" ]; then
echo "No running web container" >&2
exit 1
fi
docker inspect "$cid" --format '{{.Image}}'
The first command inspects the image currently named by the local tag. The last inspects the image recorded on the running container. For this single-platform local build, compare those IDs. The container’s Config.Image is its image reference, which can be the same mutable tag across several builds; it is not sufficient evidence that the container was updated. Docker’s inspect reference covers selecting object fields.
Edit site/index.html so the heading reads Build version 2, then run:
docker compose -f compose.yaml build web
Before running up, repeat the image-ID checks and request the page. The running container still uses its original filesystem. The expected response remains version 1 even though the local image tag now points to a rebuild containing the edited page.
docker compose -f compose.yaml up -d --no-build --pull never web
curl --fail http://127.0.0.1:8080/
Now repeat the ID comparison and verify version 2 in the response. Diagnose the image and response together: a matching image ID cannot rule out a browser cache, an application configuration error, or a mount hiding image files. This example deliberately has no mounts.
Use --force-recreate on up only when you intentionally need replacement regardless of Compose’s change detection. It does not rebuild an image. For automatic edits during development, our Compose Watch guide covers synchronization and rebuild rules.
--no-cache disables build-layer reuse for that build; --pull asks the builder to check for a newer base image. One does not imply the other. To request both:
docker compose -f compose.yaml build --no-cache --pull web
These options are listed in the Compose build reference. They do not fix a wrong context, an ignored file, or a running container that has not been recreated. --pull also cannot move a digest-pinned base to a different digest.
A content change in a copied file normally invalidates the relevant COPY cache entry. Simply touching its timestamp does not. Docker’s cache rules explain that distinction. Avoid routine cache deletion when the problem is actually the path or startup step.
.dockerignore. Files outside the context are not available to a normal COPY instruction.For a deployment on Compute with Hivenet, our guide to running Docker on a virtual machine covers the host setup. This local example does not test a Hivenet deployment or provide a production web-server configuration.
docker compose -f compose.yaml down
Run this only from the example directory with the same Compose file and project. It removes this project’s containers and network; the built image remains available. There is no need to prune unrelated images, containers, or volumes.
Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.