Docker Compose Up Build: Full Guide & Best Practices

Docker Compose Up Build: The Complete Guide to Rebuilding and Running Containers

Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

If you’ve spent any time working with multi-container applications, you’ve almost certainly typed docker compose up build into your terminal, only to get a syntax error or unexpected behavior. The correct command is docker compose up --build, and understanding exactly what it does — and when to use it — will save you hours of debugging “why isn’t my code change showing up” mysteries.

This guide covers the full mechanics of docker compose up --build, how it differs from running docker compose build separately, common pitfalls with caching, and practical workflows for local development, CI pipelines, and production-adjacent staging environments.

What Does docker compose up –build Actually Do

The docker compose up --build command tells Compose to rebuild any service images before starting the containers. Without the --build flag, Compose only builds an image if it doesn’t already exist locally — if you’ve made changes to your Dockerfile or the files it copies in, those changes will be silently ignored and you’ll keep running the old image.

Here’s the basic syntax:

docker compose up --build

This single command does three things in sequence:

  • Rebuilds every service in your docker-compose.yml that has a build: directive
  • Recreates containers that depend on those rebuilt images
  • Starts (or restarts) all services defined in the compose file
  • If you only want to rebuild and start a single service, you can scope it:

    docker compose up --build web

    This rebuilds and starts only the web service, along with any services it depends on via depends_on.

    Why the Plain docker compose up Command Isn’t Enough

    A lot of confusion comes from assuming Compose always rebuilds on up. It doesn’t. Compose checks whether an image already exists for the service; if it does, up reuses it, even if your source code or Dockerfile has changed since the image was built.

    This is a common source of “phantom bugs” where a developer fixes an issue, runs docker compose up, and the bug is still there — because the container is running stale code baked into an old image layer. Running docker compose up --build forces Compose to re-evaluate the build context and rebuild as needed.

    The Difference Between docker compose build and docker compose up –build

    These two approaches look similar but serve different purposes:

    # Build images without starting containers
    docker compose build
    
    # Build images and immediately start containers
    docker compose up --build

    docker compose build is useful in CI/CD pipelines where you want to build and push an image without running it locally. docker compose up --build is better suited for local development loops where you’re iterating quickly and want your changes reflected immediately.

    If you’re setting up a CI pipeline, it’s worth reading the official Docker Compose CLI reference for the full list of flags and how they interact with build caching.

    Common Flags and Options You’ll Actually Use

    Beyond the basic --build flag, there are several options that make the command far more useful in day-to-day work.

    Forcing a Full Rebuild with –no-cache

    Docker’s build cache is usually your friend — it speeds up rebuilds by reusing unchanged layers. But sometimes the cache causes problems, especially when a base image has been updated upstream or a RUN apt-get install step needs fresh package lists.

    docker compose build --no-cache
    docker compose up --build --no-cache

    Note that --no-cache isn’t actually a flag on up directly in older Compose versions — if you hit an error, run the no-cache build separately first, then bring the stack up:

    docker compose build --no-cache && docker compose up

    Running in Detached Mode

    For background services, especially on a VPS or remote server, combine --build with -d:

    docker compose up --build -d

    This rebuilds images and runs containers in detached mode so your terminal session stays free. This is the pattern most people use when deploying to a DigitalOcean droplet or similar cloud VM — SSH in, pull the latest code, and run this one command to redeploy.

    Forcing Container Recreation

    Sometimes Compose decides a container doesn’t need to be recreated even after a rebuild, particularly if only environment variables changed. Force it with:

    docker compose up --build --force-recreate

    This guarantees old containers are torn down and replaced, not just restarted.

    Practical Workflow Examples

    Let’s walk through a realistic scenario. Say you have a simple Node.js API with the following docker-compose.yml:

    version: "3.9"
    services:
      api:
        build:
          context: .
          dockerfile: Dockerfile
        ports:
          - "3000:3000"
        environment:
          - NODE_ENV=development
        volumes:
          - ./src:/app/src
      db:
        image: postgres:16
        environment:
          - POSTGRES_PASSWORD=devpassword
        volumes:
          - pgdata:/var/lib/postgresql/data
    
    volumes:
      pgdata:

    And a Dockerfile like this:

    FROM node:20-alpine
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    EXPOSE 3000
    CMD ["node", "src/index.js"]

    If you add a new npm dependency to package.json, running plain docker compose up won’t install it — the existing image already has node_modules baked in from the last build. You need:

    docker compose up --build

    This re-runs npm install inside the image build step, picking up the new dependency, then starts both the api and db services.

    Handling Multi-Service Rebuilds Efficiently

    In larger projects with multiple services (API, worker, frontend, reverse proxy), rebuilding everything every time is wasteful. Scope your builds:

    docker compose up --build api worker

    This rebuilds only api and worker, leaving db and any unchanged services untouched, which speeds up your iteration loop considerably.

    If you’re managing a growing compose file, it’s worth reviewing our guide to organizing multi-container Docker projects for patterns on splitting services across multiple compose files with docker compose -f.

    Troubleshooting Common docker compose up –build Issues

    A few issues come up repeatedly with this command, and most have simple fixes.

  • Changes not appearing after rebuild: Check whether you have a bind-mounted volume overwriting the container’s code directory with an outdated local copy. Volumes mounted in docker-compose.yml take precedence over what’s baked into the image.
  • Build succeeds but container immediately exits: Check your CMD or ENTRYPOINT — the container will exit if the foreground process crashes or completes. Run docker compose logs <service> to see the actual error.
  • “no space left on device” during build: Docker’s build cache and dangling images accumulate over time. Run docker system prune -a (carefully — this removes unused images) to reclaim space.
  • Environment variable changes not taking effect: Environment variables set in .env files or environment: blocks don’t always force a rebuild. Combine with --force-recreate to be safe.
  • Build context is too large and slow: Add a .dockerignore file to exclude node_modules, .git, and other large directories from being sent to the Docker daemon during build.
  • Speeding Up Builds with BuildKit

    Modern Docker installations use BuildKit by default, which parallelizes build steps and caches more intelligently. If you’re on an older setup, enable it explicitly:

    DOCKER_BUILDKIT=1 docker compose up --build

    BuildKit also supports cache mounts for package managers, which can dramatically speed up repeated npm install or pip install steps. See Docker’s own documentation on BuildKit for configuration details.

    Deploying with docker compose up –build on a Remote Server

    When deploying to a VPS, the workflow typically looks like this: SSH into the server, pull the latest code, and rebuild.

    ssh user@your-server-ip
    cd /opt/myapp
    git pull origin main
    docker compose up --build -d

    For production deployments, consider adding a health check to your compose file so Compose (and any orchestration layer) knows when a service is actually ready:

    services:
      api:
        build: .
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
          interval: 30s
          timeout: 5s
          retries: 3

    If you’re running this on a budget VPS provider, Hetzner offers solid price-to-performance ratios for small Docker Compose deployments, and their cloud consoles make it easy to spin up a fresh instance to test a deployment before pushing to your main server.

    For teams that need uptime monitoring on these deployed services, pairing your compose stack with BetterStack gives you alerting when a container-based service goes down, which is especially useful right after a --build deploy where a bad image can silently break a health check.

    If your compose-based app sits behind a public domain, routing DNS and basic DDoS protection through Cloudflare is a near-zero-cost way to harden a VPS-hosted deployment before you invest in a full load balancer setup.

    Automating Rebuilds in CI/CD

    Many teams wire docker compose up --build into a deployment script triggered by a git push. A minimal GitHub Actions step might look like:

    - name: Deploy via SSH
      run: |
        ssh -o StrictHostKeyChecking=no user@${{ secrets.SERVER_IP }} 
          "cd /opt/myapp && git pull && docker compose up --build -d"

    This pattern works well for small to medium projects. For anything with higher uptime requirements, you’ll want a proper CI pipeline that builds and tests images before they ever touch your production compose file — check out our beginner’s guide to Docker fundamentals if you’re still solidifying the basics before automating deployments.

    Best Practices Checklist

    Before you wire docker compose up --build into your regular workflow, keep these practices in mind:

  • Always add a .dockerignore file to keep build contexts small and fast
  • Use --build only when source files or Dockerfiles have changed — otherwise plain up is faster
  • Combine with -d for background services on remote servers
  • Use --force-recreate when environment variables or configs change without a corresponding image rebuild
  • Periodically run docker system prune to clean up unused build cache and dangling images
  • Pin base image versions (e.g., node:20-alpine instead of node:latest) to avoid unexpected behavior from upstream updates
  • Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Is “docker compose up build” a valid command?
    No. The correct syntax requires two dashes: docker compose up --build. Without the dashes, Docker will interpret build as a service name and likely return an error saying no such service exists.

    Does docker compose up –build always rebuild every service?
    Yes, by default it rebuilds all services in the compose file that have a build: directive. To limit the rebuild to specific services, list them after the command, like docker compose up --build api.

    What’s the difference between docker compose up –build and docker-compose up –build?
    Functionally they’re the same. docker compose (no hyphen) is the newer Compose V2 syntax integrated into the Docker CLI, while docker-compose is the older standalone Python-based tool. Compose V2 is faster and actively maintained, so it’s the recommended syntax going forward.

    Why isn’t my code change showing up even after running docker compose up –build?
    The most common cause is a bind-mounted volume overriding the container’s working directory with a stale local copy, or the build cache reusing a layer that should have been invalidated. Try docker compose build --no-cache followed by docker compose up --force-recreate.

    Does docker compose up –build slow down my workflow?
    It adds build time on every run, so it’s slower than plain up when nothing has changed. Use it selectively — only when you’ve modified your Dockerfile, dependencies, or files copied during the build step.

    Can I use docker compose up –build in production?
    It’s more common in development and simple deployment scripts. For production, most teams prefer building images in CI, pushing them to a registry, and having the production compose file reference a specific image tag rather than rebuilding on the server itself.

    Wrapping Up

    docker compose up --build is one of the most-used commands in a containerized development workflow, but its behavior around caching and image reuse trips up even experienced developers. The core rule to remember: if you’ve changed anything that affects the image (Dockerfile, dependencies, copied files), you need --build to see those changes reflected. Combine it with -d for background services, --force-recreate when configs change, and --no-cache when you suspect stale cache layers are the culprit, and you’ll avoid most of the common headaches associated with this command.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *