Restart or rebuild a Docker Compose service
Restart, up -d, rebuild or pull: which Docker Compose command picks up an edited compose file, a new .env, a changed Dockerfile or a new image.
Restart or rebuild: start from what you changed
Whether you restart or rebuild a Docker Compose service depends on one thing: what you changed. docker compose restart web bounces the process inside the container you already have, and it applies nothing else. docker compose up -d web compares your files against the running container and replaces that container only if the definition or the image ID changed. Anything that lives inside the Dockerfile needs a build first, because Compose has nothing new to recreate from until a new image exists.
Every command on this page ends with a service name, web in the examples. Leave the name off and Compose applies the same action to every service in the project. That is rarely what you want when one service is the problem.
The ladder, cheapest rung first
docker compose restart webrestarts the process. Same container, same config, same image.docker compose up -d webrecreates the container if the rendered service definition or the image ID changed.docker compose up -d --build webrebuilds the image from the Dockerfile using the layer cache, then recreates the container.docker compose build --no-cache webthendocker compose up -d webrebuilds every layer from scratch, then recreates.docker compose pull webthendocker compose up -d webfetches a newer image from the registry, then recreates.
Each rung takes longer and picks up more of your changes. Start at the rung that matches the edit you made. The rest of this guide covers what each rung does not pick up, because that gap is the real source of confusion.
What does docker compose restart not pick up?
A container's configuration is fixed at the moment the container is created. Environment variables, published ports, mounts, the command, the user, and the image are all decided then and written into the container's own record. restart sends a stop signal to the main process and starts it again inside that same container, so none of those things can change.
Restart therefore ignores:
- an edited
compose.yaml, including a new port mapping, a new volume or a new environment variable - a changed value in
.env, because Compose reads.envwhen it renders the service definition, not when it starts a process - a file listed under
env_file:, for the same reason - a rebuilt or newly pulled image, because the container keeps running the image it was created from
Restart is the right command in a narrow set of cases. The process crashed or wedged and you want it back. Or the service reads its configuration from a bind mount at startup, so the file on the host changed while the container itself does not need to change. An nginx container with a config directory bind mounted from the host is the classic example. Even there, docker compose exec web nginx -s reload is gentler, because it reloads the configuration without dropping the connections the container is already serving. If you are not sure which of your mounts are bind mounts and which are named volumes, the difference between bind mounts and named volumes decides whether an edit on the host is visible inside the container at all.
What docker compose up -d picks up, and what it skips
up -d is the command most edits need. Compose reads the compose file, resolves every variable, and computes a hash of the resulting service definition. It stores that hash on the container as a label when it creates it. On the next up, Compose computes the hash again and compares it to the label on the running container. Different hash, new container. Same hash, Compose leaves the container alone.
That comparison is why up -d sometimes looks like it did nothing. Nothing was different, so nothing needed to happen. It is also why up -d picks up compose file edits and .env edits with no extra flag: both feed into the rendered definition, so both change the hash. Values pulled in through env_file: are resolved into that same definition before the container is created, which is why an edited env file needs a recreate and not a restart. The wider question of where secrets should live is covered in handling .env files and secrets in Compose.
The image takes part in that comparison as an ID, not as a name. If the tag web:latest on your host now resolves to a different image ID than the one the container was created from, up -d replaces the container. If the tag still resolves to the same ID, the container keeps running even though the Dockerfile on disk has changed, because Compose does not read your Dockerfile during a plain up. It builds only when the image is missing entirely.
To see the definition Compose will actually compare, run docker compose config web. It prints the final service definition after variable interpolation, so you can confirm that the value you edited landed where you expected before you recreate anything.
--force-recreate and --no-deps, the two escape hatches
Two flags cover the cases where the hash comparison does not do what you want.
docker compose up -d --force-recreate web replaces the container even when the definition is unchanged. Use it when the container is in a state you do not trust, or when something outside the compose file changed and you want a clean container without editing a file just to move the hash. It is the honest version of running restart twice and hoping.
docker compose up -d --no-deps web stops Compose from touching the services listed in that service's depends_on. Without it, up -d web also evaluates the database and any other dependency, and a dependency whose definition changed gets recreated along with web. On a stack where the database takes a minute to come back, that is an expensive surprise. Add --no-deps when you already know the dependencies are up and healthy.
The two combine. docker compose up -d --force-recreate --no-deps web gives you a brand new container for exactly one service and leaves the rest of the project untouched. Keep both forms somewhere you can find them; the Compose command cheat sheet collects the flags that come up most.
When the Dockerfile or your source changed: rebuild
If the service has a build: section, your application code lives inside the image. Editing a source file on the host changes nothing in the running container, because that container holds a copy that was baked in at build time. The exception is a development setup that bind mounts the source directory into the container, where the file is shared and only a process restart is needed.
docker compose up -d --build web builds the image for that one service, then recreates the container from the result. The build uses the layer cache, so it reuses every instruction up to the first one whose inputs changed. A COPY . /app line rebuilds when any copied file changes, and every layer after it rebuilds too.
docker compose build web builds without touching the running container. That is useful when the build takes several minutes and you would rather have the image ready before the short outage starts. Follow it with docker compose up -d web to swap the container over.
Which half of this page applies to a service depends on how it gets its image, and choosing between build and image in a service definition is worth reading once if your compose file uses both.
When you need --no-cache
The layer cache is keyed on the text of each instruction and on the files that instruction copies. It is not keyed on what the instruction downloads. A RUN apt-get install -y curl line looks identical to the builder on the second build, so the cached layer is reused and the packages inside it stay as old as the day that layer was first created. The same is true of a RUN git clone line and of anything that fetches a moving tag.
docker compose build --no-cache web discards the cache for that service and runs every instruction again. Then docker compose up -d web swaps in the new image. It is the slowest rung on the ladder, so keep it for the case it exists for: the Dockerfile did not change, but the things it reaches out and fetches did.
Pulling a newer upstream image
For a service defined with image: ghcr.io/example/app:latest and no build: section, the sequence is docker compose pull web followed by docker compose up -d web. pull downloads whatever that tag points to now. up -d then sees that the container's image ID no longer matches the tag and recreates the container.
A plain up -d does not pull. Compose pulls only when the image is missing locally, or when the service sets pull_policy: always. A stack that was deployed months ago and never pulled is still running the image that the tag pointed to on the day the container was created, however many times someone has run up -d since.
If you prefer one command, docker compose up -d --pull always web does both. Either way, leave the old image on disk until the new container has proved itself. docker compose images lists the image each service container is running, and rolling back means pointing the compose file at the previous tag or digest and running up -d web again. Pinning a digest instead of a moving tag is what makes that rollback repeatable.
Does recreating a container delete my data?
Recreating a container is not the same as deleting its data, and nothing on the ladder above removes a volume.
Named volumes are declared in the compose file and exist independently of any container, so a recreated service attaches the same volume again. Bind mounts point at a path on the host and are equally untouched. Anonymous volumes, the ones an image creates through a VOLUME instruction when your compose file gives them no name, are carried across a recreate by default. The flag that changes that is -V, also written --renew-anon-volumes, which gives the new container empty anonymous volumes instead. Check what your service mounts with docker compose config web before you use it.
The command that does delete data is docker compose down -v, because -v removes the named volumes that belong to the project. That is a different operation from anything on this page, and what down destroys that stop does not is the guide for it.
The safe order for a service that owns data
- Read the final definition with
docker compose config weband note every mount, so you know which paths hold state. - Back the state up. For a database, take a dump through the database's own tooling while the container is running. Copying the files under a volume while the engine is writing to them produces a backup you cannot trust.
- Recreate with the smallest rung that applies, adding
--no-depsif a database is listed as a dependency and does not need to move. - Confirm the service is answering before you prune anything or remove the old image.
Changing the image tag of a database to a new major version is not a rebuild, and it does not belong on this ladder. The engine has to migrate its data directory, and several engines refuse to start against a directory that was written by a newer version, which leaves you with a container that will not come up and a volume you cannot downgrade. backing up and upgrading a Compose stack takes over at that point.
Make the swap quieter
A recreate always has a gap. The old container stops, the new one starts, and the service is unavailable in between. Two things shrink that gap. Build the image first with docker compose build web or docker compose pull web, so the slow part happens while the old container is still serving. Then give the service a healthcheck, and have anything that depends on it wait for condition: service_healthy rather than for the container merely existing. That stops the window where an application container is running and the database behind it is not ready to answer. writing healthchecks that test the service rather than the process covers the test commands and the timing values that make that wait meaningful.
FAQ
Does docker compose restart apply changes to my compose file?
No. A container's environment variables, ports, mounts and command are fixed when the container is created, and restart reuses that same container. Edits to compose.yaml, to .env and to any file under env_file: need docker compose up -d web, which renders the definition again and replaces the container when the result differs from what is running.
Why does docker compose up -d leave my container alone after I edit the Dockerfile?
Because a plain up never reads your Dockerfile. Compose compares the rendered service definition and the image ID against the running container, and an edited Dockerfile changes neither until you build. Run docker compose up -d --build web, or docker compose build web followed by docker compose up -d web if you want the build to finish before the container is swapped.
How do I restart one service without touching the rest of the stack?
Name the service and add --no-deps: docker compose up -d --no-deps web. Without that flag, Compose also evaluates every service listed in the depends_on block of web, and recreates any of them whose definition changed. Add --force-recreate as well if you want a new container even when nothing in the definition has changed.
Will I lose my database if I recreate the container?
Named volumes and bind mounts survive a recreate, because both live outside the container. Anonymous volumes are carried over by default and are only discarded if you pass -V or --renew-anon-volumes. The command that removes data is docker compose down -v, which deletes the project's named volumes. Take a database dump before any upgrade that changes the engine's major version.
When do I need --no-cache instead of --build?
Use --no-cache when the Dockerfile text did not change but what it downloads did. Package installs and git clone lines are the common cases: the instruction looks identical to the builder, so the cached layer is reused and the contents stay stale. docker compose build --no-cache web rebuilds every layer, then docker compose up -d web swaps the container. For an ordinary source code change, --build on its own is enough, since the cache already invalidates at the copy step.