docker-compose up vs docker-compose up --build vs docker-compose build --no-cache
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
The three commands docker-compose up, docker-compose up --build, and docker-compose build --no-cache control how Docker Compose builds images and starts containers. The short answer is that up reuses existing images, up --build rebuilds before starting, and build --no-cache rebuilds every layer from scratch. Choosing the wrong one wastes minutes on every iteration or, worse, ships stale code.
How Docker Layer Caching Works
Before comparing the commands, you need to understand what Docker is actually caching. Every instruction in a Dockerfile produces an intermediate layer. Docker hashes the instruction text plus the content of any files added by COPY or ADD. If the hash matches a cached layer, Docker skips execution and reuses the cached result.
If you only change application source code, layers 1 through 4 are cache hits. Docker rebuilds only layers 5 and 6. This is the foundation of fast iterative builds, and it directly affects which compose command you should reach for.
docker-compose up
This is the default development workflow command. It reads your docker-compose.yml, creates networks and volumes if needed, and starts every defined service. If an image already exists locally, Compose reuses it without rebuilding.
Running this the first time builds the api image because none exists. Running it again reuses that cached image, even if you have changed source files since the last build. That is the critical behavior to understand: docker-compose up does not watch your source tree for changes.
When to use it
Use plain up when you have not changed any Dockerfiles or application code since the last build, or when you are restarting containers that were stopped. It is also the right command when all your services use pre-built images from a registry and no local build step is involved.
docker-compose up --build
Adding --build tells Compose to run the build step before starting containers. Docker still uses layer caching, so unchanged layers are fast. Only modified layers and their dependents are rebuilt.
In practice, this is the command most developers should reach for during active development. Suppose you edit a route handler in your API:
Running docker-compose up --build detects that the COPY . . layer's context changed, rebuilds from that point forward, and starts the updated container. Without --build, the old image keeps running your previous code.
Interaction with layer caching
The --build flag does not disable the cache. It simply guarantees that Compose runs docker build before docker start. Layers whose inputs have not changed are still pulled from cache. This is why --build typically adds only seconds to a well-structured Dockerfile.
docker-compose build --no-cache
This command rebuilds every image from the first instruction, ignoring all cached layers. It is a separate build step that does not start containers afterward.
Because every layer executes fresh, this is the slowest option. A Node.js project that takes 5 seconds with cache hits might take 90 seconds without them, mostly due to npm ci re-downloading every dependency.
When to use it
There are specific scenarios where a full cache bust is necessary:
- Debugging "works on my machine" issues. A cached layer might contain an artifact from a previous build that masks a real problem.
- Base image updates. If
node:20-alpinereceived a security patch, cached layers still reference the old base. A no-cache build pulls the fresh base. - CI/CD pipelines. Some teams run no-cache builds in CI to guarantee reproducibility, accepting the time cost in exchange for confidence.
- Dependency resolution changes. If your lockfile has not changed but upstream package behavior has (rare but real), caching the
npm cilayer hides the difference.
You can also target a single service:
That rebuilds only the api service from scratch, leaving other services cached.
Command Comparison
| Command | Builds images? | Uses layer cache? | Starts containers? | Typical use case |
docker-compose up | Only if no image exists | Yes | Yes | Restarting unchanged services |
docker-compose up --build | Always | Yes | Yes | Active development with code changes |
docker-compose build --no-cache | Always | No | No (run up separately) | Debugging, CI, base image refresh |
Dockerfile Structure Matters More Than the Command
The biggest performance lever is not which command you run but how your Dockerfile is structured. The general rule is to put instructions that change least frequently at the top and instructions that change most frequently at the bottom.
A poorly ordered Dockerfile:
A well-ordered Dockerfile:
In the second version, changing application code only invalidates the final COPY . . layer. The dependency installation layer stays cached. This single change can turn a 60-second rebuild into a 3-second rebuild when using docker-compose up --build.
Multi-Stage Builds and Cache Implications
Production Dockerfiles often use multi-stage builds. Each stage has its own cache chain:
With --build, Docker caches both stages independently. With --no-cache, both stages rebuild from scratch. Understanding this is important because multi-stage builds already reduce final image size; combining them with proper cache ordering keeps iteration speed high.
Common Pitfalls
Running plain up after code changes and wondering why nothing changed. This is the most frequent mistake. The existing image is reused. Use --build.
Using --no-cache as a default habit. Some developers add --no-cache to every build out of caution. This throws away all caching benefits and can add minutes to every cycle. Reserve it for situations where you have a specific reason to distrust the cache.
Forgetting that build --no-cache does not start containers. Unlike up --build, the build command only produces images. You still need to run up afterward.
Not structuring the Dockerfile for cache efficiency. If your COPY . . comes before RUN pip install, every source code change triggers a full dependency reinstall regardless of which compose command you use.
Ignoring .dockerignore. Without a .dockerignore file, Docker includes everything in the build context, including node_modules, .git, and test artifacts. This inflates context size and causes unnecessary cache invalidation.
Summary
- Use
docker-compose upwhen images are already built and you just need to start or restart containers. - Use
docker-compose up --buildas your standard development command whenever code or Dockerfiles have changed. Layer caching keeps it fast. - Use
docker-compose build --no-cacheonly when you need a guaranteed clean build, such as debugging cache-related issues or refreshing base images in CI. - Structure your Dockerfile so that frequently changing layers come last. This has a bigger impact on build speed than which command you choose.
- Always pair
build --no-cachewith a follow-upupcommand, since it does not start containers on its own.

